diff --git a/contrib/gcc/builtins.c b/contrib/gcc/builtins.c index a3e069e4baca..2931684a9a62 100644 --- a/contrib/gcc/builtins.c +++ b/contrib/gcc/builtins.c @@ -1,7045 +1,7054 @@ /* Expand builtin functions. Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include "system.h" #include "coretypes.h" #include "tm.h" #include "machmode.h" #include "real.h" #include "rtl.h" #include "tree.h" #include "flags.h" #include "regs.h" #include "hard-reg-set.h" #include "except.h" #include "function.h" #include "insn-config.h" #include "expr.h" #include "optabs.h" #include "libfuncs.h" #include "recog.h" #include "output.h" #include "typeclass.h" #include "toplev.h" #include "predict.h" #include "tm_p.h" #include "target.h" #include "langhooks.h" #define CALLED_AS_BUILT_IN(NODE) \ (!strncmp (IDENTIFIER_POINTER (DECL_NAME (NODE)), "__builtin_", 10)) /* Register mappings for target machines without register windows. */ #ifndef INCOMING_REGNO #define INCOMING_REGNO(OUT) (OUT) #endif #ifndef OUTGOING_REGNO #define OUTGOING_REGNO(IN) (IN) #endif #ifndef PAD_VARARGS_DOWN #define PAD_VARARGS_DOWN BYTES_BIG_ENDIAN #endif /* Define the names of the builtin function types and codes. */ const char *const built_in_class_names[4] = {"NOT_BUILT_IN", "BUILT_IN_FRONTEND", "BUILT_IN_MD", "BUILT_IN_NORMAL"}; #define DEF_BUILTIN(X, N, C, T, LT, B, F, NA, AT, IM) #X, const char *const built_in_names[(int) END_BUILTINS] = { #include "builtins.def" }; #undef DEF_BUILTIN /* Setup an array of _DECL trees, make sure each element is initialized to NULL_TREE. */ tree built_in_decls[(int) END_BUILTINS]; /* Declarations used when constructing the builtin implicitly in the compiler. It may be NULL_TREE when this is invalid (for instance runtime is not required to implement the function call in all cases. */ tree implicit_built_in_decls[(int) END_BUILTINS]; static int get_pointer_alignment (tree, unsigned int); static tree c_strlen (tree, int); static const char *c_getstr (tree); static rtx c_readstr (const char *, enum machine_mode); static int target_char_cast (tree, char *); static rtx get_memory_rtx (tree); static tree build_string_literal (int, const char *); static int apply_args_size (void); static int apply_result_size (void); #if defined (HAVE_untyped_call) || defined (HAVE_untyped_return) static rtx result_vector (int, rtx); #endif static rtx expand_builtin_setjmp (tree, rtx); static void expand_builtin_prefetch (tree); static rtx expand_builtin_apply_args (void); static rtx expand_builtin_apply_args_1 (void); static rtx expand_builtin_apply (rtx, rtx, rtx); static void expand_builtin_return (rtx); static enum type_class type_to_class (tree); static rtx expand_builtin_classify_type (tree); static void expand_errno_check (tree, rtx); static rtx expand_builtin_mathfn (tree, rtx, rtx); static rtx expand_builtin_mathfn_2 (tree, rtx, rtx); static rtx expand_builtin_constant_p (tree, enum machine_mode); static rtx expand_builtin_args_info (tree); static rtx expand_builtin_next_arg (tree); static rtx expand_builtin_va_start (tree); static rtx expand_builtin_va_end (tree); static rtx expand_builtin_va_copy (tree); static rtx expand_builtin_memcmp (tree, tree, rtx, enum machine_mode); static rtx expand_builtin_strcmp (tree, rtx, enum machine_mode); static rtx expand_builtin_strncmp (tree, rtx, enum machine_mode); static rtx builtin_memcpy_read_str (void *, HOST_WIDE_INT, enum machine_mode); static rtx expand_builtin_strcat (tree, rtx, enum machine_mode); static rtx expand_builtin_strncat (tree, rtx, enum machine_mode); static rtx expand_builtin_strspn (tree, rtx, enum machine_mode); static rtx expand_builtin_strcspn (tree, rtx, enum machine_mode); static rtx expand_builtin_memcpy (tree, rtx, enum machine_mode); static rtx expand_builtin_mempcpy (tree, rtx, enum machine_mode, int); static rtx expand_builtin_memmove (tree, rtx, enum machine_mode); static rtx expand_builtin_bcopy (tree); static rtx expand_builtin_strcpy (tree, rtx, enum machine_mode); static rtx expand_builtin_stpcpy (tree, rtx, enum machine_mode); static rtx builtin_strncpy_read_str (void *, HOST_WIDE_INT, enum machine_mode); static rtx expand_builtin_strncpy (tree, rtx, enum machine_mode); static rtx builtin_memset_read_str (void *, HOST_WIDE_INT, enum machine_mode); static rtx builtin_memset_gen_str (void *, HOST_WIDE_INT, enum machine_mode); static rtx expand_builtin_memset (tree, rtx, enum machine_mode); static rtx expand_builtin_bzero (tree); static rtx expand_builtin_strlen (tree, rtx, enum machine_mode); static rtx expand_builtin_strstr (tree, rtx, enum machine_mode); static rtx expand_builtin_strpbrk (tree, rtx, enum machine_mode); static rtx expand_builtin_strchr (tree, rtx, enum machine_mode); static rtx expand_builtin_strrchr (tree, rtx, enum machine_mode); static rtx expand_builtin_alloca (tree, rtx); static rtx expand_builtin_unop (enum machine_mode, tree, rtx, rtx, optab); static rtx expand_builtin_frame_address (tree, tree); static rtx expand_builtin_fputs (tree, rtx, bool); static rtx expand_builtin_printf (tree, rtx, enum machine_mode, bool); static rtx expand_builtin_fprintf (tree, rtx, enum machine_mode, bool); static rtx expand_builtin_sprintf (tree, rtx, enum machine_mode); static tree stabilize_va_list (tree, int); static rtx expand_builtin_expect (tree, rtx); static tree fold_builtin_constant_p (tree); static tree fold_builtin_classify_type (tree); static tree fold_builtin_inf (tree, int); static tree fold_builtin_nan (tree, tree, int); static int validate_arglist (tree, ...); static bool integer_valued_real_p (tree); static tree fold_trunc_transparent_mathfn (tree); static bool readonly_data_expr (tree); static rtx expand_builtin_fabs (tree, rtx, rtx); static rtx expand_builtin_cabs (tree, rtx); static tree fold_builtin_cabs (tree, tree, tree); static tree fold_builtin_trunc (tree); static tree fold_builtin_floor (tree); static tree fold_builtin_ceil (tree); static tree fold_builtin_bitop (tree); static tree fold_builtin_memcpy (tree); static tree fold_builtin_mempcpy (tree); static tree fold_builtin_memmove (tree); static tree fold_builtin_strcpy (tree); static tree fold_builtin_strncpy (tree); static tree fold_builtin_memcmp (tree); static tree fold_builtin_strcmp (tree); static tree fold_builtin_strncmp (tree); /* Return the alignment in bits of EXP, a pointer valued expression. But don't return more than MAX_ALIGN no matter what. The alignment returned is, by default, the alignment of the thing that EXP points to. If it is not a POINTER_TYPE, 0 is returned. Otherwise, look at the expression to see if we can do better, i.e., if the expression is actually pointing at an object whose alignment is tighter. */ static int get_pointer_alignment (tree exp, unsigned int max_align) { unsigned int align, inner; if (TREE_CODE (TREE_TYPE (exp)) != POINTER_TYPE) return 0; align = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp))); align = MIN (align, max_align); while (1) { switch (TREE_CODE (exp)) { case NOP_EXPR: case CONVERT_EXPR: case NON_LVALUE_EXPR: exp = TREE_OPERAND (exp, 0); if (TREE_CODE (TREE_TYPE (exp)) != POINTER_TYPE) return align; inner = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp))); align = MIN (inner, max_align); break; case PLUS_EXPR: /* If sum of pointer + int, restrict our maximum alignment to that imposed by the integer. If not, we can't do any better than ALIGN. */ if (! host_integerp (TREE_OPERAND (exp, 1), 1)) return align; while (((tree_low_cst (TREE_OPERAND (exp, 1), 1)) & (max_align / BITS_PER_UNIT - 1)) != 0) max_align >>= 1; exp = TREE_OPERAND (exp, 0); break; case ADDR_EXPR: /* See what we are pointing at and look at its alignment. */ exp = TREE_OPERAND (exp, 0); if (TREE_CODE (exp) == FUNCTION_DECL) align = FUNCTION_BOUNDARY; else if (DECL_P (exp)) align = DECL_ALIGN (exp); #ifdef CONSTANT_ALIGNMENT else if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'c') align = CONSTANT_ALIGNMENT (exp, align); #endif return MIN (align, max_align); default: return align; } } } /* Compute the length of a C string. TREE_STRING_LENGTH is not the right way, because it could contain a zero byte in the middle. TREE_STRING_LENGTH is the size of the character array, not the string. ONLY_VALUE should be nonzero if the result is not going to be emitted into the instruction stream and zero if it is going to be expanded. E.g. with i++ ? "foo" : "bar", if ONLY_VALUE is nonzero, constant 3 is returned, otherwise NULL, since len = c_strlen (src, 1); if (len) expand_expr (len, ...); would not evaluate the side-effects. The value returned is of type `ssizetype'. Unfortunately, string_constant can't access the values of const char arrays with initializers, so neither can we do so here. */ static tree c_strlen (tree src, int only_value) { tree offset_node; HOST_WIDE_INT offset; int max; const char *ptr; STRIP_NOPS (src); if (TREE_CODE (src) == COND_EXPR && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0)))) { tree len1, len2; len1 = c_strlen (TREE_OPERAND (src, 1), only_value); len2 = c_strlen (TREE_OPERAND (src, 2), only_value); if (tree_int_cst_equal (len1, len2)) return len1; } if (TREE_CODE (src) == COMPOUND_EXPR && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0)))) return c_strlen (TREE_OPERAND (src, 1), only_value); src = string_constant (src, &offset_node); if (src == 0) return 0; max = TREE_STRING_LENGTH (src) - 1; ptr = TREE_STRING_POINTER (src); if (offset_node && TREE_CODE (offset_node) != INTEGER_CST) { /* If the string has an internal zero byte (e.g., "foo\0bar"), we can't compute the offset to the following null if we don't know where to start searching for it. */ int i; for (i = 0; i < max; i++) if (ptr[i] == 0) return 0; /* We don't know the starting offset, but we do know that the string has no internal zero bytes. We can assume that the offset falls within the bounds of the string; otherwise, the programmer deserves what he gets. Subtract the offset from the length of the string, and return that. This would perhaps not be valid if we were dealing with named arrays in addition to literal string constants. */ return size_diffop (size_int (max), offset_node); } /* We have a known offset into the string. Start searching there for a null character if we can represent it as a single HOST_WIDE_INT. */ if (offset_node == 0) offset = 0; else if (! host_integerp (offset_node, 0)) offset = -1; else offset = tree_low_cst (offset_node, 0); /* If the offset is known to be out of bounds, warn, and call strlen at runtime. */ if (offset < 0 || offset > max) { warning ("offset outside bounds of constant string"); return 0; } /* Use strlen to search for the first zero byte. Since any strings constructed with build_string will have nulls appended, we win even if we get handed something like (char[4])"abcd". Since OFFSET is our starting index into the string, no further calculation is needed. */ return ssize_int (strlen (ptr + offset)); } /* Return a char pointer for a C string if it is a string constant or sum of string constant and integer constant. */ static const char * c_getstr (tree src) { tree offset_node; src = string_constant (src, &offset_node); if (src == 0) return 0; if (offset_node == 0) return TREE_STRING_POINTER (src); else if (!host_integerp (offset_node, 1) || compare_tree_int (offset_node, TREE_STRING_LENGTH (src) - 1) > 0) return 0; return TREE_STRING_POINTER (src) + tree_low_cst (offset_node, 1); } /* Return a CONST_INT or CONST_DOUBLE corresponding to target reading GET_MODE_BITSIZE (MODE) bits from string constant STR. */ static rtx c_readstr (const char *str, enum machine_mode mode) { HOST_WIDE_INT c[2]; HOST_WIDE_INT ch; unsigned int i, j; if (GET_MODE_CLASS (mode) != MODE_INT) abort (); c[0] = 0; c[1] = 0; ch = 1; for (i = 0; i < GET_MODE_SIZE (mode); i++) { j = i; if (WORDS_BIG_ENDIAN) j = GET_MODE_SIZE (mode) - i - 1; if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN && GET_MODE_SIZE (mode) > UNITS_PER_WORD) j = j + UNITS_PER_WORD - 2 * (j % UNITS_PER_WORD) - 1; j *= BITS_PER_UNIT; if (j > 2 * HOST_BITS_PER_WIDE_INT) abort (); if (ch) ch = (unsigned char) str[i]; c[j / HOST_BITS_PER_WIDE_INT] |= ch << (j % HOST_BITS_PER_WIDE_INT); } return immed_double_const (c[0], c[1], mode); } /* Cast a target constant CST to target CHAR and if that value fits into host char type, return zero and put that value into variable pointed by P. */ static int target_char_cast (tree cst, char *p) { unsigned HOST_WIDE_INT val, hostval; if (!host_integerp (cst, 1) || CHAR_TYPE_SIZE > HOST_BITS_PER_WIDE_INT) return 1; val = tree_low_cst (cst, 1); if (CHAR_TYPE_SIZE < HOST_BITS_PER_WIDE_INT) val &= (((unsigned HOST_WIDE_INT) 1) << CHAR_TYPE_SIZE) - 1; hostval = val; if (HOST_BITS_PER_CHAR < HOST_BITS_PER_WIDE_INT) hostval &= (((unsigned HOST_WIDE_INT) 1) << HOST_BITS_PER_CHAR) - 1; if (val != hostval) return 1; *p = hostval; return 0; } /* Given TEM, a pointer to a stack frame, follow the dynamic chain COUNT times to get the address of either a higher stack frame, or a return address located within it (depending on FNDECL_CODE). */ rtx expand_builtin_return_addr (enum built_in_function fndecl_code, int count, rtx tem) { int i; /* Some machines need special handling before we can access arbitrary frames. For example, on the sparc, we must first flush all register windows to the stack. */ #ifdef SETUP_FRAME_ADDRESSES if (count > 0) SETUP_FRAME_ADDRESSES (); #endif /* On the sparc, the return address is not in the frame, it is in a register. There is no way to access it off of the current frame pointer, but it can be accessed off the previous frame pointer by reading the value from the register window save area. */ #ifdef RETURN_ADDR_IN_PREVIOUS_FRAME if (fndecl_code == BUILT_IN_RETURN_ADDRESS) count--; #endif /* Scan back COUNT frames to the specified frame. */ for (i = 0; i < count; i++) { /* Assume the dynamic chain pointer is in the word that the frame address points to, unless otherwise specified. */ #ifdef DYNAMIC_CHAIN_ADDRESS tem = DYNAMIC_CHAIN_ADDRESS (tem); #endif tem = memory_address (Pmode, tem); tem = gen_rtx_MEM (Pmode, tem); set_mem_alias_set (tem, get_frame_alias_set ()); tem = copy_to_reg (tem); } /* For __builtin_frame_address, return what we've got. */ if (fndecl_code == BUILT_IN_FRAME_ADDRESS) return tem; /* For __builtin_return_address, Get the return address from that frame. */ #ifdef RETURN_ADDR_RTX tem = RETURN_ADDR_RTX (count, tem); #else tem = memory_address (Pmode, plus_constant (tem, GET_MODE_SIZE (Pmode))); tem = gen_rtx_MEM (Pmode, tem); set_mem_alias_set (tem, get_frame_alias_set ()); #endif return tem; } /* Alias set used for setjmp buffer. */ static HOST_WIDE_INT setjmp_alias_set = -1; /* Construct the leading half of a __builtin_setjmp call. Control will return to RECEIVER_LABEL. This is used directly by sjlj exception handling code. */ void expand_builtin_setjmp_setup (rtx buf_addr, rtx receiver_label) { enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL); rtx stack_save; rtx mem; if (setjmp_alias_set == -1) setjmp_alias_set = new_alias_set (); buf_addr = convert_memory_address (Pmode, buf_addr); buf_addr = force_reg (Pmode, force_operand (buf_addr, NULL_RTX)); emit_queue (); /* We store the frame pointer and the address of receiver_label in the buffer and use the rest of it for the stack save area, which is machine-dependent. */ #ifndef BUILTIN_SETJMP_FRAME_VALUE #define BUILTIN_SETJMP_FRAME_VALUE virtual_stack_vars_rtx #endif mem = gen_rtx_MEM (Pmode, buf_addr); set_mem_alias_set (mem, setjmp_alias_set); emit_move_insn (mem, BUILTIN_SETJMP_FRAME_VALUE); mem = gen_rtx_MEM (Pmode, plus_constant (buf_addr, GET_MODE_SIZE (Pmode))), set_mem_alias_set (mem, setjmp_alias_set); emit_move_insn (validize_mem (mem), force_reg (Pmode, gen_rtx_LABEL_REF (Pmode, receiver_label))); stack_save = gen_rtx_MEM (sa_mode, plus_constant (buf_addr, 2 * GET_MODE_SIZE (Pmode))); set_mem_alias_set (stack_save, setjmp_alias_set); emit_stack_save (SAVE_NONLOCAL, &stack_save, NULL_RTX); /* If there is further processing to do, do it. */ #ifdef HAVE_builtin_setjmp_setup if (HAVE_builtin_setjmp_setup) emit_insn (gen_builtin_setjmp_setup (buf_addr)); #endif /* Tell optimize_save_area_alloca that extra work is going to need to go on during alloca. */ current_function_calls_setjmp = 1; /* Set this so all the registers get saved in our frame; we need to be able to copy the saved values for any registers from frames we unwind. */ current_function_has_nonlocal_label = 1; } /* Construct the trailing part of a __builtin_setjmp call. This is used directly by sjlj exception handling code. */ void expand_builtin_setjmp_receiver (rtx receiver_label ATTRIBUTE_UNUSED) { /* Clobber the FP when we get here, so we have to make sure it's marked as used by this function. */ emit_insn (gen_rtx_USE (VOIDmode, hard_frame_pointer_rtx)); /* Mark the static chain as clobbered here so life information doesn't get messed up for it. */ emit_insn (gen_rtx_CLOBBER (VOIDmode, static_chain_rtx)); /* Now put in the code to restore the frame pointer, and argument pointer, if needed. The code below is from expand_end_bindings in stmt.c; see detailed documentation there. */ #ifdef HAVE_nonlocal_goto if (! HAVE_nonlocal_goto) #endif emit_move_insn (virtual_stack_vars_rtx, hard_frame_pointer_rtx); #if ARG_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM if (fixed_regs[ARG_POINTER_REGNUM]) { #ifdef ELIMINABLE_REGS size_t i; static const struct elims {const int from, to;} elim_regs[] = ELIMINABLE_REGS; for (i = 0; i < ARRAY_SIZE (elim_regs); i++) if (elim_regs[i].from == ARG_POINTER_REGNUM && elim_regs[i].to == HARD_FRAME_POINTER_REGNUM) break; if (i == ARRAY_SIZE (elim_regs)) #endif { /* Now restore our arg pointer from the address at which it was saved in our stack frame. */ emit_move_insn (virtual_incoming_args_rtx, copy_to_reg (get_arg_pointer_save_area (cfun))); } } #endif #ifdef HAVE_builtin_setjmp_receiver if (HAVE_builtin_setjmp_receiver) emit_insn (gen_builtin_setjmp_receiver (receiver_label)); else #endif #ifdef HAVE_nonlocal_goto_receiver if (HAVE_nonlocal_goto_receiver) emit_insn (gen_nonlocal_goto_receiver ()); else #endif { /* Nothing */ } /* @@@ This is a kludge. Not all machine descriptions define a blockage insn, but we must not allow the code we just generated to be reordered by scheduling. Specifically, the update of the frame pointer must happen immediately, not later. So emit an ASM_INPUT to act as blockage insn. */ emit_insn (gen_rtx_ASM_INPUT (VOIDmode, "")); } /* __builtin_setjmp is passed a pointer to an array of five words (not all will be used on all machines). It operates similarly to the C library function of the same name, but is more efficient. Much of the code below (and for longjmp) is copied from the handling of non-local gotos. NOTE: This is intended for use by GNAT and the exception handling scheme in the compiler and will only work in the method used by them. */ static rtx expand_builtin_setjmp (tree arglist, rtx target) { rtx buf_addr, next_lab, cont_lab; if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) return NULL_RTX; if (target == 0 || GET_CODE (target) != REG || REGNO (target) < FIRST_PSEUDO_REGISTER) target = gen_reg_rtx (TYPE_MODE (integer_type_node)); buf_addr = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0); next_lab = gen_label_rtx (); cont_lab = gen_label_rtx (); expand_builtin_setjmp_setup (buf_addr, next_lab); /* Set TARGET to zero and branch to the continue label. Use emit_jump to ensure that pending stack adjustments are flushed. */ emit_move_insn (target, const0_rtx); emit_jump (cont_lab); emit_label (next_lab); expand_builtin_setjmp_receiver (next_lab); /* Set TARGET to one. */ emit_move_insn (target, const1_rtx); emit_label (cont_lab); /* Tell flow about the strange goings on. Putting `next_lab' on `nonlocal_goto_handler_labels' to indicates that function calls may traverse the arc back to this label. */ current_function_has_nonlocal_label = 1; nonlocal_goto_handler_labels = gen_rtx_EXPR_LIST (VOIDmode, next_lab, nonlocal_goto_handler_labels); return target; } /* __builtin_longjmp is passed a pointer to an array of five words (not all will be used on all machines). It operates similarly to the C library function of the same name, but is more efficient. Much of the code below is copied from the handling of non-local gotos. NOTE: This is intended for use by GNAT and the exception handling scheme in the compiler and will only work in the method used by them. */ void expand_builtin_longjmp (rtx buf_addr, rtx value) { rtx fp, lab, stack, insn, last; enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL); if (setjmp_alias_set == -1) setjmp_alias_set = new_alias_set (); buf_addr = convert_memory_address (Pmode, buf_addr); buf_addr = force_reg (Pmode, buf_addr); /* We used to store value in static_chain_rtx, but that fails if pointers are smaller than integers. We instead require that the user must pass a second argument of 1, because that is what builtin_setjmp will return. This also makes EH slightly more efficient, since we are no longer copying around a value that we don't care about. */ if (value != const1_rtx) abort (); current_function_calls_longjmp = 1; last = get_last_insn (); #ifdef HAVE_builtin_longjmp if (HAVE_builtin_longjmp) emit_insn (gen_builtin_longjmp (buf_addr)); else #endif { fp = gen_rtx_MEM (Pmode, buf_addr); lab = gen_rtx_MEM (Pmode, plus_constant (buf_addr, GET_MODE_SIZE (Pmode))); stack = gen_rtx_MEM (sa_mode, plus_constant (buf_addr, 2 * GET_MODE_SIZE (Pmode))); set_mem_alias_set (fp, setjmp_alias_set); set_mem_alias_set (lab, setjmp_alias_set); set_mem_alias_set (stack, setjmp_alias_set); /* Pick up FP, label, and SP from the block and jump. This code is from expand_goto in stmt.c; see there for detailed comments. */ #if HAVE_nonlocal_goto if (HAVE_nonlocal_goto) /* We have to pass a value to the nonlocal_goto pattern that will get copied into the static_chain pointer, but it does not matter what that value is, because builtin_setjmp does not use it. */ emit_insn (gen_nonlocal_goto (value, lab, stack, fp)); else #endif { lab = copy_to_reg (lab); emit_insn (gen_rtx_CLOBBER (VOIDmode, gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode)))); emit_insn (gen_rtx_CLOBBER (VOIDmode, gen_rtx_MEM (BLKmode, hard_frame_pointer_rtx))); emit_move_insn (hard_frame_pointer_rtx, fp); emit_stack_restore (SAVE_NONLOCAL, stack, NULL_RTX); emit_insn (gen_rtx_USE (VOIDmode, hard_frame_pointer_rtx)); emit_insn (gen_rtx_USE (VOIDmode, stack_pointer_rtx)); emit_indirect_jump (lab); } } /* Search backwards and mark the jump insn as a non-local goto. Note that this precludes the use of __builtin_longjmp to a __builtin_setjmp target in the same function. However, we've already cautioned the user that these functions are for internal exception handling use only. */ for (insn = get_last_insn (); insn; insn = PREV_INSN (insn)) { if (insn == last) abort (); if (GET_CODE (insn) == JUMP_INSN) { REG_NOTES (insn) = alloc_EXPR_LIST (REG_NON_LOCAL_GOTO, const0_rtx, REG_NOTES (insn)); break; } else if (GET_CODE (insn) == CALL_INSN) break; } } /* Expand a call to __builtin_prefetch. For a target that does not support data prefetch, evaluate the memory address argument in case it has side effects. */ static void expand_builtin_prefetch (tree arglist) { tree arg0, arg1, arg2; rtx op0, op1, op2; if (!validate_arglist (arglist, POINTER_TYPE, 0)) return; arg0 = TREE_VALUE (arglist); /* Arguments 1 and 2 are optional; argument 1 (read/write) defaults to zero (read) and argument 2 (locality) defaults to 3 (high degree of locality). */ if (TREE_CHAIN (arglist)) { arg1 = TREE_VALUE (TREE_CHAIN (arglist)); if (TREE_CHAIN (TREE_CHAIN (arglist))) arg2 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); else arg2 = build_int_2 (3, 0); } else { arg1 = integer_zero_node; arg2 = build_int_2 (3, 0); } /* Argument 0 is an address. */ op0 = expand_expr (arg0, NULL_RTX, Pmode, EXPAND_NORMAL); /* Argument 1 (read/write flag) must be a compile-time constant int. */ if (TREE_CODE (arg1) != INTEGER_CST) { error ("second arg to `__builtin_prefetch' must be a constant"); arg1 = integer_zero_node; } op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); /* Argument 1 must be either zero or one. */ if (INTVAL (op1) != 0 && INTVAL (op1) != 1) { warning ("invalid second arg to __builtin_prefetch; using zero"); op1 = const0_rtx; } /* Argument 2 (locality) must be a compile-time constant int. */ if (TREE_CODE (arg2) != INTEGER_CST) { error ("third arg to `__builtin_prefetch' must be a constant"); arg2 = integer_zero_node; } op2 = expand_expr (arg2, NULL_RTX, VOIDmode, 0); /* Argument 2 must be 0, 1, 2, or 3. */ if (INTVAL (op2) < 0 || INTVAL (op2) > 3) { warning ("invalid third arg to __builtin_prefetch; using zero"); op2 = const0_rtx; } #ifdef HAVE_prefetch if (HAVE_prefetch) { if ((! (*insn_data[(int) CODE_FOR_prefetch].operand[0].predicate) (op0, insn_data[(int) CODE_FOR_prefetch].operand[0].mode)) || (GET_MODE (op0) != Pmode)) { op0 = convert_memory_address (Pmode, op0); op0 = force_reg (Pmode, op0); } emit_insn (gen_prefetch (op0, op1, op2)); } else #endif op0 = protect_from_queue (op0, 0); /* Don't do anything with direct references to volatile memory, but generate code to handle other side effects. */ if (GET_CODE (op0) != MEM && side_effects_p (op0)) emit_insn (op0); } /* Get a MEM rtx for expression EXP which is the address of an operand to be used to be used in a string instruction (cmpstrsi, movstrsi, ..). */ static rtx get_memory_rtx (tree exp) { rtx addr = expand_expr (exp, NULL_RTX, ptr_mode, EXPAND_SUM); rtx mem; addr = convert_memory_address (Pmode, addr); mem = gen_rtx_MEM (BLKmode, memory_address (BLKmode, addr)); /* Get an expression we can use to find the attributes to assign to MEM. If it is an ADDR_EXPR, use the operand. Otherwise, dereference it if we can. First remove any nops. */ while ((TREE_CODE (exp) == NOP_EXPR || TREE_CODE (exp) == CONVERT_EXPR || TREE_CODE (exp) == NON_LVALUE_EXPR) && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (exp, 0)))) exp = TREE_OPERAND (exp, 0); if (TREE_CODE (exp) == ADDR_EXPR) { exp = TREE_OPERAND (exp, 0); set_mem_attributes (mem, exp, 0); } else if (POINTER_TYPE_P (TREE_TYPE (exp))) { exp = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (exp)), exp); /* memcpy, memset and other builtin stringops can alias with anything. */ set_mem_alias_set (mem, 0); } return mem; } /* Built-in functions to perform an untyped call and return. */ /* For each register that may be used for calling a function, this gives a mode used to copy the register's value. VOIDmode indicates the register is not used for calling a function. If the machine has register windows, this gives only the outbound registers. INCOMING_REGNO gives the corresponding inbound register. */ static enum machine_mode apply_args_mode[FIRST_PSEUDO_REGISTER]; /* For each register that may be used for returning values, this gives a mode used to copy the register's value. VOIDmode indicates the register is not used for returning values. If the machine has register windows, this gives only the outbound registers. INCOMING_REGNO gives the corresponding inbound register. */ static enum machine_mode apply_result_mode[FIRST_PSEUDO_REGISTER]; /* For each register that may be used for calling a function, this gives the offset of that register into the block returned by __builtin_apply_args. 0 indicates that the register is not used for calling a function. */ static int apply_args_reg_offset[FIRST_PSEUDO_REGISTER]; /* Return the offset of register REGNO into the block returned by __builtin_apply_args. This is not declared static, since it is needed in objc-act.c. */ int apply_args_register_offset (int regno) { apply_args_size (); /* Arguments are always put in outgoing registers (in the argument block) if such make sense. */ #ifdef OUTGOING_REGNO regno = OUTGOING_REGNO (regno); #endif return apply_args_reg_offset[regno]; } /* Return the size required for the block returned by __builtin_apply_args, and initialize apply_args_mode. */ static int apply_args_size (void) { static int size = -1; int align; unsigned int regno; enum machine_mode mode; /* The values computed by this function never change. */ if (size < 0) { /* The first value is the incoming arg-pointer. */ size = GET_MODE_SIZE (Pmode); /* The second value is the structure value address unless this is passed as an "invisible" first argument. */ if (targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 0)) size += GET_MODE_SIZE (Pmode); for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) if (FUNCTION_ARG_REGNO_P (regno)) { /* Search for the proper mode for copying this register's value. I'm not sure this is right, but it works so far. */ enum machine_mode best_mode = VOIDmode; for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode; mode = GET_MODE_WIDER_MODE (mode)) if (HARD_REGNO_MODE_OK (regno, mode) && HARD_REGNO_NREGS (regno, mode) == 1) best_mode = mode; if (best_mode == VOIDmode) for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); mode != VOIDmode; mode = GET_MODE_WIDER_MODE (mode)) if (HARD_REGNO_MODE_OK (regno, mode) && have_insn_for (SET, mode)) best_mode = mode; if (best_mode == VOIDmode) for (mode = GET_CLASS_NARROWEST_MODE (MODE_VECTOR_FLOAT); mode != VOIDmode; mode = GET_MODE_WIDER_MODE (mode)) if (HARD_REGNO_MODE_OK (regno, mode) && have_insn_for (SET, mode)) best_mode = mode; if (best_mode == VOIDmode) for (mode = GET_CLASS_NARROWEST_MODE (MODE_VECTOR_INT); mode != VOIDmode; mode = GET_MODE_WIDER_MODE (mode)) if (HARD_REGNO_MODE_OK (regno, mode) && have_insn_for (SET, mode)) best_mode = mode; mode = best_mode; if (mode == VOIDmode) abort (); align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; if (size % align != 0) size = CEIL (size, align) * align; apply_args_reg_offset[regno] = size; size += GET_MODE_SIZE (mode); apply_args_mode[regno] = mode; } else { apply_args_mode[regno] = VOIDmode; apply_args_reg_offset[regno] = 0; } } return size; } /* Return the size required for the block returned by __builtin_apply, and initialize apply_result_mode. */ static int apply_result_size (void) { static int size = -1; int align, regno; enum machine_mode mode; /* The values computed by this function never change. */ if (size < 0) { size = 0; for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) if (FUNCTION_VALUE_REGNO_P (regno)) { /* Search for the proper mode for copying this register's value. I'm not sure this is right, but it works so far. */ enum machine_mode best_mode = VOIDmode; for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != TImode; mode = GET_MODE_WIDER_MODE (mode)) if (HARD_REGNO_MODE_OK (regno, mode)) best_mode = mode; if (best_mode == VOIDmode) for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); mode != VOIDmode; mode = GET_MODE_WIDER_MODE (mode)) if (HARD_REGNO_MODE_OK (regno, mode) && have_insn_for (SET, mode)) best_mode = mode; if (best_mode == VOIDmode) for (mode = GET_CLASS_NARROWEST_MODE (MODE_VECTOR_FLOAT); mode != VOIDmode; mode = GET_MODE_WIDER_MODE (mode)) if (HARD_REGNO_MODE_OK (regno, mode) && have_insn_for (SET, mode)) best_mode = mode; if (best_mode == VOIDmode) for (mode = GET_CLASS_NARROWEST_MODE (MODE_VECTOR_INT); mode != VOIDmode; mode = GET_MODE_WIDER_MODE (mode)) if (HARD_REGNO_MODE_OK (regno, mode) && have_insn_for (SET, mode)) best_mode = mode; mode = best_mode; if (mode == VOIDmode) abort (); align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; if (size % align != 0) size = CEIL (size, align) * align; size += GET_MODE_SIZE (mode); apply_result_mode[regno] = mode; } else apply_result_mode[regno] = VOIDmode; /* Allow targets that use untyped_call and untyped_return to override the size so that machine-specific information can be stored here. */ #ifdef APPLY_RESULT_SIZE size = APPLY_RESULT_SIZE; #endif } return size; } #if defined (HAVE_untyped_call) || defined (HAVE_untyped_return) /* Create a vector describing the result block RESULT. If SAVEP is true, the result block is used to save the values; otherwise it is used to restore the values. */ static rtx result_vector (int savep, rtx result) { int regno, size, align, nelts; enum machine_mode mode; rtx reg, mem; rtx *savevec = alloca (FIRST_PSEUDO_REGISTER * sizeof (rtx)); size = nelts = 0; for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) if ((mode = apply_result_mode[regno]) != VOIDmode) { align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; if (size % align != 0) size = CEIL (size, align) * align; reg = gen_rtx_REG (mode, savep ? regno : INCOMING_REGNO (regno)); mem = adjust_address (result, mode, size); savevec[nelts++] = (savep ? gen_rtx_SET (VOIDmode, mem, reg) : gen_rtx_SET (VOIDmode, reg, mem)); size += GET_MODE_SIZE (mode); } return gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (nelts, savevec)); } #endif /* HAVE_untyped_call or HAVE_untyped_return */ /* Save the state required to perform an untyped call with the same arguments as were passed to the current function. */ static rtx expand_builtin_apply_args_1 (void) { rtx registers, tem; int size, align, regno; enum machine_mode mode; rtx struct_incoming_value = targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 1); /* Create a block where the arg-pointer, structure value address, and argument registers can be saved. */ registers = assign_stack_local (BLKmode, apply_args_size (), -1); /* Walk past the arg-pointer and structure value address. */ size = GET_MODE_SIZE (Pmode); if (targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 0)) size += GET_MODE_SIZE (Pmode); /* Save each register used in calling a function to the block. */ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) if ((mode = apply_args_mode[regno]) != VOIDmode) { align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; if (size % align != 0) size = CEIL (size, align) * align; tem = gen_rtx_REG (mode, INCOMING_REGNO (regno)); emit_move_insn (adjust_address (registers, mode, size), tem); size += GET_MODE_SIZE (mode); } /* Save the arg pointer to the block. */ tem = copy_to_reg (virtual_incoming_args_rtx); #ifdef STACK_GROWS_DOWNWARD /* We need the pointer as the caller actually passed them to us, not as we might have pretended they were passed. Make sure it's a valid operand, as emit_move_insn isn't expected to handle a PLUS. */ tem = force_operand (plus_constant (tem, current_function_pretend_args_size), NULL_RTX); #endif emit_move_insn (adjust_address (registers, Pmode, 0), tem); size = GET_MODE_SIZE (Pmode); /* Save the structure value address unless this is passed as an "invisible" first argument. */ if (struct_incoming_value) { emit_move_insn (adjust_address (registers, Pmode, size), copy_to_reg (struct_incoming_value)); size += GET_MODE_SIZE (Pmode); } /* Return the address of the block. */ return copy_addr_to_reg (XEXP (registers, 0)); } /* __builtin_apply_args returns block of memory allocated on the stack into which is stored the arg pointer, structure value address, static chain, and all the registers that might possibly be used in performing a function call. The code is moved to the start of the function so the incoming values are saved. */ static rtx expand_builtin_apply_args (void) { /* Don't do __builtin_apply_args more than once in a function. Save the result of the first call and reuse it. */ if (apply_args_value != 0) return apply_args_value; { /* When this function is called, it means that registers must be saved on entry to this function. So we migrate the call to the first insn of this function. */ rtx temp; rtx seq; start_sequence (); temp = expand_builtin_apply_args_1 (); seq = get_insns (); end_sequence (); apply_args_value = temp; /* Put the insns after the NOTE that starts the function. If this is inside a start_sequence, make the outer-level insn chain current, so the code is placed at the start of the function. */ push_topmost_sequence (); emit_insn_before (seq, NEXT_INSN (get_insns ())); pop_topmost_sequence (); return temp; } } /* Perform an untyped call and save the state required to perform an untyped return of whatever value was returned by the given function. */ static rtx expand_builtin_apply (rtx function, rtx arguments, rtx argsize) { int size, align, regno; enum machine_mode mode; rtx incoming_args, result, reg, dest, src, call_insn; rtx old_stack_level = 0; rtx call_fusage = 0; rtx struct_value = targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 0); arguments = convert_memory_address (Pmode, arguments); /* Create a block where the return registers can be saved. */ result = assign_stack_local (BLKmode, apply_result_size (), -1); /* Fetch the arg pointer from the ARGUMENTS block. */ incoming_args = gen_reg_rtx (Pmode); emit_move_insn (incoming_args, gen_rtx_MEM (Pmode, arguments)); #ifndef STACK_GROWS_DOWNWARD incoming_args = expand_simple_binop (Pmode, MINUS, incoming_args, argsize, incoming_args, 0, OPTAB_LIB_WIDEN); #endif /* Perform postincrements before actually calling the function. */ emit_queue (); /* Push a new argument block and copy the arguments. Do not allow the (potential) memcpy call below to interfere with our stack manipulations. */ do_pending_stack_adjust (); NO_DEFER_POP; /* Save the stack with nonlocal if available. */ #ifdef HAVE_save_stack_nonlocal if (HAVE_save_stack_nonlocal) emit_stack_save (SAVE_NONLOCAL, &old_stack_level, NULL_RTX); else #endif emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX); /* Allocate a block of memory onto the stack and copy the memory arguments to the outgoing arguments address. */ allocate_dynamic_stack_space (argsize, 0, BITS_PER_UNIT); dest = virtual_outgoing_args_rtx; #ifndef STACK_GROWS_DOWNWARD if (GET_CODE (argsize) == CONST_INT) dest = plus_constant (dest, -INTVAL (argsize)); else dest = gen_rtx_PLUS (Pmode, dest, negate_rtx (Pmode, argsize)); #endif dest = gen_rtx_MEM (BLKmode, dest); set_mem_align (dest, PARM_BOUNDARY); src = gen_rtx_MEM (BLKmode, incoming_args); set_mem_align (src, PARM_BOUNDARY); emit_block_move (dest, src, argsize, BLOCK_OP_NORMAL); /* Refer to the argument block. */ apply_args_size (); arguments = gen_rtx_MEM (BLKmode, arguments); set_mem_align (arguments, PARM_BOUNDARY); /* Walk past the arg-pointer and structure value address. */ size = GET_MODE_SIZE (Pmode); if (struct_value) size += GET_MODE_SIZE (Pmode); /* Restore each of the registers previously saved. Make USE insns for each of these registers for use in making the call. */ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) if ((mode = apply_args_mode[regno]) != VOIDmode) { align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; if (size % align != 0) size = CEIL (size, align) * align; reg = gen_rtx_REG (mode, regno); emit_move_insn (reg, adjust_address (arguments, mode, size)); use_reg (&call_fusage, reg); size += GET_MODE_SIZE (mode); } /* Restore the structure value address unless this is passed as an "invisible" first argument. */ size = GET_MODE_SIZE (Pmode); if (struct_value) { rtx value = gen_reg_rtx (Pmode); emit_move_insn (value, adjust_address (arguments, Pmode, size)); emit_move_insn (struct_value, value); if (GET_CODE (struct_value) == REG) use_reg (&call_fusage, struct_value); size += GET_MODE_SIZE (Pmode); } /* All arguments and registers used for the call are set up by now! */ function = prepare_call_address (function, NULL_TREE, &call_fusage, 0, 0); /* Ensure address is valid. SYMBOL_REF is already valid, so no need, and we don't want to load it into a register as an optimization, because prepare_call_address already did it if it should be done. */ if (GET_CODE (function) != SYMBOL_REF) function = memory_address (FUNCTION_MODE, function); /* Generate the actual call instruction and save the return value. */ #ifdef HAVE_untyped_call if (HAVE_untyped_call) emit_call_insn (gen_untyped_call (gen_rtx_MEM (FUNCTION_MODE, function), result, result_vector (1, result))); else #endif #ifdef HAVE_call_value if (HAVE_call_value) { rtx valreg = 0; /* Locate the unique return register. It is not possible to express a call that sets more than one return register using call_value; use untyped_call for that. In fact, untyped_call only needs to save the return registers in the given block. */ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) if ((mode = apply_result_mode[regno]) != VOIDmode) { if (valreg) abort (); /* HAVE_untyped_call required. */ valreg = gen_rtx_REG (mode, regno); } emit_call_insn (GEN_CALL_VALUE (valreg, gen_rtx_MEM (FUNCTION_MODE, function), const0_rtx, NULL_RTX, const0_rtx)); emit_move_insn (adjust_address (result, GET_MODE (valreg), 0), valreg); } else #endif abort (); /* Find the CALL insn we just emitted, and attach the register usage information. */ call_insn = last_call_insn (); add_function_usage_to (call_insn, call_fusage); /* Restore the stack. */ #ifdef HAVE_save_stack_nonlocal if (HAVE_save_stack_nonlocal) emit_stack_restore (SAVE_NONLOCAL, old_stack_level, NULL_RTX); else #endif emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX); OK_DEFER_POP; /* Return the address of the result block. */ result = copy_addr_to_reg (XEXP (result, 0)); return convert_memory_address (ptr_mode, result); } /* Perform an untyped return. */ static void expand_builtin_return (rtx result) { int size, align, regno; enum machine_mode mode; rtx reg; rtx call_fusage = 0; result = convert_memory_address (Pmode, result); apply_result_size (); result = gen_rtx_MEM (BLKmode, result); #ifdef HAVE_untyped_return if (HAVE_untyped_return) { emit_jump_insn (gen_untyped_return (result, result_vector (0, result))); emit_barrier (); return; } #endif /* Restore the return value and note that each value is used. */ size = 0; for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) if ((mode = apply_result_mode[regno]) != VOIDmode) { align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; if (size % align != 0) size = CEIL (size, align) * align; reg = gen_rtx_REG (mode, INCOMING_REGNO (regno)); emit_move_insn (reg, adjust_address (result, mode, size)); push_to_sequence (call_fusage); emit_insn (gen_rtx_USE (VOIDmode, reg)); call_fusage = get_insns (); end_sequence (); size += GET_MODE_SIZE (mode); } /* Put the USE insns before the return. */ emit_insn (call_fusage); /* Return whatever values was restored by jumping directly to the end of the function. */ expand_naked_return (); } /* Used by expand_builtin_classify_type and fold_builtin_classify_type. */ static enum type_class type_to_class (tree type) { switch (TREE_CODE (type)) { case VOID_TYPE: return void_type_class; case INTEGER_TYPE: return integer_type_class; case CHAR_TYPE: return char_type_class; case ENUMERAL_TYPE: return enumeral_type_class; case BOOLEAN_TYPE: return boolean_type_class; case POINTER_TYPE: return pointer_type_class; case REFERENCE_TYPE: return reference_type_class; case OFFSET_TYPE: return offset_type_class; case REAL_TYPE: return real_type_class; case COMPLEX_TYPE: return complex_type_class; case FUNCTION_TYPE: return function_type_class; case METHOD_TYPE: return method_type_class; case RECORD_TYPE: return record_type_class; case UNION_TYPE: case QUAL_UNION_TYPE: return union_type_class; case ARRAY_TYPE: return (TYPE_STRING_FLAG (type) ? string_type_class : array_type_class); case SET_TYPE: return set_type_class; case FILE_TYPE: return file_type_class; case LANG_TYPE: return lang_type_class; default: return no_type_class; } } /* Expand a call to __builtin_classify_type with arguments found in ARGLIST. */ static rtx expand_builtin_classify_type (tree arglist) { if (arglist != 0) return GEN_INT (type_to_class (TREE_TYPE (TREE_VALUE (arglist)))); return GEN_INT (no_type_class); } /* Expand expression EXP, which is a call to __builtin_constant_p. */ static rtx expand_builtin_constant_p (tree arglist, enum machine_mode target_mode) { rtx tmp; if (arglist == 0) return const0_rtx; arglist = TREE_VALUE (arglist); /* We have taken care of the easy cases during constant folding. This case is not obvious, so emit (constant_p_rtx (ARGLIST)) and let CSE get a chance to see if it can deduce whether ARGLIST is constant. If CSE isn't going to run, of course, don't bother waiting. */ if (cse_not_expected) return const0_rtx; current_function_calls_constant_p = 1; tmp = expand_expr (arglist, NULL_RTX, VOIDmode, 0); tmp = gen_rtx_CONSTANT_P_RTX (target_mode, tmp); return tmp; } /* This helper macro, meant to be used in mathfn_built_in below, determines which among a set of three builtin math functions is appropriate for a given type mode. The `F' and `L' cases are automatically generated from the `double' case. */ #define CASE_MATHFN(BUILT_IN_MATHFN) \ case BUILT_IN_MATHFN: case BUILT_IN_MATHFN##F: case BUILT_IN_MATHFN##L: \ fcode = BUILT_IN_MATHFN; fcodef = BUILT_IN_MATHFN##F ; \ fcodel = BUILT_IN_MATHFN##L ; break; /* Return mathematic function equivalent to FN but operating directly on TYPE, if available. If we can't do the conversion, return zero. */ tree mathfn_built_in (tree type, enum built_in_function fn) { const enum machine_mode type_mode = TYPE_MODE (type); enum built_in_function fcode, fcodef, fcodel; switch (fn) { CASE_MATHFN (BUILT_IN_ACOS) CASE_MATHFN (BUILT_IN_ACOSH) CASE_MATHFN (BUILT_IN_ASIN) CASE_MATHFN (BUILT_IN_ASINH) CASE_MATHFN (BUILT_IN_ATAN) CASE_MATHFN (BUILT_IN_ATAN2) CASE_MATHFN (BUILT_IN_ATANH) CASE_MATHFN (BUILT_IN_CBRT) CASE_MATHFN (BUILT_IN_CEIL) CASE_MATHFN (BUILT_IN_COPYSIGN) CASE_MATHFN (BUILT_IN_COS) CASE_MATHFN (BUILT_IN_COSH) CASE_MATHFN (BUILT_IN_DREM) CASE_MATHFN (BUILT_IN_ERF) CASE_MATHFN (BUILT_IN_ERFC) CASE_MATHFN (BUILT_IN_EXP) CASE_MATHFN (BUILT_IN_EXP10) CASE_MATHFN (BUILT_IN_EXP2) CASE_MATHFN (BUILT_IN_EXPM1) CASE_MATHFN (BUILT_IN_FABS) CASE_MATHFN (BUILT_IN_FDIM) CASE_MATHFN (BUILT_IN_FLOOR) CASE_MATHFN (BUILT_IN_FMA) CASE_MATHFN (BUILT_IN_FMAX) CASE_MATHFN (BUILT_IN_FMIN) CASE_MATHFN (BUILT_IN_FMOD) CASE_MATHFN (BUILT_IN_FREXP) CASE_MATHFN (BUILT_IN_GAMMA) CASE_MATHFN (BUILT_IN_HUGE_VAL) CASE_MATHFN (BUILT_IN_HYPOT) CASE_MATHFN (BUILT_IN_ILOGB) CASE_MATHFN (BUILT_IN_INF) CASE_MATHFN (BUILT_IN_J0) CASE_MATHFN (BUILT_IN_J1) CASE_MATHFN (BUILT_IN_JN) CASE_MATHFN (BUILT_IN_LDEXP) CASE_MATHFN (BUILT_IN_LGAMMA) CASE_MATHFN (BUILT_IN_LLRINT) CASE_MATHFN (BUILT_IN_LLROUND) CASE_MATHFN (BUILT_IN_LOG) CASE_MATHFN (BUILT_IN_LOG10) CASE_MATHFN (BUILT_IN_LOG1P) CASE_MATHFN (BUILT_IN_LOG2) CASE_MATHFN (BUILT_IN_LOGB) CASE_MATHFN (BUILT_IN_LRINT) CASE_MATHFN (BUILT_IN_LROUND) CASE_MATHFN (BUILT_IN_MODF) CASE_MATHFN (BUILT_IN_NAN) CASE_MATHFN (BUILT_IN_NANS) CASE_MATHFN (BUILT_IN_NEARBYINT) CASE_MATHFN (BUILT_IN_NEXTAFTER) CASE_MATHFN (BUILT_IN_NEXTTOWARD) CASE_MATHFN (BUILT_IN_POW) CASE_MATHFN (BUILT_IN_POW10) CASE_MATHFN (BUILT_IN_REMAINDER) CASE_MATHFN (BUILT_IN_REMQUO) CASE_MATHFN (BUILT_IN_RINT) CASE_MATHFN (BUILT_IN_ROUND) CASE_MATHFN (BUILT_IN_SCALB) CASE_MATHFN (BUILT_IN_SCALBLN) CASE_MATHFN (BUILT_IN_SCALBN) CASE_MATHFN (BUILT_IN_SIGNIFICAND) CASE_MATHFN (BUILT_IN_SIN) CASE_MATHFN (BUILT_IN_SINCOS) CASE_MATHFN (BUILT_IN_SINH) CASE_MATHFN (BUILT_IN_SQRT) CASE_MATHFN (BUILT_IN_TAN) CASE_MATHFN (BUILT_IN_TANH) CASE_MATHFN (BUILT_IN_TGAMMA) CASE_MATHFN (BUILT_IN_TRUNC) CASE_MATHFN (BUILT_IN_Y0) CASE_MATHFN (BUILT_IN_Y1) CASE_MATHFN (BUILT_IN_YN) default: return 0; } if (type_mode == TYPE_MODE (double_type_node)) return implicit_built_in_decls[fcode]; else if (type_mode == TYPE_MODE (float_type_node)) return implicit_built_in_decls[fcodef]; else if (type_mode == TYPE_MODE (long_double_type_node)) return implicit_built_in_decls[fcodel]; else return 0; } /* If errno must be maintained, expand the RTL to check if the result, TARGET, of a built-in function call, EXP, is NaN, and if so set errno to EDOM. */ static void expand_errno_check (tree exp, rtx target) { rtx lab = gen_label_rtx (); /* Test the result; if it is NaN, set errno=EDOM because the argument was not in the domain. */ emit_cmp_and_jump_insns (target, target, EQ, 0, GET_MODE (target), 0, lab); #ifdef TARGET_EDOM /* If this built-in doesn't throw an exception, set errno directly. */ if (TREE_NOTHROW (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))) { #ifdef GEN_ERRNO_RTX rtx errno_rtx = GEN_ERRNO_RTX; #else rtx errno_rtx = gen_rtx_MEM (word_mode, gen_rtx_SYMBOL_REF (Pmode, "errno")); #endif emit_move_insn (errno_rtx, GEN_INT (TARGET_EDOM)); emit_label (lab); return; } #endif /* We can't set errno=EDOM directly; let the library call do it. Pop the arguments right away in case the call gets deleted. */ NO_DEFER_POP; expand_call (exp, target, 0); OK_DEFER_POP; emit_label (lab); } /* Expand a call to one of the builtin math functions (sin, cos, or sqrt). Return 0 if a normal call should be emitted rather than expanding the function in-line. EXP is the expression that is a call to the builtin function; if convenient, the result should be placed in TARGET. SUBTARGET may be used as the target for computing one of EXP's operands. */ static rtx expand_builtin_mathfn (tree exp, rtx target, rtx subtarget) { optab builtin_optab; rtx op0, insns, before_call; tree fndecl = get_callee_fndecl (exp); tree arglist = TREE_OPERAND (exp, 1); enum machine_mode mode; bool errno_set = false; tree arg, narg; if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) return 0; arg = TREE_VALUE (arglist); switch (DECL_FUNCTION_CODE (fndecl)) { case BUILT_IN_SIN: case BUILT_IN_SINF: case BUILT_IN_SINL: builtin_optab = sin_optab; break; case BUILT_IN_COS: case BUILT_IN_COSF: case BUILT_IN_COSL: builtin_optab = cos_optab; break; case BUILT_IN_SQRT: case BUILT_IN_SQRTF: case BUILT_IN_SQRTL: errno_set = ! tree_expr_nonnegative_p (arg); builtin_optab = sqrt_optab; break; case BUILT_IN_EXP: case BUILT_IN_EXPF: case BUILT_IN_EXPL: errno_set = true; builtin_optab = exp_optab; break; case BUILT_IN_LOG: case BUILT_IN_LOGF: case BUILT_IN_LOGL: errno_set = true; builtin_optab = log_optab; break; case BUILT_IN_TAN: case BUILT_IN_TANF: case BUILT_IN_TANL: builtin_optab = tan_optab; break; case BUILT_IN_ATAN: case BUILT_IN_ATANF: case BUILT_IN_ATANL: builtin_optab = atan_optab; break; case BUILT_IN_FLOOR: case BUILT_IN_FLOORF: case BUILT_IN_FLOORL: builtin_optab = floor_optab; break; case BUILT_IN_CEIL: case BUILT_IN_CEILF: case BUILT_IN_CEILL: builtin_optab = ceil_optab; break; case BUILT_IN_TRUNC: case BUILT_IN_TRUNCF: case BUILT_IN_TRUNCL: builtin_optab = btrunc_optab; break; case BUILT_IN_ROUND: case BUILT_IN_ROUNDF: case BUILT_IN_ROUNDL: builtin_optab = round_optab; break; case BUILT_IN_NEARBYINT: case BUILT_IN_NEARBYINTF: case BUILT_IN_NEARBYINTL: builtin_optab = nearbyint_optab; break; default: abort (); } /* Make a suitable register to place result in. */ mode = TYPE_MODE (TREE_TYPE (exp)); if (! flag_errno_math || ! HONOR_NANS (mode)) errno_set = false; /* Before working hard, check whether the instruction is available. */ if (builtin_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) { target = gen_reg_rtx (mode); /* Wrap the computation of the argument in a SAVE_EXPR, as we may need to expand the argument again. This way, we will not perform side-effects more the once. */ narg = save_expr (arg); if (narg != arg) { arg = narg; arglist = build_tree_list (NULL_TREE, arg); exp = build_function_call_expr (fndecl, arglist); } op0 = expand_expr (arg, subtarget, VOIDmode, 0); emit_queue (); start_sequence (); /* Compute into TARGET. Set TARGET to wherever the result comes back. */ target = expand_unop (mode, builtin_optab, op0, target, 0); if (target != 0) { if (errno_set) expand_errno_check (exp, target); /* Output the entire sequence. */ insns = get_insns (); end_sequence (); emit_insn (insns); return target; } /* If we were unable to expand via the builtin, stop the sequence (without outputting the insns) and call to the library function with the stabilized argument list. */ end_sequence (); } before_call = get_last_insn (); target = expand_call (exp, target, target == const0_rtx); /* If this is a sqrt operation and we don't care about errno, try to attach a REG_EQUAL note with a SQRT rtx to the emitted libcall. This allows the semantics of the libcall to be visible to the RTL optimizers. */ if (builtin_optab == sqrt_optab && !errno_set) { /* Search backwards through the insns emitted by expand_call looking for the instruction with the REG_RETVAL note. */ rtx last = get_last_insn (); while (last != before_call) { if (find_reg_note (last, REG_RETVAL, NULL)) { rtx note = find_reg_note (last, REG_EQUAL, NULL); /* Check that the REQ_EQUAL note is an EXPR_LIST with two elements, i.e. symbol_ref(sqrt) and the operand. */ if (note && GET_CODE (note) == EXPR_LIST && GET_CODE (XEXP (note, 0)) == EXPR_LIST && XEXP (XEXP (note, 0), 1) != NULL_RTX && XEXP (XEXP (XEXP (note, 0), 1), 1) == NULL_RTX) { rtx operand = XEXP (XEXP (XEXP (note, 0), 1), 0); /* Check operand is a register with expected mode. */ if (operand && GET_CODE (operand) == REG && GET_MODE (operand) == mode) { /* Replace the REG_EQUAL note with a SQRT rtx. */ rtx equiv = gen_rtx_SQRT (mode, operand); set_unique_reg_note (last, REG_EQUAL, equiv); } } break; } last = PREV_INSN (last); } } return target; } /* Expand a call to the builtin binary math functions (pow and atan2). Return 0 if a normal call should be emitted rather than expanding the function in-line. EXP is the expression that is a call to the builtin function; if convenient, the result should be placed in TARGET. SUBTARGET may be used as the target for computing one of EXP's operands. */ static rtx expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget) { optab builtin_optab; rtx op0, op1, insns; tree fndecl = get_callee_fndecl (exp); tree arglist = TREE_OPERAND (exp, 1); tree arg0, arg1, temp, narg; enum machine_mode mode; bool errno_set = true; bool stable = true; if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) return 0; arg0 = TREE_VALUE (arglist); arg1 = TREE_VALUE (TREE_CHAIN (arglist)); switch (DECL_FUNCTION_CODE (fndecl)) { case BUILT_IN_POW: case BUILT_IN_POWF: case BUILT_IN_POWL: builtin_optab = pow_optab; break; case BUILT_IN_ATAN2: case BUILT_IN_ATAN2F: case BUILT_IN_ATAN2L: builtin_optab = atan2_optab; break; default: abort (); } /* Make a suitable register to place result in. */ mode = TYPE_MODE (TREE_TYPE (exp)); /* Before working hard, check whether the instruction is available. */ if (builtin_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) return 0; target = gen_reg_rtx (mode); if (! flag_errno_math || ! HONOR_NANS (mode)) errno_set = false; /* Alway stabilize the argument list. */ narg = save_expr (arg1); if (narg != arg1) { arg1 = narg; temp = build_tree_list (NULL_TREE, narg); stable = false; } else temp = TREE_CHAIN (arglist); narg = save_expr (arg0); if (narg != arg0) { arg0 = narg; arglist = tree_cons (NULL_TREE, narg, temp); stable = false; } else if (! stable) arglist = tree_cons (NULL_TREE, arg0, temp); if (! stable) exp = build_function_call_expr (fndecl, arglist); op0 = expand_expr (arg0, subtarget, VOIDmode, 0); op1 = expand_expr (arg1, 0, VOIDmode, 0); emit_queue (); start_sequence (); /* Compute into TARGET. Set TARGET to wherever the result comes back. */ target = expand_binop (mode, builtin_optab, op0, op1, target, 0, OPTAB_DIRECT); /* If we were unable to expand via the builtin, stop the sequence (without outputting the insns) and call to the library function with the stabilized argument list. */ if (target == 0) { end_sequence (); return expand_call (exp, target, target == const0_rtx); } if (errno_set) expand_errno_check (exp, target); /* Output the entire sequence. */ insns = get_insns (); end_sequence (); emit_insn (insns); return target; } /* To evaluate powi(x,n), the floating point value x raised to the constant integer exponent n, we use a hybrid algorithm that combines the "window method" with look-up tables. For an introduction to exponentiation algorithms and "addition chains", see section 4.6.3, "Evaluation of Powers" of Donald E. Knuth, "Seminumerical Algorithms", Vol. 2, "The Art of Computer Programming", 3rd Edition, 1998, and Daniel M. Gordon, "A Survey of Fast Exponentiation Methods", Journal of Algorithms, Vol. 27, pp. 129-146, 1998. */ /* Provide a default value for POWI_MAX_MULTS, the maximum number of multiplications to inline before calling the system library's pow function. powi(x,n) requires at worst 2*bits(n)-2 multiplications, so this default never requires calling pow, powf or powl. */ #ifndef POWI_MAX_MULTS #define POWI_MAX_MULTS (2*HOST_BITS_PER_WIDE_INT-2) #endif /* The size of the "optimal power tree" lookup table. All exponents less than this value are simply looked up in the powi_table below. This threshold is also used to size the cache of pseudo registers that hold intermediate results. */ #define POWI_TABLE_SIZE 256 /* The size, in bits of the window, used in the "window method" exponentiation algorithm. This is equivalent to a radix of (1<= POWI_TABLE_SIZE) { if (val & 1) { digit = val & ((1 << POWI_WINDOW_SIZE) - 1); result += powi_lookup_cost (digit, cache) + POWI_WINDOW_SIZE + 1; val >>= POWI_WINDOW_SIZE; } else { val >>= 1; result++; } } return result + powi_lookup_cost (val, cache); } /* Recursive subroutine of expand_powi. This function takes the array, CACHE, of already calculated exponents and an exponent N and returns an RTX that corresponds to CACHE[1]**N, as calculated in mode MODE. */ static rtx expand_powi_1 (enum machine_mode mode, unsigned HOST_WIDE_INT n, rtx *cache) { unsigned HOST_WIDE_INT digit; rtx target, result; rtx op0, op1; if (n < POWI_TABLE_SIZE) { if (cache[n]) return cache[n]; target = gen_reg_rtx (mode); cache[n] = target; op0 = expand_powi_1 (mode, n - powi_table[n], cache); op1 = expand_powi_1 (mode, powi_table[n], cache); } else if (n & 1) { target = gen_reg_rtx (mode); digit = n & ((1 << POWI_WINDOW_SIZE) - 1); op0 = expand_powi_1 (mode, n - digit, cache); op1 = expand_powi_1 (mode, digit, cache); } else { target = gen_reg_rtx (mode); op0 = expand_powi_1 (mode, n >> 1, cache); op1 = op0; } result = expand_mult (mode, op0, op1, target, 0); if (result != target) emit_move_insn (target, result); return target; } /* Expand the RTL to evaluate powi(x,n) in mode MODE. X is the floating point operand in mode MODE, and N is the exponent. This function needs to be kept in sync with powi_cost above. */ static rtx expand_powi (rtx x, enum machine_mode mode, HOST_WIDE_INT n) { unsigned HOST_WIDE_INT val; rtx cache[POWI_TABLE_SIZE]; rtx result; if (n == 0) return CONST1_RTX (mode); val = (n < 0) ? -n : n; memset (cache, 0, sizeof (cache)); cache[1] = x; result = expand_powi_1 (mode, (n < 0) ? -n : n, cache); /* If the original exponent was negative, reciprocate the result. */ if (n < 0) result = expand_binop (mode, sdiv_optab, CONST1_RTX (mode), result, NULL_RTX, 0, OPTAB_LIB_WIDEN); return result; } /* Expand a call to the pow built-in mathematical function. Return 0 if a normal call should be emitted rather than expanding the function in-line. EXP is the expression that is a call to the builtin function; if convenient, the result should be placed in TARGET. */ static rtx expand_builtin_pow (tree exp, rtx target, rtx subtarget) { tree arglist = TREE_OPERAND (exp, 1); tree arg0, arg1; if (! validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) return 0; arg0 = TREE_VALUE (arglist); arg1 = TREE_VALUE (TREE_CHAIN (arglist)); if (TREE_CODE (arg1) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg1)) { REAL_VALUE_TYPE cint; REAL_VALUE_TYPE c; HOST_WIDE_INT n; c = TREE_REAL_CST (arg1); n = real_to_integer (&c); real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0); if (real_identical (&c, &cint)) { /* If the exponent is -1, 0, 1 or 2, then expand_powi is exact. Otherwise, check the number of multiplications required. Note that pow never sets errno for an integer exponent. */ if ((n >= -1 && n <= 2) || (flag_unsafe_math_optimizations && ! optimize_size && powi_cost (n) <= POWI_MAX_MULTS)) { enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp)); rtx op = expand_expr (arg0, subtarget, VOIDmode, 0); op = force_reg (mode, op); return expand_powi (op, mode, n); } } } return expand_builtin_mathfn_2 (exp, target, NULL_RTX); } /* Expand expression EXP which is a call to the strlen builtin. Return 0 if we failed the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient. */ static rtx expand_builtin_strlen (tree arglist, rtx target, enum machine_mode target_mode) { if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) return 0; else { rtx pat; tree len, src = TREE_VALUE (arglist); rtx result, src_reg, char_rtx, before_strlen; enum machine_mode insn_mode = target_mode, char_mode; enum insn_code icode = CODE_FOR_nothing; int align; /* If the length can be computed at compile-time, return it. */ len = c_strlen (src, 0); if (len) return expand_expr (len, target, target_mode, EXPAND_NORMAL); /* If the length can be computed at compile-time and is constant integer, but there are side-effects in src, evaluate src for side-effects, then return len. E.g. x = strlen (i++ ? "xfoo" + 1 : "bar"); can be optimized into: i++; x = 3; */ len = c_strlen (src, 1); if (len && TREE_CODE (len) == INTEGER_CST) { expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL); return expand_expr (len, target, target_mode, EXPAND_NORMAL); } align = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; /* If SRC is not a pointer type, don't do this operation inline. */ if (align == 0) return 0; /* Bail out if we can't compute strlen in the right mode. */ while (insn_mode != VOIDmode) { icode = strlen_optab->handlers[(int) insn_mode].insn_code; if (icode != CODE_FOR_nothing) break; insn_mode = GET_MODE_WIDER_MODE (insn_mode); } if (insn_mode == VOIDmode) return 0; /* Make a place to write the result of the instruction. */ result = target; if (! (result != 0 && GET_CODE (result) == REG && GET_MODE (result) == insn_mode && REGNO (result) >= FIRST_PSEUDO_REGISTER)) result = gen_reg_rtx (insn_mode); /* Make a place to hold the source address. We will not expand the actual source until we are sure that the expansion will not fail -- there are trees that cannot be expanded twice. */ src_reg = gen_reg_rtx (Pmode); /* Mark the beginning of the strlen sequence so we can emit the source operand later. */ before_strlen = get_last_insn (); char_rtx = const0_rtx; char_mode = insn_data[(int) icode].operand[2].mode; if (! (*insn_data[(int) icode].operand[2].predicate) (char_rtx, char_mode)) char_rtx = copy_to_mode_reg (char_mode, char_rtx); pat = GEN_FCN (icode) (result, gen_rtx_MEM (BLKmode, src_reg), char_rtx, GEN_INT (align)); if (! pat) return 0; emit_insn (pat); /* Now that we are assured of success, expand the source. */ start_sequence (); pat = memory_address (BLKmode, expand_expr (src, src_reg, ptr_mode, EXPAND_SUM)); if (pat != src_reg) emit_move_insn (src_reg, pat); pat = get_insns (); end_sequence (); if (before_strlen) emit_insn_after (pat, before_strlen); else emit_insn_before (pat, get_insns ()); /* Return the value in the proper mode for this function. */ if (GET_MODE (result) == target_mode) target = result; else if (target != 0) convert_move (target, result, 0); else target = convert_to_mode (target_mode, result, 0); return target; } } /* Expand a call to the strstr builtin. Return 0 if we failed the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient (and in mode MODE if that's convenient). */ static rtx expand_builtin_strstr (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; else { tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); tree fn; const char *p1, *p2; p2 = c_getstr (s2); if (p2 == NULL) return 0; p1 = c_getstr (s1); if (p1 != NULL) { const char *r = strstr (p1, p2); if (r == NULL) return const0_rtx; /* Return an offset into the constant string argument. */ return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1), s1, convert (TREE_TYPE (s1), ssize_int (r - p1)))), target, mode, EXPAND_NORMAL); } if (p2[0] == '\0') return expand_expr (s1, target, mode, EXPAND_NORMAL); if (p2[1] != '\0') return 0; fn = implicit_built_in_decls[BUILT_IN_STRCHR]; if (!fn) return 0; /* New argument list transforming strstr(s1, s2) to strchr(s1, s2[0]). */ arglist = build_tree_list (NULL_TREE, build_int_2 (p2[0], 0)); arglist = tree_cons (NULL_TREE, s1, arglist); return expand_expr (build_function_call_expr (fn, arglist), target, mode, EXPAND_NORMAL); } } /* Expand a call to the strchr builtin. Return 0 if we failed the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient (and in mode MODE if that's convenient). */ static rtx expand_builtin_strchr (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; else { tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); const char *p1; if (TREE_CODE (s2) != INTEGER_CST) return 0; p1 = c_getstr (s1); if (p1 != NULL) { char c; const char *r; if (target_char_cast (s2, &c)) return 0; r = strchr (p1, c); if (r == NULL) return const0_rtx; /* Return an offset into the constant string argument. */ return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1), s1, convert (TREE_TYPE (s1), ssize_int (r - p1)))), target, mode, EXPAND_NORMAL); } /* FIXME: Should use here strchrM optab so that ports can optimize this. */ return 0; } } /* Expand a call to the strrchr builtin. Return 0 if we failed the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient (and in mode MODE if that's convenient). */ static rtx expand_builtin_strrchr (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; else { tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); tree fn; const char *p1; if (TREE_CODE (s2) != INTEGER_CST) return 0; p1 = c_getstr (s1); if (p1 != NULL) { char c; const char *r; if (target_char_cast (s2, &c)) return 0; r = strrchr (p1, c); if (r == NULL) return const0_rtx; /* Return an offset into the constant string argument. */ return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1), s1, convert (TREE_TYPE (s1), ssize_int (r - p1)))), target, mode, EXPAND_NORMAL); } if (! integer_zerop (s2)) return 0; fn = implicit_built_in_decls[BUILT_IN_STRCHR]; if (!fn) return 0; /* Transform strrchr(s1, '\0') to strchr(s1, '\0'). */ return expand_expr (build_function_call_expr (fn, arglist), target, mode, EXPAND_NORMAL); } } /* Expand a call to the strpbrk builtin. Return 0 if we failed the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient (and in mode MODE if that's convenient). */ static rtx expand_builtin_strpbrk (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; else { tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); tree fn; const char *p1, *p2; p2 = c_getstr (s2); if (p2 == NULL) return 0; p1 = c_getstr (s1); if (p1 != NULL) { const char *r = strpbrk (p1, p2); if (r == NULL) return const0_rtx; /* Return an offset into the constant string argument. */ return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1), s1, convert (TREE_TYPE (s1), ssize_int (r - p1)))), target, mode, EXPAND_NORMAL); } if (p2[0] == '\0') { /* strpbrk(x, "") == NULL. Evaluate and ignore the arguments in case they had side-effects. */ expand_expr (s1, const0_rtx, VOIDmode, EXPAND_NORMAL); return const0_rtx; } if (p2[1] != '\0') return 0; /* Really call strpbrk. */ fn = implicit_built_in_decls[BUILT_IN_STRCHR]; if (!fn) return 0; /* New argument list transforming strpbrk(s1, s2) to strchr(s1, s2[0]). */ arglist = build_tree_list (NULL_TREE, build_int_2 (p2[0], 0)); arglist = tree_cons (NULL_TREE, s1, arglist); return expand_expr (build_function_call_expr (fn, arglist), target, mode, EXPAND_NORMAL); } } /* Callback routine for store_by_pieces. Read GET_MODE_BITSIZE (MODE) bytes from constant string DATA + OFFSET and return it as target constant. */ static rtx builtin_memcpy_read_str (void *data, HOST_WIDE_INT offset, enum machine_mode mode) { const char *str = (const char *) data; if (offset < 0 || ((unsigned HOST_WIDE_INT) offset + GET_MODE_SIZE (mode) > strlen (str) + 1)) abort (); /* Attempt to read past the end of constant string. */ return c_readstr (str + offset, mode); } /* Expand a call to the memcpy builtin, with arguments in ARGLIST. Return 0 if we failed, the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient (and in mode MODE if that's convenient). */ static rtx expand_builtin_memcpy (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; else { tree dest = TREE_VALUE (arglist); tree src = TREE_VALUE (TREE_CHAIN (arglist)); tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); const char *src_str; unsigned int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT); unsigned int dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); rtx dest_mem, src_mem, dest_addr, len_rtx; /* If DEST is not a pointer type, call the normal function. */ if (dest_align == 0) return 0; /* If the LEN parameter is zero, return DEST. */ if (integer_zerop (len)) { /* Evaluate and ignore SRC in case it has side-effects. */ expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL); return expand_expr (dest, target, mode, EXPAND_NORMAL); } /* If SRC and DEST are the same (and not volatile), return DEST. */ if (operand_equal_p (src, dest, 0)) { /* Evaluate and ignore LEN in case it has side-effects. */ expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL); return expand_expr (dest, target, mode, EXPAND_NORMAL); } /* If either SRC is not a pointer type, don't do this operation in-line. */ if (src_align == 0) return 0; dest_mem = get_memory_rtx (dest); set_mem_align (dest_mem, dest_align); len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); src_str = c_getstr (src); /* If SRC is a string constant and block move would be done by pieces, we can avoid loading the string from memory and only stored the computed constants. */ if (src_str && GET_CODE (len_rtx) == CONST_INT && (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1 && can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str, (void *) src_str, dest_align)) { dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx), builtin_memcpy_read_str, (void *) src_str, dest_align, 0); dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX); dest_mem = convert_memory_address (ptr_mode, dest_mem); return dest_mem; } src_mem = get_memory_rtx (src); set_mem_align (src_mem, src_align); /* Copy word part most expediently. */ dest_addr = emit_block_move (dest_mem, src_mem, len_rtx, BLOCK_OP_NORMAL); if (dest_addr == 0) { dest_addr = force_operand (XEXP (dest_mem, 0), NULL_RTX); dest_addr = convert_memory_address (ptr_mode, dest_addr); } return dest_addr; } } /* Expand a call to the mempcpy builtin, with arguments in ARGLIST. Return 0 if we failed the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient (and in mode MODE if that's convenient). If ENDP is 0 return the destination pointer, if ENDP is 1 return the end pointer ala mempcpy, and if ENDP is 2 return the end pointer minus one ala stpcpy. */ static rtx expand_builtin_mempcpy (tree arglist, rtx target, enum machine_mode mode, int endp) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; /* If return value is ignored, transform mempcpy into memcpy. */ else if (target == const0_rtx) { tree fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; if (!fn) return 0; return expand_expr (build_function_call_expr (fn, arglist), target, mode, EXPAND_NORMAL); } else { tree dest = TREE_VALUE (arglist); tree src = TREE_VALUE (TREE_CHAIN (arglist)); tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); const char *src_str; unsigned int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT); unsigned int dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); rtx dest_mem, src_mem, len_rtx; /* If DEST is not a pointer type, call the normal function. */ if (dest_align == 0) return 0; /* If SRC and DEST are the same (and not volatile), do nothing. */ if (operand_equal_p (src, dest, 0)) { tree expr; if (endp == 0) { /* Evaluate and ignore LEN in case it has side-effects. */ expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL); return expand_expr (dest, target, mode, EXPAND_NORMAL); } if (endp == 2) len = fold (build (MINUS_EXPR, TREE_TYPE (len), dest, integer_one_node)); len = convert (TREE_TYPE (dest), len); expr = fold (build (PLUS_EXPR, TREE_TYPE (dest), dest, len)); return expand_expr (expr, target, mode, EXPAND_NORMAL); } /* If LEN is not constant, call the normal function. */ if (! host_integerp (len, 1)) return 0; /* If the LEN parameter is zero, return DEST. */ if (tree_low_cst (len, 1) == 0) { /* Evaluate and ignore SRC in case it has side-effects. */ expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL); return expand_expr (dest, target, mode, EXPAND_NORMAL); } /* If either SRC is not a pointer type, don't do this operation in-line. */ if (src_align == 0) return 0; len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); src_str = c_getstr (src); /* If SRC is a string constant and block move would be done by pieces, we can avoid loading the string from memory and only stored the computed constants. */ if (src_str && GET_CODE (len_rtx) == CONST_INT && (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1 && can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str, (void *) src_str, dest_align)) { dest_mem = get_memory_rtx (dest); set_mem_align (dest_mem, dest_align); dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx), builtin_memcpy_read_str, (void *) src_str, dest_align, endp); dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX); dest_mem = convert_memory_address (ptr_mode, dest_mem); return dest_mem; } if (GET_CODE (len_rtx) == CONST_INT && can_move_by_pieces (INTVAL (len_rtx), MIN (dest_align, src_align))) { dest_mem = get_memory_rtx (dest); set_mem_align (dest_mem, dest_align); src_mem = get_memory_rtx (src); set_mem_align (src_mem, src_align); dest_mem = move_by_pieces (dest_mem, src_mem, INTVAL (len_rtx), MIN (dest_align, src_align), endp); dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX); dest_mem = convert_memory_address (ptr_mode, dest_mem); return dest_mem; } return 0; } } /* Expand expression EXP, which is a call to the memmove builtin. Return 0 if we failed the caller should emit a normal call. */ static rtx expand_builtin_memmove (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; else { tree dest = TREE_VALUE (arglist); tree src = TREE_VALUE (TREE_CHAIN (arglist)); tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); unsigned int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT); unsigned int dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); /* If DEST is not a pointer type, call the normal function. */ if (dest_align == 0) return 0; /* If the LEN parameter is zero, return DEST. */ if (integer_zerop (len)) { /* Evaluate and ignore SRC in case it has side-effects. */ expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL); return expand_expr (dest, target, mode, EXPAND_NORMAL); } /* If SRC and DEST are the same (and not volatile), return DEST. */ if (operand_equal_p (src, dest, 0)) { /* Evaluate and ignore LEN in case it has side-effects. */ expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL); return expand_expr (dest, target, mode, EXPAND_NORMAL); } /* If either SRC is not a pointer type, don't do this operation in-line. */ if (src_align == 0) return 0; /* If src is categorized for a readonly section we can use normal memcpy. */ if (readonly_data_expr (src)) { tree const fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; if (!fn) return 0; return expand_expr (build_function_call_expr (fn, arglist), target, mode, EXPAND_NORMAL); } /* Otherwise, call the normal function. */ return 0; } } /* Expand expression EXP, which is a call to the bcopy builtin. Return 0 if we failed the caller should emit a normal call. */ static rtx expand_builtin_bcopy (tree arglist) { tree src, dest, size, newarglist; if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return NULL_RTX; src = TREE_VALUE (arglist); dest = TREE_VALUE (TREE_CHAIN (arglist)); size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); /* New argument list transforming bcopy(ptr x, ptr y, int z) to memmove(ptr y, ptr x, size_t z). This is done this way so that if it isn't expanded inline, we fallback to calling bcopy instead of memmove. */ newarglist = build_tree_list (NULL_TREE, convert (sizetype, size)); newarglist = tree_cons (NULL_TREE, src, newarglist); newarglist = tree_cons (NULL_TREE, dest, newarglist); return expand_builtin_memmove (newarglist, const0_rtx, VOIDmode); } /* Expand expression EXP, which is a call to the strcpy builtin. Return 0 if we failed the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient (and in mode MODE if that's convenient). */ static rtx expand_builtin_strcpy (tree arglist, rtx target, enum machine_mode mode) { tree fn, len, src, dst; if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; src = TREE_VALUE (TREE_CHAIN (arglist)); dst = TREE_VALUE (arglist); /* If SRC and DST are equal (and not volatile), return DST. */ if (operand_equal_p (src, dst, 0)) return expand_expr (dst, target, mode, EXPAND_NORMAL); fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; if (!fn) return 0; len = c_strlen (src, 1); if (len == 0 || TREE_SIDE_EFFECTS (len)) return 0; len = size_binop (PLUS_EXPR, len, ssize_int (1)); arglist = build_tree_list (NULL_TREE, len); arglist = tree_cons (NULL_TREE, src, arglist); arglist = tree_cons (NULL_TREE, dst, arglist); return expand_expr (build_function_call_expr (fn, arglist), target, mode, EXPAND_NORMAL); } /* Expand a call to the stpcpy builtin, with arguments in ARGLIST. Return 0 if we failed the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient (and in mode MODE if that's convenient). */ static rtx expand_builtin_stpcpy (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; else { tree dst, src, len; /* If return value is ignored, transform stpcpy into strcpy. */ if (target == const0_rtx) { tree fn = implicit_built_in_decls[BUILT_IN_STRCPY]; if (!fn) return 0; return expand_expr (build_function_call_expr (fn, arglist), target, mode, EXPAND_NORMAL); } /* Ensure we get an actual string whose length can be evaluated at compile-time, not an expression containing a string. This is because the latter will potentially produce pessimized code when used to produce the return value. */ src = TREE_VALUE (TREE_CHAIN (arglist)); if (! c_getstr (src) || ! (len = c_strlen (src, 0))) return 0; dst = TREE_VALUE (arglist); len = fold (size_binop (PLUS_EXPR, len, ssize_int (1))); arglist = build_tree_list (NULL_TREE, len); arglist = tree_cons (NULL_TREE, src, arglist); arglist = tree_cons (NULL_TREE, dst, arglist); return expand_builtin_mempcpy (arglist, target, mode, /*endp=*/2); } } /* Callback routine for store_by_pieces. Read GET_MODE_BITSIZE (MODE) bytes from constant string DATA + OFFSET and return it as target constant. */ static rtx builtin_strncpy_read_str (void *data, HOST_WIDE_INT offset, enum machine_mode mode) { const char *str = (const char *) data; if ((unsigned HOST_WIDE_INT) offset > strlen (str)) return const0_rtx; return c_readstr (str + offset, mode); } /* Expand expression EXP, which is a call to the strncpy builtin. Return 0 if we failed the caller should emit a normal call. */ static rtx expand_builtin_strncpy (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; else { tree slen = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)), 1); tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); tree fn; /* We must be passed a constant len parameter. */ if (TREE_CODE (len) != INTEGER_CST) return 0; /* If the len parameter is zero, return the dst parameter. */ if (integer_zerop (len)) { /* Evaluate and ignore the src argument in case it has side-effects. */ expand_expr (TREE_VALUE (TREE_CHAIN (arglist)), const0_rtx, VOIDmode, EXPAND_NORMAL); /* Return the dst parameter. */ return expand_expr (TREE_VALUE (arglist), target, mode, EXPAND_NORMAL); } /* Now, we must be passed a constant src ptr parameter. */ if (slen == 0 || TREE_CODE (slen) != INTEGER_CST) return 0; slen = size_binop (PLUS_EXPR, slen, ssize_int (1)); /* We're required to pad with trailing zeros if the requested len is greater than strlen(s2)+1. In that case try to use store_by_pieces, if it fails, punt. */ if (tree_int_cst_lt (slen, len)) { tree dest = TREE_VALUE (arglist); unsigned int dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); const char *p = c_getstr (TREE_VALUE (TREE_CHAIN (arglist))); rtx dest_mem; if (!p || dest_align == 0 || !host_integerp (len, 1) || !can_store_by_pieces (tree_low_cst (len, 1), builtin_strncpy_read_str, (void *) p, dest_align)) return 0; dest_mem = get_memory_rtx (dest); store_by_pieces (dest_mem, tree_low_cst (len, 1), builtin_strncpy_read_str, (void *) p, dest_align, 0); dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX); dest_mem = convert_memory_address (ptr_mode, dest_mem); return dest_mem; } /* OK transform into builtin memcpy. */ fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; if (!fn) return 0; return expand_expr (build_function_call_expr (fn, arglist), target, mode, EXPAND_NORMAL); } } /* Callback routine for store_by_pieces. Read GET_MODE_BITSIZE (MODE) bytes from constant string DATA + OFFSET and return it as target constant. */ static rtx builtin_memset_read_str (void *data, HOST_WIDE_INT offset ATTRIBUTE_UNUSED, enum machine_mode mode) { const char *c = (const char *) data; char *p = alloca (GET_MODE_SIZE (mode)); memset (p, *c, GET_MODE_SIZE (mode)); return c_readstr (p, mode); } /* Callback routine for store_by_pieces. Return the RTL of a register containing GET_MODE_SIZE (MODE) consecutive copies of the unsigned char value given in the RTL register data. For example, if mode is 4 bytes wide, return the RTL for 0x01010101*data. */ static rtx builtin_memset_gen_str (void *data, HOST_WIDE_INT offset ATTRIBUTE_UNUSED, enum machine_mode mode) { rtx target, coeff; size_t size; char *p; size = GET_MODE_SIZE (mode); if (size == 1) return (rtx) data; p = alloca (size); memset (p, 1, size); coeff = c_readstr (p, mode); target = convert_to_mode (mode, (rtx) data, 1); target = expand_mult (mode, target, coeff, NULL_RTX, 1); return force_reg (mode, target); } /* Expand expression EXP, which is a call to the memset builtin. Return 0 if we failed the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient (and in mode MODE if that's convenient). */ static rtx expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; else { tree dest = TREE_VALUE (arglist); tree val = TREE_VALUE (TREE_CHAIN (arglist)); tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); char c; unsigned int dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); rtx dest_mem, dest_addr, len_rtx; /* If DEST is not a pointer type, don't do this operation in-line. */ if (dest_align == 0) return 0; /* If the LEN parameter is zero, return DEST. */ if (integer_zerop (len)) { /* Evaluate and ignore VAL in case it has side-effects. */ expand_expr (val, const0_rtx, VOIDmode, EXPAND_NORMAL); return expand_expr (dest, target, mode, EXPAND_NORMAL); } if (TREE_CODE (val) != INTEGER_CST) { rtx val_rtx; if (!host_integerp (len, 1)) return 0; if (optimize_size && tree_low_cst (len, 1) > 1) return 0; /* Assume that we can memset by pieces if we can store the * the coefficients by pieces (in the required modes). * We can't pass builtin_memset_gen_str as that emits RTL. */ c = 1; if (!can_store_by_pieces (tree_low_cst (len, 1), builtin_memset_read_str, &c, dest_align)) return 0; val = fold (build1 (CONVERT_EXPR, unsigned_char_type_node, val)); val_rtx = expand_expr (val, NULL_RTX, VOIDmode, 0); val_rtx = force_reg (TYPE_MODE (unsigned_char_type_node), val_rtx); dest_mem = get_memory_rtx (dest); store_by_pieces (dest_mem, tree_low_cst (len, 1), builtin_memset_gen_str, val_rtx, dest_align, 0); dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX); dest_mem = convert_memory_address (ptr_mode, dest_mem); return dest_mem; } if (target_char_cast (val, &c)) return 0; if (c) { if (!host_integerp (len, 1)) return 0; if (!can_store_by_pieces (tree_low_cst (len, 1), builtin_memset_read_str, &c, dest_align)) return 0; dest_mem = get_memory_rtx (dest); store_by_pieces (dest_mem, tree_low_cst (len, 1), builtin_memset_read_str, &c, dest_align, 0); dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX); dest_mem = convert_memory_address (ptr_mode, dest_mem); return dest_mem; } len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); dest_mem = get_memory_rtx (dest); set_mem_align (dest_mem, dest_align); dest_addr = clear_storage (dest_mem, len_rtx); if (dest_addr == 0) { dest_addr = force_operand (XEXP (dest_mem, 0), NULL_RTX); dest_addr = convert_memory_address (ptr_mode, dest_addr); } return dest_addr; } } /* Expand expression EXP, which is a call to the bzero builtin. Return 0 if we failed the caller should emit a normal call. */ static rtx expand_builtin_bzero (tree arglist) { tree dest, size, newarglist; if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return NULL_RTX; dest = TREE_VALUE (arglist); size = TREE_VALUE (TREE_CHAIN (arglist)); /* New argument list transforming bzero(ptr x, int y) to memset(ptr x, int 0, size_t y). This is done this way so that if it isn't expanded inline, we fallback to calling bzero instead of memset. */ newarglist = build_tree_list (NULL_TREE, convert (sizetype, size)); newarglist = tree_cons (NULL_TREE, integer_zero_node, newarglist); newarglist = tree_cons (NULL_TREE, dest, newarglist); return expand_builtin_memset (newarglist, const0_rtx, VOIDmode); } /* Expand expression EXP, which is a call to the memcmp built-in function. ARGLIST is the argument list for this call. Return 0 if we failed and the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient (and in mode MODE, if that's convenient). */ static rtx expand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target, enum machine_mode mode) { tree arg1, arg2, len; const char *p1, *p2; if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; arg1 = TREE_VALUE (arglist); arg2 = TREE_VALUE (TREE_CHAIN (arglist)); len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); /* If the len parameter is zero, return zero. */ if (integer_zerop (len)) { /* Evaluate and ignore arg1 and arg2 in case they have side-effects. */ expand_expr (arg1, const0_rtx, VOIDmode, EXPAND_NORMAL); expand_expr (arg2, const0_rtx, VOIDmode, EXPAND_NORMAL); return const0_rtx; } /* If both arguments are equal (and not volatile), return zero. */ if (operand_equal_p (arg1, arg2, 0)) { /* Evaluate and ignore len in case it has side-effects. */ expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL); return const0_rtx; } p1 = c_getstr (arg1); p2 = c_getstr (arg2); /* If all arguments are constant, and the value of len is not greater than the lengths of arg1 and arg2, evaluate at compile-time. */ if (host_integerp (len, 1) && p1 && p2 && compare_tree_int (len, strlen (p1) + 1) <= 0 && compare_tree_int (len, strlen (p2) + 1) <= 0) { const int r = memcmp (p1, p2, tree_low_cst (len, 1)); return (r < 0 ? constm1_rtx : (r > 0 ? const1_rtx : const0_rtx)); } /* If len parameter is one, return an expression corresponding to (*(const unsigned char*)arg1 - (const unsigned char*)arg2). */ if (integer_onep (len)) { tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node); tree ind1 = fold (build1 (CONVERT_EXPR, integer_type_node, build1 (INDIRECT_REF, cst_uchar_node, build1 (NOP_EXPR, cst_uchar_ptr_node, arg1)))); tree ind2 = fold (build1 (CONVERT_EXPR, integer_type_node, build1 (INDIRECT_REF, cst_uchar_node, build1 (NOP_EXPR, cst_uchar_ptr_node, arg2)))); tree result = fold (build (MINUS_EXPR, integer_type_node, ind1, ind2)); return expand_expr (result, target, mode, EXPAND_NORMAL); } #if defined HAVE_cmpmemsi || defined HAVE_cmpstrsi { rtx arg1_rtx, arg2_rtx, arg3_rtx; rtx result; rtx insn; int arg1_align = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; int arg2_align = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; enum machine_mode insn_mode; #ifdef HAVE_cmpmemsi if (HAVE_cmpmemsi) insn_mode = insn_data[(int) CODE_FOR_cmpmemsi].operand[0].mode; else #endif #ifdef HAVE_cmpstrsi if (HAVE_cmpstrsi) insn_mode = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode; else #endif return 0; /* If we don't have POINTER_TYPE, call the function. */ if (arg1_align == 0 || arg2_align == 0) return 0; /* Make a place to write the result of the instruction. */ result = target; if (! (result != 0 && GET_CODE (result) == REG && GET_MODE (result) == insn_mode && REGNO (result) >= FIRST_PSEUDO_REGISTER)) result = gen_reg_rtx (insn_mode); arg1_rtx = get_memory_rtx (arg1); arg2_rtx = get_memory_rtx (arg2); arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); #ifdef HAVE_cmpmemsi if (HAVE_cmpmemsi) insn = gen_cmpmemsi (result, arg1_rtx, arg2_rtx, arg3_rtx, GEN_INT (MIN (arg1_align, arg2_align))); else #endif #ifdef HAVE_cmpstrsi if (HAVE_cmpstrsi) insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx, GEN_INT (MIN (arg1_align, arg2_align))); else #endif abort (); if (insn) emit_insn (insn); else emit_library_call_value (memcmp_libfunc, result, LCT_PURE_MAKE_BLOCK, TYPE_MODE (integer_type_node), 3, XEXP (arg1_rtx, 0), Pmode, XEXP (arg2_rtx, 0), Pmode, convert_to_mode (TYPE_MODE (sizetype), arg3_rtx, TREE_UNSIGNED (sizetype)), TYPE_MODE (sizetype)); /* Return the value in the proper mode for this function. */ mode = TYPE_MODE (TREE_TYPE (exp)); if (GET_MODE (result) == mode) return result; else if (target != 0) { convert_move (target, result, 0); return target; } else return convert_to_mode (mode, result, 0); } #endif return 0; } /* Expand expression EXP, which is a call to the strcmp builtin. Return 0 if we failed the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient. */ static rtx expand_builtin_strcmp (tree exp, rtx target, enum machine_mode mode) { tree arglist = TREE_OPERAND (exp, 1); tree arg1, arg2; const char *p1, *p2; if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; arg1 = TREE_VALUE (arglist); arg2 = TREE_VALUE (TREE_CHAIN (arglist)); /* If both arguments are equal (and not volatile), return zero. */ if (operand_equal_p (arg1, arg2, 0)) return const0_rtx; p1 = c_getstr (arg1); p2 = c_getstr (arg2); if (p1 && p2) { const int i = strcmp (p1, p2); return (i < 0 ? constm1_rtx : (i > 0 ? const1_rtx : const0_rtx)); } /* If either arg is "", return an expression corresponding to (*(const unsigned char*)arg1 - (const unsigned char*)arg2). */ if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0')) { tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node); tree ind1 = fold (build1 (CONVERT_EXPR, integer_type_node, build1 (INDIRECT_REF, cst_uchar_node, build1 (NOP_EXPR, cst_uchar_ptr_node, arg1)))); tree ind2 = fold (build1 (CONVERT_EXPR, integer_type_node, build1 (INDIRECT_REF, cst_uchar_node, build1 (NOP_EXPR, cst_uchar_ptr_node, arg2)))); tree result = fold (build (MINUS_EXPR, integer_type_node, ind1, ind2)); return expand_expr (result, target, mode, EXPAND_NORMAL); } #ifdef HAVE_cmpstrsi if (HAVE_cmpstrsi) { tree len, len1, len2; rtx arg1_rtx, arg2_rtx, arg3_rtx; rtx result, insn; tree fndecl; int arg1_align = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; int arg2_align = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; enum machine_mode insn_mode = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode; len1 = c_strlen (arg1, 1); len2 = c_strlen (arg2, 1); if (len1) len1 = size_binop (PLUS_EXPR, ssize_int (1), len1); if (len2) len2 = size_binop (PLUS_EXPR, ssize_int (1), len2); /* If we don't have a constant length for the first, use the length of the second, if we know it. We don't require a constant for this case; some cost analysis could be done if both are available but neither is constant. For now, assume they're equally cheap, unless one has side effects. If both strings have constant lengths, use the smaller. */ if (!len1) len = len2; else if (!len2) len = len1; else if (TREE_SIDE_EFFECTS (len1)) len = len2; else if (TREE_SIDE_EFFECTS (len2)) len = len1; else if (TREE_CODE (len1) != INTEGER_CST) len = len2; else if (TREE_CODE (len2) != INTEGER_CST) len = len1; else if (tree_int_cst_lt (len1, len2)) len = len1; else len = len2; /* If both arguments have side effects, we cannot optimize. */ if (!len || TREE_SIDE_EFFECTS (len)) return 0; /* If we don't have POINTER_TYPE, call the function. */ if (arg1_align == 0 || arg2_align == 0) return 0; /* Make a place to write the result of the instruction. */ result = target; if (! (result != 0 && GET_CODE (result) == REG && GET_MODE (result) == insn_mode && REGNO (result) >= FIRST_PSEUDO_REGISTER)) result = gen_reg_rtx (insn_mode); /* Stabilize the arguments in case gen_cmpstrsi fails. */ arg1 = save_expr (arg1); arg2 = save_expr (arg2); arg1_rtx = get_memory_rtx (arg1); arg2_rtx = get_memory_rtx (arg2); arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx, GEN_INT (MIN (arg1_align, arg2_align))); if (insn) { emit_insn (insn); /* Return the value in the proper mode for this function. */ mode = TYPE_MODE (TREE_TYPE (exp)); if (GET_MODE (result) == mode) return result; if (target == 0) return convert_to_mode (mode, result, 0); convert_move (target, result, 0); return target; } /* Expand the library call ourselves using a stabilized argument list to avoid re-evaluating the function's arguments twice. */ arglist = build_tree_list (NULL_TREE, arg2); arglist = tree_cons (NULL_TREE, arg1, arglist); fndecl = get_callee_fndecl (exp); exp = build_function_call_expr (fndecl, arglist); return expand_call (exp, target, target == const0_rtx); } #endif return 0; } /* Expand expression EXP, which is a call to the strncmp builtin. Return 0 if we failed the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient. */ static rtx expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode) { tree arglist = TREE_OPERAND (exp, 1); tree arg1, arg2, arg3; const char *p1, *p2; if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; arg1 = TREE_VALUE (arglist); arg2 = TREE_VALUE (TREE_CHAIN (arglist)); arg3 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); /* If the len parameter is zero, return zero. */ if (integer_zerop (arg3)) { /* Evaluate and ignore arg1 and arg2 in case they have side-effects. */ expand_expr (arg1, const0_rtx, VOIDmode, EXPAND_NORMAL); expand_expr (arg2, const0_rtx, VOIDmode, EXPAND_NORMAL); return const0_rtx; } /* If arg1 and arg2 are equal (and not volatile), return zero. */ if (operand_equal_p (arg1, arg2, 0)) { /* Evaluate and ignore arg3 in case it has side-effects. */ expand_expr (arg3, const0_rtx, VOIDmode, EXPAND_NORMAL); return const0_rtx; } p1 = c_getstr (arg1); p2 = c_getstr (arg2); /* If all arguments are constant, evaluate at compile-time. */ if (host_integerp (arg3, 1) && p1 && p2) { const int r = strncmp (p1, p2, tree_low_cst (arg3, 1)); return (r < 0 ? constm1_rtx : (r > 0 ? const1_rtx : const0_rtx)); } /* If len == 1 or (either string parameter is "" and (len >= 1)), return (*(const u_char*)arg1 - *(const u_char*)arg2). */ if (host_integerp (arg3, 1) && (tree_low_cst (arg3, 1) == 1 || (tree_low_cst (arg3, 1) > 1 && ((p1 && *p1 == '\0') || (p2 && *p2 == '\0'))))) { tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node); tree ind1 = fold (build1 (CONVERT_EXPR, integer_type_node, build1 (INDIRECT_REF, cst_uchar_node, build1 (NOP_EXPR, cst_uchar_ptr_node, arg1)))); tree ind2 = fold (build1 (CONVERT_EXPR, integer_type_node, build1 (INDIRECT_REF, cst_uchar_node, build1 (NOP_EXPR, cst_uchar_ptr_node, arg2)))); tree result = fold (build (MINUS_EXPR, integer_type_node, ind1, ind2)); return expand_expr (result, target, mode, EXPAND_NORMAL); } /* If c_strlen can determine an expression for one of the string lengths, and it doesn't have side effects, then emit cmpstrsi using length MIN(strlen(string)+1, arg3). */ #ifdef HAVE_cmpstrsi if (HAVE_cmpstrsi) { tree len, len1, len2; rtx arg1_rtx, arg2_rtx, arg3_rtx; rtx result, insn; tree fndecl; int arg1_align = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; int arg2_align = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; enum machine_mode insn_mode = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode; len1 = c_strlen (arg1, 1); len2 = c_strlen (arg2, 1); if (len1) len1 = size_binop (PLUS_EXPR, ssize_int (1), len1); if (len2) len2 = size_binop (PLUS_EXPR, ssize_int (1), len2); /* If we don't have a constant length for the first, use the length of the second, if we know it. We don't require a constant for this case; some cost analysis could be done if both are available but neither is constant. For now, assume they're equally cheap, unless one has side effects. If both strings have constant lengths, use the smaller. */ if (!len1) len = len2; else if (!len2) len = len1; else if (TREE_SIDE_EFFECTS (len1)) len = len2; else if (TREE_SIDE_EFFECTS (len2)) len = len1; else if (TREE_CODE (len1) != INTEGER_CST) len = len2; else if (TREE_CODE (len2) != INTEGER_CST) len = len1; else if (tree_int_cst_lt (len1, len2)) len = len1; else len = len2; /* If both arguments have side effects, we cannot optimize. */ if (!len || TREE_SIDE_EFFECTS (len)) return 0; /* The actual new length parameter is MIN(len,arg3). */ len = fold (build (MIN_EXPR, TREE_TYPE (len), len, arg3)); /* If we don't have POINTER_TYPE, call the function. */ if (arg1_align == 0 || arg2_align == 0) return 0; /* Make a place to write the result of the instruction. */ result = target; if (! (result != 0 && GET_CODE (result) == REG && GET_MODE (result) == insn_mode && REGNO (result) >= FIRST_PSEUDO_REGISTER)) result = gen_reg_rtx (insn_mode); /* Stabilize the arguments in case gen_cmpstrsi fails. */ arg1 = save_expr (arg1); arg2 = save_expr (arg2); len = save_expr (len); arg1_rtx = get_memory_rtx (arg1); arg2_rtx = get_memory_rtx (arg2); arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx, GEN_INT (MIN (arg1_align, arg2_align))); if (insn) { emit_insn (insn); /* Return the value in the proper mode for this function. */ mode = TYPE_MODE (TREE_TYPE (exp)); if (GET_MODE (result) == mode) return result; if (target == 0) return convert_to_mode (mode, result, 0); convert_move (target, result, 0); return target; } /* Expand the library call ourselves using a stabilized argument list to avoid re-evaluating the function's arguments twice. */ arglist = build_tree_list (NULL_TREE, len); arglist = tree_cons (NULL_TREE, arg2, arglist); arglist = tree_cons (NULL_TREE, arg1, arglist); fndecl = get_callee_fndecl (exp); exp = build_function_call_expr (fndecl, arglist); return expand_call (exp, target, target == const0_rtx); } #endif return 0; } /* Expand expression EXP, which is a call to the strcat builtin. Return 0 if we failed the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient. */ static rtx expand_builtin_strcat (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; else { tree dst = TREE_VALUE (arglist), src = TREE_VALUE (TREE_CHAIN (arglist)); const char *p = c_getstr (src); if (p) { /* If the string length is zero, return the dst parameter. */ if (*p == '\0') return expand_expr (dst, target, mode, EXPAND_NORMAL); else if (!optimize_size) { /* Otherwise if !optimize_size, see if we can store by pieces into (dst + strlen(dst)). */ tree newdst, arglist, strlen_fn = implicit_built_in_decls[BUILT_IN_STRLEN]; /* This is the length argument. */ arglist = build_tree_list (NULL_TREE, fold (size_binop (PLUS_EXPR, c_strlen (src, 0), ssize_int (1)))); /* Prepend src argument. */ arglist = tree_cons (NULL_TREE, src, arglist); /* We're going to use dst more than once. */ dst = save_expr (dst); /* Create strlen (dst). */ newdst = fold (build_function_call_expr (strlen_fn, build_tree_list (NULL_TREE, dst))); /* Create (dst + strlen (dst)). */ newdst = fold (build (PLUS_EXPR, TREE_TYPE (dst), dst, newdst)); /* Prepend the new dst argument. */ arglist = tree_cons (NULL_TREE, newdst, arglist); /* We don't want to get turned into a memcpy if the target is const0_rtx, i.e. when the return value isn't used. That would produce pessimized code so pass in a target of zero, it should never actually be used. If this was successful return the original dst, not the result of mempcpy. */ if (expand_builtin_mempcpy (arglist, /*target=*/0, mode, /*endp=*/0)) return expand_expr (dst, target, mode, EXPAND_NORMAL); else return 0; } } return 0; } } /* Expand expression EXP, which is a call to the strncat builtin. Return 0 if we failed the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient. */ static rtx expand_builtin_strncat (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; else { tree dst = TREE_VALUE (arglist), src = TREE_VALUE (TREE_CHAIN (arglist)), len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); const char *p = c_getstr (src); /* If the requested length is zero, or the src parameter string length is zero, return the dst parameter. */ if (integer_zerop (len) || (p && *p == '\0')) { /* Evaluate and ignore the src and len parameters in case they have side-effects. */ expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL); expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL); return expand_expr (dst, target, mode, EXPAND_NORMAL); } /* If the requested len is greater than or equal to the string length, call strcat. */ if (TREE_CODE (len) == INTEGER_CST && p && compare_tree_int (len, strlen (p)) >= 0) { tree newarglist = tree_cons (NULL_TREE, dst, build_tree_list (NULL_TREE, src)); tree fn = implicit_built_in_decls[BUILT_IN_STRCAT]; /* If the replacement _DECL isn't initialized, don't do the transformation. */ if (!fn) return 0; return expand_expr (build_function_call_expr (fn, newarglist), target, mode, EXPAND_NORMAL); } return 0; } } /* Expand expression EXP, which is a call to the strspn builtin. Return 0 if we failed the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient. */ static rtx expand_builtin_strspn (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; else { tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); const char *p1 = c_getstr (s1), *p2 = c_getstr (s2); /* If both arguments are constants, evaluate at compile-time. */ if (p1 && p2) { const size_t r = strspn (p1, p2); return expand_expr (size_int (r), target, mode, EXPAND_NORMAL); } /* If either argument is "", return 0. */ if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0')) { /* Evaluate and ignore both arguments in case either one has side-effects. */ expand_expr (s1, const0_rtx, VOIDmode, EXPAND_NORMAL); expand_expr (s2, const0_rtx, VOIDmode, EXPAND_NORMAL); return const0_rtx; } return 0; } } /* Expand expression EXP, which is a call to the strcspn builtin. Return 0 if we failed the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient. */ static rtx expand_builtin_strcspn (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; else { tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); const char *p1 = c_getstr (s1), *p2 = c_getstr (s2); /* If both arguments are constants, evaluate at compile-time. */ if (p1 && p2) { const size_t r = strcspn (p1, p2); return expand_expr (size_int (r), target, mode, EXPAND_NORMAL); } /* If the first argument is "", return 0. */ if (p1 && *p1 == '\0') { /* Evaluate and ignore argument s2 in case it has side-effects. */ expand_expr (s2, const0_rtx, VOIDmode, EXPAND_NORMAL); return const0_rtx; } /* If the second argument is "", return __builtin_strlen(s1). */ if (p2 && *p2 == '\0') { tree newarglist = build_tree_list (NULL_TREE, s1), fn = implicit_built_in_decls[BUILT_IN_STRLEN]; /* If the replacement _DECL isn't initialized, don't do the transformation. */ if (!fn) return 0; return expand_expr (build_function_call_expr (fn, newarglist), target, mode, EXPAND_NORMAL); } return 0; } } /* Expand a call to __builtin_saveregs, generating the result in TARGET, if that's convenient. */ rtx expand_builtin_saveregs (void) { rtx val, seq; /* Don't do __builtin_saveregs more than once in a function. Save the result of the first call and reuse it. */ if (saveregs_value != 0) return saveregs_value; /* When this function is called, it means that registers must be saved on entry to this function. So we migrate the call to the first insn of this function. */ start_sequence (); /* Do whatever the machine needs done in this case. */ val = targetm.calls.expand_builtin_saveregs (); seq = get_insns (); end_sequence (); saveregs_value = val; /* Put the insns after the NOTE that starts the function. If this is inside a start_sequence, make the outer-level insn chain current, so the code is placed at the start of the function. */ push_topmost_sequence (); emit_insn_after (seq, get_insns ()); pop_topmost_sequence (); return val; } /* __builtin_args_info (N) returns word N of the arg space info for the current function. The number and meanings of words is controlled by the definition of CUMULATIVE_ARGS. */ static rtx expand_builtin_args_info (tree arglist) { int nwords = sizeof (CUMULATIVE_ARGS) / sizeof (int); int *word_ptr = (int *) ¤t_function_args_info; if (sizeof (CUMULATIVE_ARGS) % sizeof (int) != 0) abort (); if (arglist != 0) { if (!host_integerp (TREE_VALUE (arglist), 0)) error ("argument of `__builtin_args_info' must be constant"); else { HOST_WIDE_INT wordnum = tree_low_cst (TREE_VALUE (arglist), 0); if (wordnum < 0 || wordnum >= nwords) error ("argument of `__builtin_args_info' out of range"); else return GEN_INT (word_ptr[wordnum]); } } else error ("missing argument in `__builtin_args_info'"); return const0_rtx; } /* Expand ARGLIST, from a call to __builtin_next_arg. */ static rtx expand_builtin_next_arg (tree arglist) { tree fntype = TREE_TYPE (current_function_decl); if (TYPE_ARG_TYPES (fntype) == 0 || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) == void_type_node)) { error ("`va_start' used in function with fixed args"); return const0_rtx; } if (arglist) { tree last_parm = tree_last (DECL_ARGUMENTS (current_function_decl)); tree arg = TREE_VALUE (arglist); /* Strip off all nops for the sake of the comparison. This is not quite the same as STRIP_NOPS. It does more. We must also strip off INDIRECT_EXPR for C++ reference parameters. */ while (TREE_CODE (arg) == NOP_EXPR || TREE_CODE (arg) == CONVERT_EXPR || TREE_CODE (arg) == NON_LVALUE_EXPR || TREE_CODE (arg) == INDIRECT_REF) arg = TREE_OPERAND (arg, 0); if (arg != last_parm) warning ("second parameter of `va_start' not last named argument"); } else /* Evidently an out of date version of ; can't validate va_start's second argument, but can still work as intended. */ warning ("`__builtin_next_arg' called without an argument"); return expand_binop (Pmode, add_optab, current_function_internal_arg_pointer, current_function_arg_offset_rtx, NULL_RTX, 0, OPTAB_LIB_WIDEN); } /* Make it easier for the backends by protecting the valist argument from multiple evaluations. */ static tree stabilize_va_list (tree valist, int needs_lvalue) { if (TREE_CODE (va_list_type_node) == ARRAY_TYPE) { if (TREE_SIDE_EFFECTS (valist)) valist = save_expr (valist); /* For this case, the backends will be expecting a pointer to TREE_TYPE (va_list_type_node), but it's possible we've actually been given an array (an actual va_list_type_node). So fix it. */ if (TREE_CODE (TREE_TYPE (valist)) == ARRAY_TYPE) { tree p1 = build_pointer_type (TREE_TYPE (va_list_type_node)); tree p2 = build_pointer_type (va_list_type_node); valist = build1 (ADDR_EXPR, p2, valist); valist = fold (build1 (NOP_EXPR, p1, valist)); } } else { tree pt; if (! needs_lvalue) { if (! TREE_SIDE_EFFECTS (valist)) return valist; pt = build_pointer_type (va_list_type_node); valist = fold (build1 (ADDR_EXPR, pt, valist)); TREE_SIDE_EFFECTS (valist) = 1; } if (TREE_SIDE_EFFECTS (valist)) valist = save_expr (valist); valist = fold (build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist)); } return valist; } /* The "standard" definition of va_list is void*. */ tree std_build_builtin_va_list (void) { return ptr_type_node; } /* The "standard" implementation of va_start: just assign `nextarg' to the variable. */ void std_expand_builtin_va_start (tree valist, rtx nextarg) { tree t; t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, make_tree (ptr_type_node, nextarg)); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); } /* Expand ARGLIST, from a call to __builtin_va_start. */ static rtx expand_builtin_va_start (tree arglist) { rtx nextarg; tree chain, valist; chain = TREE_CHAIN (arglist); if (TREE_CHAIN (chain)) error ("too many arguments to function `va_start'"); nextarg = expand_builtin_next_arg (chain); valist = stabilize_va_list (TREE_VALUE (arglist), 1); #ifdef EXPAND_BUILTIN_VA_START EXPAND_BUILTIN_VA_START (valist, nextarg); #else std_expand_builtin_va_start (valist, nextarg); #endif return const0_rtx; } /* The "standard" implementation of va_arg: read the value from the current (padded) address and increment by the (padded) size. */ rtx std_expand_builtin_va_arg (tree valist, tree type) { tree addr_tree, t, type_size = NULL; tree align, alignm1; tree rounded_size; rtx addr; HOST_WIDE_INT boundary; /* Compute the rounded size of the type. */ align = size_int (PARM_BOUNDARY / BITS_PER_UNIT); alignm1 = size_int (PARM_BOUNDARY / BITS_PER_UNIT - 1); boundary = FUNCTION_ARG_BOUNDARY (TYPE_MODE (type), type); /* va_list pointer is aligned to PARM_BOUNDARY. If argument actually requires greater alignment, we must perform dynamic alignment. */ if (boundary > PARM_BOUNDARY) { if (!PAD_VARARGS_DOWN) { t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, build (PLUS_EXPR, TREE_TYPE (valist), valist, build_int_2 (boundary / BITS_PER_UNIT - 1, 0))); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); } t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, build (BIT_AND_EXPR, TREE_TYPE (valist), valist, build_int_2 (~(boundary / BITS_PER_UNIT - 1), -1))); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); } if (type == error_mark_node || (type_size = TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type))) == NULL || TREE_OVERFLOW (type_size)) rounded_size = size_zero_node; else rounded_size = fold (build (MULT_EXPR, sizetype, fold (build (TRUNC_DIV_EXPR, sizetype, fold (build (PLUS_EXPR, sizetype, type_size, alignm1)), align)), align)); /* Get AP. */ addr_tree = valist; if (PAD_VARARGS_DOWN && ! integer_zerop (rounded_size)) { /* Small args are padded downward. */ addr_tree = fold (build (PLUS_EXPR, TREE_TYPE (addr_tree), addr_tree, fold (build (COND_EXPR, sizetype, fold (build (GT_EXPR, sizetype, rounded_size, align)), size_zero_node, fold (build (MINUS_EXPR, sizetype, rounded_size, type_size)))))); } addr = expand_expr (addr_tree, NULL_RTX, Pmode, EXPAND_NORMAL); addr = copy_to_reg (addr); /* Compute new value for AP. */ if (! integer_zerop (rounded_size)) { t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, build (PLUS_EXPR, TREE_TYPE (valist), valist, rounded_size)); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); } return addr; } /* Expand __builtin_va_arg, which is not really a builtin function, but a very special sort of operator. */ rtx expand_builtin_va_arg (tree valist, tree type) { rtx addr, result; tree promoted_type, want_va_type, have_va_type; /* Verify that valist is of the proper type. */ want_va_type = va_list_type_node; have_va_type = TREE_TYPE (valist); if (TREE_CODE (want_va_type) == ARRAY_TYPE) { /* If va_list is an array type, the argument may have decayed to a pointer type, e.g. by being passed to another function. In that case, unwrap both types so that we can compare the underlying records. */ if (TREE_CODE (have_va_type) == ARRAY_TYPE || TREE_CODE (have_va_type) == POINTER_TYPE) { want_va_type = TREE_TYPE (want_va_type); have_va_type = TREE_TYPE (have_va_type); } } if (TYPE_MAIN_VARIANT (want_va_type) != TYPE_MAIN_VARIANT (have_va_type)) { error ("first argument to `va_arg' not of type `va_list'"); addr = const0_rtx; } /* Generate a diagnostic for requesting data of a type that cannot be passed through `...' due to type promotion at the call site. */ else if ((promoted_type = (*lang_hooks.types.type_promotes_to) (type)) != type) { const char *name = "", *pname = 0; static bool gave_help; if (TYPE_NAME (type)) { if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) name = IDENTIFIER_POINTER (TYPE_NAME (type)); else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL && DECL_NAME (TYPE_NAME (type))) name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); } if (TYPE_NAME (promoted_type)) { if (TREE_CODE (TYPE_NAME (promoted_type)) == IDENTIFIER_NODE) pname = IDENTIFIER_POINTER (TYPE_NAME (promoted_type)); else if (TREE_CODE (TYPE_NAME (promoted_type)) == TYPE_DECL && DECL_NAME (TYPE_NAME (promoted_type))) pname = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (promoted_type))); } /* Unfortunately, this is merely undefined, rather than a constraint violation, so we cannot make this an error. If this call is never executed, the program is still strictly conforming. */ warning ("`%s' is promoted to `%s' when passed through `...'", name, pname); if (! gave_help) { gave_help = true; warning ("(so you should pass `%s' not `%s' to `va_arg')", pname, name); } /* We can, however, treat "undefined" any way we please. Call abort to encourage the user to fix the program. */ inform ("if this code is reached, the program will abort"); expand_builtin_trap (); /* This is dead code, but go ahead and finish so that the mode of the result comes out right. */ addr = const0_rtx; } else { /* Make it easier for the backends by protecting the valist argument from multiple evaluations. */ valist = stabilize_va_list (valist, 0); #ifdef EXPAND_BUILTIN_VA_ARG addr = EXPAND_BUILTIN_VA_ARG (valist, type); #else addr = std_expand_builtin_va_arg (valist, type); #endif } addr = convert_memory_address (Pmode, addr); result = gen_rtx_MEM (TYPE_MODE (type), addr); set_mem_alias_set (result, get_varargs_alias_set ()); return result; } /* Expand ARGLIST, from a call to __builtin_va_end. */ static rtx expand_builtin_va_end (tree arglist) { tree valist = TREE_VALUE (arglist); /* Evaluate for side effects, if needed. I hate macros that don't do that. */ if (TREE_SIDE_EFFECTS (valist)) expand_expr (valist, const0_rtx, VOIDmode, EXPAND_NORMAL); return const0_rtx; } /* Expand ARGLIST, from a call to __builtin_va_copy. We do this as a builtin rather than just as an assignment in stdarg.h because of the nastiness of array-type va_list types. */ static rtx expand_builtin_va_copy (tree arglist) { tree dst, src, t; dst = TREE_VALUE (arglist); src = TREE_VALUE (TREE_CHAIN (arglist)); dst = stabilize_va_list (dst, 1); src = stabilize_va_list (src, 0); if (TREE_CODE (va_list_type_node) != ARRAY_TYPE) { t = build (MODIFY_EXPR, va_list_type_node, dst, src); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); } else { rtx dstb, srcb, size; /* Evaluate to pointers. */ dstb = expand_expr (dst, NULL_RTX, Pmode, EXPAND_NORMAL); srcb = expand_expr (src, NULL_RTX, Pmode, EXPAND_NORMAL); size = expand_expr (TYPE_SIZE_UNIT (va_list_type_node), NULL_RTX, VOIDmode, EXPAND_NORMAL); dstb = convert_memory_address (Pmode, dstb); srcb = convert_memory_address (Pmode, srcb); /* "Dereference" to BLKmode memories. */ dstb = gen_rtx_MEM (BLKmode, dstb); set_mem_alias_set (dstb, get_alias_set (TREE_TYPE (TREE_TYPE (dst)))); set_mem_align (dstb, TYPE_ALIGN (va_list_type_node)); srcb = gen_rtx_MEM (BLKmode, srcb); set_mem_alias_set (srcb, get_alias_set (TREE_TYPE (TREE_TYPE (src)))); set_mem_align (srcb, TYPE_ALIGN (va_list_type_node)); /* Copy. */ emit_block_move (dstb, srcb, size, BLOCK_OP_NORMAL); } return const0_rtx; } /* Expand a call to one of the builtin functions __builtin_frame_address or __builtin_return_address. */ static rtx expand_builtin_frame_address (tree fndecl, tree arglist) { /* The argument must be a nonnegative integer constant. It counts the number of frames to scan up the stack. The value is the return address saved in that frame. */ if (arglist == 0) /* Warning about missing arg was already issued. */ return const0_rtx; else if (! host_integerp (TREE_VALUE (arglist), 1)) { if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS) error ("invalid arg to `__builtin_frame_address'"); else error ("invalid arg to `__builtin_return_address'"); return const0_rtx; } else { rtx tem = expand_builtin_return_addr (DECL_FUNCTION_CODE (fndecl), tree_low_cst (TREE_VALUE (arglist), 1), hard_frame_pointer_rtx); /* Some ports cannot access arbitrary stack frames. */ if (tem == NULL) { if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS) warning ("unsupported arg to `__builtin_frame_address'"); else warning ("unsupported arg to `__builtin_return_address'"); return const0_rtx; } /* For __builtin_frame_address, return what we've got. */ if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS) return tem; if (GET_CODE (tem) != REG && ! CONSTANT_P (tem)) tem = copy_to_mode_reg (Pmode, tem); return tem; } } /* Expand a call to the alloca builtin, with arguments ARGLIST. Return 0 if we failed and the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient. */ static rtx expand_builtin_alloca (tree arglist, rtx target) { rtx op0; rtx result; if (!validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE)) return 0; /* Compute the argument. */ op0 = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0); /* Allocate the desired space. */ result = allocate_dynamic_stack_space (op0, target, BITS_PER_UNIT); result = convert_memory_address (ptr_mode, result); return result; } /* Expand a call to a unary builtin. The arguments are in ARGLIST. Return 0 if a normal call should be emitted rather than expanding the function in-line. If convenient, the result should be placed in TARGET. SUBTARGET may be used as the target for computing one of EXP's operands. */ static rtx expand_builtin_unop (enum machine_mode target_mode, tree arglist, rtx target, rtx subtarget, optab op_optab) { rtx op0; if (!validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE)) return 0; /* Compute the argument. */ op0 = expand_expr (TREE_VALUE (arglist), subtarget, VOIDmode, 0); /* Compute op, into TARGET if possible. Set TARGET to wherever the result comes back. */ target = expand_unop (TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist))), op_optab, op0, target, 1); if (target == 0) abort (); return convert_to_mode (target_mode, target, 0); } /* If the string passed to fputs is a constant and is one character long, we attempt to transform this call into __builtin_fputc(). */ static rtx expand_builtin_fputs (tree arglist, rtx target, bool unlocked) { tree len, fn; - tree fn_fputc = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED] + /* If we're using an unlocked function, assume the other unlocked + functions exist explicitly. */ + tree const fn_fputc = unlocked ? built_in_decls[BUILT_IN_FPUTC_UNLOCKED] : implicit_built_in_decls[BUILT_IN_FPUTC]; - tree fn_fwrite = unlocked ? implicit_built_in_decls[BUILT_IN_FWRITE_UNLOCKED] + tree const fn_fwrite = unlocked ? built_in_decls[BUILT_IN_FWRITE_UNLOCKED] : implicit_built_in_decls[BUILT_IN_FWRITE]; - /* If the return value is used, or the replacement _DECL isn't - initialized, don't do the transformation. */ - if (target != const0_rtx || !fn_fputc || !fn_fwrite) + /* If the return value is used, don't do the transformation. */ + if (target != const0_rtx) return 0; /* Verify the arguments in the original call. */ if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; /* Get the length of the string passed to fputs. If the length can't be determined, punt. */ if (!(len = c_strlen (TREE_VALUE (arglist), 1)) || TREE_CODE (len) != INTEGER_CST) return 0; switch (compare_tree_int (len, 1)) { case -1: /* length is 0, delete the call entirely . */ { /* Evaluate and ignore the argument in case it has side-effects. */ expand_expr (TREE_VALUE (TREE_CHAIN (arglist)), const0_rtx, VOIDmode, EXPAND_NORMAL); return const0_rtx; } case 0: /* length is 1, call fputc. */ { const char *p = c_getstr (TREE_VALUE (arglist)); if (p != NULL) { /* New argument list transforming fputs(string, stream) to fputc(string[0], stream). */ arglist = build_tree_list (NULL_TREE, TREE_VALUE (TREE_CHAIN (arglist))); arglist = tree_cons (NULL_TREE, build_int_2 (p[0], 0), arglist); fn = fn_fputc; break; } } /* Fall through. */ case 1: /* length is greater than 1, call fwrite. */ { tree string_arg; /* If optimizing for size keep fputs. */ if (optimize_size) return 0; string_arg = TREE_VALUE (arglist); /* New argument list transforming fputs(string, stream) to fwrite(string, 1, len, stream). */ arglist = build_tree_list (NULL_TREE, TREE_VALUE (TREE_CHAIN (arglist))); arglist = tree_cons (NULL_TREE, len, arglist); arglist = tree_cons (NULL_TREE, size_one_node, arglist); arglist = tree_cons (NULL_TREE, string_arg, arglist); fn = fn_fwrite; break; } default: abort (); } + /* If the replacement _DECL isn't initialized, don't do the + transformation. */ + if (!fn) + return 0; + return expand_expr (build_function_call_expr (fn, arglist), const0_rtx, VOIDmode, EXPAND_NORMAL); } /* Expand a call to __builtin_expect. We return our argument and emit a NOTE_INSN_EXPECTED_VALUE note. This is the expansion of __builtin_expect in a non-jump context. */ static rtx expand_builtin_expect (tree arglist, rtx target) { tree exp, c; rtx note, rtx_c; if (arglist == NULL_TREE || TREE_CHAIN (arglist) == NULL_TREE) return const0_rtx; exp = TREE_VALUE (arglist); c = TREE_VALUE (TREE_CHAIN (arglist)); if (TREE_CODE (c) != INTEGER_CST) { error ("second arg to `__builtin_expect' must be a constant"); c = integer_zero_node; } target = expand_expr (exp, target, VOIDmode, EXPAND_NORMAL); /* Don't bother with expected value notes for integral constants. */ if (flag_guess_branch_prob && GET_CODE (target) != CONST_INT) { /* We do need to force this into a register so that we can be moderately sure to be able to correctly interpret the branch condition later. */ target = force_reg (GET_MODE (target), target); rtx_c = expand_expr (c, NULL_RTX, GET_MODE (target), EXPAND_NORMAL); note = emit_note (NOTE_INSN_EXPECTED_VALUE); NOTE_EXPECTED_VALUE (note) = gen_rtx_EQ (VOIDmode, target, rtx_c); } return target; } /* Like expand_builtin_expect, except do this in a jump context. This is called from do_jump if the conditional is a __builtin_expect. Return either a list of insns to emit the jump or NULL if we cannot optimize __builtin_expect. We need to optimize this at jump time so that machines like the PowerPC don't turn the test into a SCC operation, and then jump based on the test being 0/1. */ rtx expand_builtin_expect_jump (tree exp, rtx if_false_label, rtx if_true_label) { tree arglist = TREE_OPERAND (exp, 1); tree arg0 = TREE_VALUE (arglist); tree arg1 = TREE_VALUE (TREE_CHAIN (arglist)); rtx ret = NULL_RTX; /* Only handle __builtin_expect (test, 0) and __builtin_expect (test, 1). */ if (TREE_CODE (TREE_TYPE (arg1)) == INTEGER_TYPE && (integer_zerop (arg1) || integer_onep (arg1))) { rtx insn, drop_through_label, temp; /* Expand the jump insns. */ start_sequence (); do_jump (arg0, if_false_label, if_true_label); ret = get_insns (); drop_through_label = get_last_insn (); if (drop_through_label && GET_CODE (drop_through_label) == NOTE) drop_through_label = prev_nonnote_insn (drop_through_label); if (drop_through_label && GET_CODE (drop_through_label) != CODE_LABEL) drop_through_label = NULL_RTX; end_sequence (); if (! if_true_label) if_true_label = drop_through_label; if (! if_false_label) if_false_label = drop_through_label; /* Go through and add the expect's to each of the conditional jumps. */ insn = ret; while (insn != NULL_RTX) { rtx next = NEXT_INSN (insn); if (GET_CODE (insn) == JUMP_INSN && any_condjump_p (insn)) { rtx ifelse = SET_SRC (pc_set (insn)); rtx then_dest = XEXP (ifelse, 1); rtx else_dest = XEXP (ifelse, 2); int taken = -1; /* First check if we recognize any of the labels. */ if (GET_CODE (then_dest) == LABEL_REF && XEXP (then_dest, 0) == if_true_label) taken = 1; else if (GET_CODE (then_dest) == LABEL_REF && XEXP (then_dest, 0) == if_false_label) taken = 0; else if (GET_CODE (else_dest) == LABEL_REF && XEXP (else_dest, 0) == if_false_label) taken = 1; else if (GET_CODE (else_dest) == LABEL_REF && XEXP (else_dest, 0) == if_true_label) taken = 0; /* Otherwise check where we drop through. */ else if (else_dest == pc_rtx) { if (next && GET_CODE (next) == NOTE) next = next_nonnote_insn (next); if (next && GET_CODE (next) == JUMP_INSN && any_uncondjump_p (next)) temp = XEXP (SET_SRC (pc_set (next)), 0); else temp = next; /* TEMP is either a CODE_LABEL, NULL_RTX or something else that can't possibly match either target label. */ if (temp == if_false_label) taken = 1; else if (temp == if_true_label) taken = 0; } else if (then_dest == pc_rtx) { if (next && GET_CODE (next) == NOTE) next = next_nonnote_insn (next); if (next && GET_CODE (next) == JUMP_INSN && any_uncondjump_p (next)) temp = XEXP (SET_SRC (pc_set (next)), 0); else temp = next; if (temp == if_false_label) taken = 0; else if (temp == if_true_label) taken = 1; } if (taken != -1) { /* If the test is expected to fail, reverse the probabilities. */ if (integer_zerop (arg1)) taken = 1 - taken; predict_insn_def (insn, PRED_BUILTIN_EXPECT, taken); } } insn = next; } } return ret; } void expand_builtin_trap (void) { #ifdef HAVE_trap if (HAVE_trap) emit_insn (gen_trap ()); else #endif emit_library_call (abort_libfunc, LCT_NORETURN, VOIDmode, 0); emit_barrier (); } /* Expand a call to fabs, fabsf or fabsl with arguments ARGLIST. Return 0 if a normal call should be emitted rather than expanding the function inline. If convenient, the result should be placed in TARGET. SUBTARGET may be used as the target for computing the operand. */ static rtx expand_builtin_fabs (tree arglist, rtx target, rtx subtarget) { enum machine_mode mode; tree arg; rtx op0; if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) return 0; arg = TREE_VALUE (arglist); mode = TYPE_MODE (TREE_TYPE (arg)); op0 = expand_expr (arg, subtarget, VOIDmode, 0); return expand_abs (mode, op0, target, 0, safe_from_p (target, arg, 1)); } /* Expand a call to cabs, cabsf or cabsl with arguments ARGLIST. Return 0 if a normal call should be emitted rather than expanding the function inline. If convenient, the result should be placed in target. */ static rtx expand_builtin_cabs (tree arglist, rtx target) { enum machine_mode mode; tree arg; rtx op0; if (arglist == 0 || TREE_CHAIN (arglist)) return 0; arg = TREE_VALUE (arglist); if (TREE_CODE (TREE_TYPE (arg)) != COMPLEX_TYPE || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) != REAL_TYPE) return 0; mode = TYPE_MODE (TREE_TYPE (arg)); op0 = expand_expr (arg, NULL_RTX, VOIDmode, 0); return expand_complex_abs (mode, op0, target, 0); } /* Create a new constant string literal and return a char* pointer to it. The STRING_CST value is the LEN characters at STR. */ static tree build_string_literal (int len, const char *str) { tree t, elem, index, type; t = build_string (len, str); elem = build_type_variant (char_type_node, 1, 0); index = build_index_type (build_int_2 (len - 1, 0)); type = build_array_type (elem, index); TREE_TYPE (t) = type; TREE_CONSTANT (t) = 1; TREE_READONLY (t) = 1; TREE_STATIC (t) = 1; type = build_pointer_type (type); t = build1 (ADDR_EXPR, type, t); type = build_pointer_type (elem); t = build1 (NOP_EXPR, type, t); return t; } /* Expand a call to printf or printf_unlocked with argument list ARGLIST. Return 0 if a normal call should be emitted rather than transforming the function inline. If convenient, the result should be placed in TARGET with mode MODE. UNLOCKED indicates this is a printf_unlocked call. */ static rtx expand_builtin_printf (tree arglist, rtx target, enum machine_mode mode, bool unlocked) { - tree fn_putchar = unlocked - ? implicit_built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED] - : implicit_built_in_decls[BUILT_IN_PUTCHAR]; - tree fn_puts = unlocked ? implicit_built_in_decls[BUILT_IN_PUTS_UNLOCKED] - : implicit_built_in_decls[BUILT_IN_PUTS]; + /* If we're using an unlocked function, assume the other unlocked + functions exist explicitly. */ + tree const fn_putchar = unlocked ? built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED] + : implicit_built_in_decls[BUILT_IN_PUTCHAR]; + tree const fn_puts = unlocked ? built_in_decls[BUILT_IN_PUTS_UNLOCKED] + : implicit_built_in_decls[BUILT_IN_PUTS]; const char *fmt_str; tree fn, fmt, arg; /* If the return value is used, don't do the transformation. */ if (target != const0_rtx) return 0; /* Verify the required arguments in the original call. */ if (! arglist) return 0; fmt = TREE_VALUE (arglist); if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE) return 0; arglist = TREE_CHAIN (arglist); /* Check whether the format is a literal string constant. */ fmt_str = c_getstr (fmt); if (fmt_str == NULL) return 0; /* If the format specifier was "%s\n", call __builtin_puts(arg). */ if (strcmp (fmt_str, "%s\n") == 0) { if (! arglist || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE || TREE_CHAIN (arglist)) return 0; fn = fn_puts; } /* If the format specifier was "%c", call __builtin_putchar(arg). */ else if (strcmp (fmt_str, "%c") == 0) { if (! arglist || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE || TREE_CHAIN (arglist)) return 0; fn = fn_putchar; } else { /* We can't handle anything else with % args or %% ... yet. */ if (strchr (fmt_str, '%')) return 0; if (arglist) return 0; /* If the format specifier was "", printf does nothing. */ if (fmt_str[0] == '\0') return const0_rtx; /* If the format specifier has length of 1, call putchar. */ if (fmt_str[1] == '\0') { /* Given printf("c"), (where c is any one character,) convert "c"[0] to an int and pass that to the replacement function. */ arg = build_int_2 (fmt_str[0], 0); arglist = build_tree_list (NULL_TREE, arg); fn = fn_putchar; } else { /* If the format specifier was "string\n", call puts("string"). */ size_t len = strlen (fmt_str); if (fmt_str[len - 1] == '\n') { /* Create a NUL-terminated string that's one char shorter than the original, stripping off the trailing '\n'. */ char *newstr = (char *) alloca (len); memcpy (newstr, fmt_str, len - 1); newstr[len - 1] = 0; arg = build_string_literal (len, newstr); arglist = build_tree_list (NULL_TREE, arg); fn = fn_puts; } else /* We'd like to arrange to call fputs(string,stdout) here, but we need stdout and don't have a way to get it yet. */ return 0; } } if (!fn) return 0; return expand_expr (build_function_call_expr (fn, arglist), target, mode, EXPAND_NORMAL); } /* Expand a call to fprintf or fprintf_unlocked with argument list ARGLIST. Return 0 if a normal call should be emitted rather than transforming the function inline. If convenient, the result should be placed in TARGET with mode MODE. UNLOCKED indicates this is a fprintf_unlocked call. */ static rtx expand_builtin_fprintf (tree arglist, rtx target, enum machine_mode mode, bool unlocked) { - tree fn_fputc = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED] - : implicit_built_in_decls[BUILT_IN_FPUTC]; - tree fn_fputs = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTS_UNLOCKED] - : implicit_built_in_decls[BUILT_IN_FPUTS]; + /* If we're using an unlocked function, assume the other unlocked + functions exist explicitly. */ + tree const fn_fputc = unlocked ? built_in_decls[BUILT_IN_FPUTC_UNLOCKED] + : implicit_built_in_decls[BUILT_IN_FPUTC]; + tree const fn_fputs = unlocked ? built_in_decls[BUILT_IN_FPUTS_UNLOCKED] + : implicit_built_in_decls[BUILT_IN_FPUTS]; const char *fmt_str; tree fn, fmt, fp, arg; /* If the return value is used, don't do the transformation. */ if (target != const0_rtx) return 0; /* Verify the required arguments in the original call. */ if (! arglist) return 0; fp = TREE_VALUE (arglist); if (TREE_CODE (TREE_TYPE (fp)) != POINTER_TYPE) return 0; arglist = TREE_CHAIN (arglist); if (! arglist) return 0; fmt = TREE_VALUE (arglist); if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE) return 0; arglist = TREE_CHAIN (arglist); /* Check whether the format is a literal string constant. */ fmt_str = c_getstr (fmt); if (fmt_str == NULL) return 0; /* If the format specifier was "%s", call __builtin_fputs(arg,fp). */ if (strcmp (fmt_str, "%s") == 0) { if (! arglist || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE || TREE_CHAIN (arglist)) return 0; arg = TREE_VALUE (arglist); arglist = build_tree_list (NULL_TREE, fp); arglist = tree_cons (NULL_TREE, arg, arglist); fn = fn_fputs; } /* If the format specifier was "%c", call __builtin_fputc(arg,fp). */ else if (strcmp (fmt_str, "%c") == 0) { if (! arglist || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE || TREE_CHAIN (arglist)) return 0; arg = TREE_VALUE (arglist); arglist = build_tree_list (NULL_TREE, fp); arglist = tree_cons (NULL_TREE, arg, arglist); fn = fn_fputc; } else { /* We can't handle anything else with % args or %% ... yet. */ if (strchr (fmt_str, '%')) return 0; if (arglist) return 0; /* If the format specifier was "", fprintf does nothing. */ if (fmt_str[0] == '\0') { /* Evaluate and ignore FILE* argument for side-effects. */ expand_expr (fp, const0_rtx, VOIDmode, EXPAND_NORMAL); return const0_rtx; } /* When "string" doesn't contain %, replace all cases of fprintf(stream,string) with fputs(string,stream). The fputs builtin will take care of special cases like length == 1. */ arglist = build_tree_list (NULL_TREE, fp); arglist = tree_cons (NULL_TREE, fmt, arglist); fn = fn_fputs; } if (!fn) return 0; return expand_expr (build_function_call_expr (fn, arglist), target, mode, EXPAND_NORMAL); } /* Expand a call to sprintf with argument list ARGLIST. Return 0 if a normal call should be emitted rather than expanding the function inline. If convenient, the result should be placed in TARGET with mode MODE. */ static rtx expand_builtin_sprintf (tree arglist, rtx target, enum machine_mode mode) { tree orig_arglist, dest, fmt; const char *fmt_str; orig_arglist = arglist; /* Verify the required arguments in the original call. */ if (! arglist) return 0; dest = TREE_VALUE (arglist); if (TREE_CODE (TREE_TYPE (dest)) != POINTER_TYPE) return 0; arglist = TREE_CHAIN (arglist); if (! arglist) return 0; fmt = TREE_VALUE (arglist); if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE) return 0; arglist = TREE_CHAIN (arglist); /* Check whether the format is a literal string constant. */ fmt_str = c_getstr (fmt); if (fmt_str == NULL) return 0; /* If the format doesn't contain % args or %%, use strcpy. */ if (strchr (fmt_str, '%') == 0) { tree fn = implicit_built_in_decls[BUILT_IN_STRCPY]; tree exp; if (arglist || ! fn) return 0; expand_expr (build_function_call_expr (fn, orig_arglist), const0_rtx, VOIDmode, EXPAND_NORMAL); if (target == const0_rtx) return const0_rtx; exp = build_int_2 (strlen (fmt_str), 0); exp = fold (build1 (NOP_EXPR, integer_type_node, exp)); return expand_expr (exp, target, mode, EXPAND_NORMAL); } /* If the format is "%s", use strcpy if the result isn't used. */ else if (strcmp (fmt_str, "%s") == 0) { tree fn, arg, len; fn = implicit_built_in_decls[BUILT_IN_STRCPY]; if (! fn) return 0; if (! arglist || TREE_CHAIN (arglist)) return 0; arg = TREE_VALUE (arglist); if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE) return 0; if (target != const0_rtx) { len = c_strlen (arg, 1); if (! len || TREE_CODE (len) != INTEGER_CST) return 0; } else len = NULL_TREE; arglist = build_tree_list (NULL_TREE, arg); arglist = tree_cons (NULL_TREE, dest, arglist); expand_expr (build_function_call_expr (fn, arglist), const0_rtx, VOIDmode, EXPAND_NORMAL); if (target == const0_rtx) return const0_rtx; return expand_expr (len, target, mode, EXPAND_NORMAL); } return 0; } /* Expand an expression EXP that calls a built-in function, with result going to TARGET if that's convenient (and in mode MODE if that's convenient). SUBTARGET may be used as the target for computing one of EXP's operands. IGNORE is nonzero if the value is to be ignored. */ rtx expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, int ignore) { tree fndecl = get_callee_fndecl (exp); tree arglist = TREE_OPERAND (exp, 1); enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); enum machine_mode target_mode = TYPE_MODE (TREE_TYPE (exp)); /* Perform postincrements before expanding builtin functions. */ emit_queue (); if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) return (*targetm.expand_builtin) (exp, target, subtarget, mode, ignore); /* When not optimizing, generate calls to library functions for a certain set of builtins. */ if (!optimize && !CALLED_AS_BUILT_IN (fndecl) && DECL_ASSEMBLER_NAME_SET_P (fndecl) && fcode != BUILT_IN_ALLOCA) return expand_call (exp, target, ignore); /* The built-in function expanders test for target == const0_rtx to determine whether the function's result will be ignored. */ if (ignore) target = const0_rtx; /* If the result of a pure or const built-in function is ignored, and none of its arguments are volatile, we can avoid expanding the built-in call and just evaluate the arguments for side-effects. */ if (target == const0_rtx && (DECL_IS_PURE (fndecl) || TREE_READONLY (fndecl))) { bool volatilep = false; tree arg; for (arg = arglist; arg; arg = TREE_CHAIN (arg)) if (TREE_THIS_VOLATILE (TREE_VALUE (arg))) { volatilep = true; break; } if (! volatilep) { for (arg = arglist; arg; arg = TREE_CHAIN (arg)) expand_expr (TREE_VALUE (arg), const0_rtx, VOIDmode, EXPAND_NORMAL); return const0_rtx; } } switch (fcode) { case BUILT_IN_ABS: case BUILT_IN_LABS: case BUILT_IN_LLABS: case BUILT_IN_IMAXABS: /* build_function_call changes these into ABS_EXPR. */ abort (); case BUILT_IN_FABS: case BUILT_IN_FABSF: case BUILT_IN_FABSL: target = expand_builtin_fabs (arglist, target, subtarget); if (target) return target; break; case BUILT_IN_CABS: case BUILT_IN_CABSF: case BUILT_IN_CABSL: if (flag_unsafe_math_optimizations) { target = expand_builtin_cabs (arglist, target); if (target) return target; } break; case BUILT_IN_CONJ: case BUILT_IN_CONJF: case BUILT_IN_CONJL: case BUILT_IN_CREAL: case BUILT_IN_CREALF: case BUILT_IN_CREALL: case BUILT_IN_CIMAG: case BUILT_IN_CIMAGF: case BUILT_IN_CIMAGL: /* expand_tree_builtin changes these into CONJ_EXPR, REALPART_EXPR and IMAGPART_EXPR. */ abort (); case BUILT_IN_SIN: case BUILT_IN_SINF: case BUILT_IN_SINL: case BUILT_IN_COS: case BUILT_IN_COSF: case BUILT_IN_COSL: case BUILT_IN_EXP: case BUILT_IN_EXPF: case BUILT_IN_EXPL: case BUILT_IN_LOG: case BUILT_IN_LOGF: case BUILT_IN_LOGL: case BUILT_IN_TAN: case BUILT_IN_TANF: case BUILT_IN_TANL: case BUILT_IN_ATAN: case BUILT_IN_ATANF: case BUILT_IN_ATANL: /* Treat these like sqrt only if unsafe math optimizations are allowed, because of possible accuracy problems. */ if (! flag_unsafe_math_optimizations) break; case BUILT_IN_SQRT: case BUILT_IN_SQRTF: case BUILT_IN_SQRTL: case BUILT_IN_FLOOR: case BUILT_IN_FLOORF: case BUILT_IN_FLOORL: case BUILT_IN_CEIL: case BUILT_IN_CEILF: case BUILT_IN_CEILL: case BUILT_IN_TRUNC: case BUILT_IN_TRUNCF: case BUILT_IN_TRUNCL: case BUILT_IN_ROUND: case BUILT_IN_ROUNDF: case BUILT_IN_ROUNDL: case BUILT_IN_NEARBYINT: case BUILT_IN_NEARBYINTF: case BUILT_IN_NEARBYINTL: target = expand_builtin_mathfn (exp, target, subtarget); if (target) return target; break; case BUILT_IN_POW: case BUILT_IN_POWF: case BUILT_IN_POWL: if (! flag_unsafe_math_optimizations) break; target = expand_builtin_pow (exp, target, subtarget); if (target) return target; break; case BUILT_IN_ATAN2: case BUILT_IN_ATAN2F: case BUILT_IN_ATAN2L: if (! flag_unsafe_math_optimizations) break; target = expand_builtin_mathfn_2 (exp, target, subtarget); if (target) return target; break; case BUILT_IN_APPLY_ARGS: return expand_builtin_apply_args (); /* __builtin_apply (FUNCTION, ARGUMENTS, ARGSIZE) invokes FUNCTION with a copy of the parameters described by ARGUMENTS, and ARGSIZE. It returns a block of memory allocated on the stack into which is stored all the registers that might possibly be used for returning the result of a function. ARGUMENTS is the value returned by __builtin_apply_args. ARGSIZE is the number of bytes of arguments that must be copied. ??? How should this value be computed? We'll also need a safe worst case value for varargs functions. */ case BUILT_IN_APPLY: if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE) && !validate_arglist (arglist, REFERENCE_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return const0_rtx; else { int i; tree t; rtx ops[3]; for (t = arglist, i = 0; t; t = TREE_CHAIN (t), i++) ops[i] = expand_expr (TREE_VALUE (t), NULL_RTX, VOIDmode, 0); return expand_builtin_apply (ops[0], ops[1], ops[2]); } /* __builtin_return (RESULT) causes the function to return the value described by RESULT. RESULT is address of the block of memory returned by __builtin_apply. */ case BUILT_IN_RETURN: if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) expand_builtin_return (expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0)); return const0_rtx; case BUILT_IN_SAVEREGS: return expand_builtin_saveregs (); case BUILT_IN_ARGS_INFO: return expand_builtin_args_info (arglist); /* Return the address of the first anonymous stack arg. */ case BUILT_IN_NEXT_ARG: return expand_builtin_next_arg (arglist); case BUILT_IN_CLASSIFY_TYPE: return expand_builtin_classify_type (arglist); case BUILT_IN_CONSTANT_P: return expand_builtin_constant_p (arglist, target_mode); case BUILT_IN_FRAME_ADDRESS: case BUILT_IN_RETURN_ADDRESS: return expand_builtin_frame_address (fndecl, arglist); /* Returns the address of the area where the structure is returned. 0 otherwise. */ case BUILT_IN_AGGREGATE_INCOMING_ADDRESS: if (arglist != 0 || ! AGGREGATE_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))) || GET_CODE (DECL_RTL (DECL_RESULT (current_function_decl))) != MEM) return const0_rtx; else return XEXP (DECL_RTL (DECL_RESULT (current_function_decl)), 0); case BUILT_IN_ALLOCA: target = expand_builtin_alloca (arglist, target); if (target) return target; break; case BUILT_IN_FFS: case BUILT_IN_FFSL: case BUILT_IN_FFSLL: target = expand_builtin_unop (target_mode, arglist, target, subtarget, ffs_optab); if (target) return target; break; case BUILT_IN_CLZ: case BUILT_IN_CLZL: case BUILT_IN_CLZLL: target = expand_builtin_unop (target_mode, arglist, target, subtarget, clz_optab); if (target) return target; break; case BUILT_IN_CTZ: case BUILT_IN_CTZL: case BUILT_IN_CTZLL: target = expand_builtin_unop (target_mode, arglist, target, subtarget, ctz_optab); if (target) return target; break; case BUILT_IN_POPCOUNT: case BUILT_IN_POPCOUNTL: case BUILT_IN_POPCOUNTLL: target = expand_builtin_unop (target_mode, arglist, target, subtarget, popcount_optab); if (target) return target; break; case BUILT_IN_PARITY: case BUILT_IN_PARITYL: case BUILT_IN_PARITYLL: target = expand_builtin_unop (target_mode, arglist, target, subtarget, parity_optab); if (target) return target; break; case BUILT_IN_STRLEN: target = expand_builtin_strlen (arglist, target, target_mode); if (target) return target; break; case BUILT_IN_STRCPY: target = expand_builtin_strcpy (arglist, target, mode); if (target) return target; break; case BUILT_IN_STRNCPY: target = expand_builtin_strncpy (arglist, target, mode); if (target) return target; break; case BUILT_IN_STPCPY: target = expand_builtin_stpcpy (arglist, target, mode); if (target) return target; break; case BUILT_IN_STRCAT: target = expand_builtin_strcat (arglist, target, mode); if (target) return target; break; case BUILT_IN_STRNCAT: target = expand_builtin_strncat (arglist, target, mode); if (target) return target; break; case BUILT_IN_STRSPN: target = expand_builtin_strspn (arglist, target, mode); if (target) return target; break; case BUILT_IN_STRCSPN: target = expand_builtin_strcspn (arglist, target, mode); if (target) return target; break; case BUILT_IN_STRSTR: target = expand_builtin_strstr (arglist, target, mode); if (target) return target; break; case BUILT_IN_STRPBRK: target = expand_builtin_strpbrk (arglist, target, mode); if (target) return target; break; case BUILT_IN_INDEX: case BUILT_IN_STRCHR: target = expand_builtin_strchr (arglist, target, mode); if (target) return target; break; case BUILT_IN_RINDEX: case BUILT_IN_STRRCHR: target = expand_builtin_strrchr (arglist, target, mode); if (target) return target; break; case BUILT_IN_MEMCPY: target = expand_builtin_memcpy (arglist, target, mode); if (target) return target; break; case BUILT_IN_MEMPCPY: target = expand_builtin_mempcpy (arglist, target, mode, /*endp=*/ 1); if (target) return target; break; case BUILT_IN_MEMMOVE: target = expand_builtin_memmove (arglist, target, mode); if (target) return target; break; case BUILT_IN_BCOPY: target = expand_builtin_bcopy (arglist); if (target) return target; break; case BUILT_IN_MEMSET: target = expand_builtin_memset (arglist, target, mode); if (target) return target; break; case BUILT_IN_BZERO: target = expand_builtin_bzero (arglist); if (target) return target; break; case BUILT_IN_STRCMP: target = expand_builtin_strcmp (exp, target, mode); if (target) return target; break; case BUILT_IN_STRNCMP: target = expand_builtin_strncmp (exp, target, mode); if (target) return target; break; case BUILT_IN_BCMP: case BUILT_IN_MEMCMP: target = expand_builtin_memcmp (exp, arglist, target, mode); if (target) return target; break; case BUILT_IN_SETJMP: target = expand_builtin_setjmp (arglist, target); if (target) return target; break; /* __builtin_longjmp is passed a pointer to an array of five words. It's similar to the C library longjmp function but works with __builtin_setjmp above. */ case BUILT_IN_LONGJMP: if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) break; else { rtx buf_addr = expand_expr (TREE_VALUE (arglist), subtarget, VOIDmode, 0); rtx value = expand_expr (TREE_VALUE (TREE_CHAIN (arglist)), NULL_RTX, VOIDmode, 0); if (value != const1_rtx) { error ("__builtin_longjmp second argument must be 1"); return const0_rtx; } expand_builtin_longjmp (buf_addr, value); return const0_rtx; } case BUILT_IN_TRAP: expand_builtin_trap (); return const0_rtx; case BUILT_IN_PRINTF: target = expand_builtin_printf (arglist, target, mode, false); if (target) return target; break; case BUILT_IN_PRINTF_UNLOCKED: target = expand_builtin_printf (arglist, target, mode, true); if (target) return target; break; case BUILT_IN_FPUTS: target = expand_builtin_fputs (arglist, target, false); if (target) return target; break; case BUILT_IN_FPUTS_UNLOCKED: target = expand_builtin_fputs (arglist, target, true); if (target) return target; break; case BUILT_IN_FPRINTF: target = expand_builtin_fprintf (arglist, target, mode, false); if (target) return target; break; case BUILT_IN_FPRINTF_UNLOCKED: target = expand_builtin_fprintf (arglist, target, mode, true); if (target) return target; break; case BUILT_IN_SPRINTF: target = expand_builtin_sprintf (arglist, target, mode); if (target) return target; break; /* Various hooks for the DWARF 2 __throw routine. */ case BUILT_IN_UNWIND_INIT: expand_builtin_unwind_init (); return const0_rtx; case BUILT_IN_DWARF_CFA: return virtual_cfa_rtx; #ifdef DWARF2_UNWIND_INFO case BUILT_IN_DWARF_SP_COLUMN: return expand_builtin_dwarf_sp_column (); case BUILT_IN_INIT_DWARF_REG_SIZES: expand_builtin_init_dwarf_reg_sizes (TREE_VALUE (arglist)); return const0_rtx; #endif case BUILT_IN_FROB_RETURN_ADDR: return expand_builtin_frob_return_addr (TREE_VALUE (arglist)); case BUILT_IN_EXTRACT_RETURN_ADDR: return expand_builtin_extract_return_addr (TREE_VALUE (arglist)); case BUILT_IN_EH_RETURN: expand_builtin_eh_return (TREE_VALUE (arglist), TREE_VALUE (TREE_CHAIN (arglist))); return const0_rtx; #ifdef EH_RETURN_DATA_REGNO case BUILT_IN_EH_RETURN_DATA_REGNO: return expand_builtin_eh_return_data_regno (arglist); #endif case BUILT_IN_EXTEND_POINTER: return expand_builtin_extend_pointer (TREE_VALUE (arglist)); case BUILT_IN_VA_START: case BUILT_IN_STDARG_START: return expand_builtin_va_start (arglist); case BUILT_IN_VA_END: return expand_builtin_va_end (arglist); case BUILT_IN_VA_COPY: return expand_builtin_va_copy (arglist); case BUILT_IN_EXPECT: return expand_builtin_expect (arglist, target); case BUILT_IN_PREFETCH: expand_builtin_prefetch (arglist); return const0_rtx; default: /* just do library call, if unknown builtin */ if (!DECL_ASSEMBLER_NAME_SET_P (fndecl)) error ("built-in function `%s' not currently supported", IDENTIFIER_POINTER (DECL_NAME (fndecl))); } /* The switch statement above can drop through to cause the function to be called normally. */ return expand_call (exp, target, ignore); } /* Determine whether a tree node represents a call to a built-in function. If the tree T is a call to a built-in function with the right number of arguments of the appropriate types, return the DECL_FUNCTION_CODE of the call, e.g. BUILT_IN_SQRT. Otherwise the return value is END_BUILTINS. */ enum built_in_function builtin_mathfn_code (tree t) { tree fndecl, arglist, parmlist; tree argtype, parmtype; if (TREE_CODE (t) != CALL_EXPR || TREE_CODE (TREE_OPERAND (t, 0)) != ADDR_EXPR) return END_BUILTINS; fndecl = get_callee_fndecl (t); if (fndecl == NULL_TREE || TREE_CODE (fndecl) != FUNCTION_DECL || ! DECL_BUILT_IN (fndecl) || DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) return END_BUILTINS; arglist = TREE_OPERAND (t, 1); parmlist = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); for (; parmlist; parmlist = TREE_CHAIN (parmlist)) { /* If a function doesn't take a variable number of arguments, the last element in the list will have type `void'. */ parmtype = TREE_VALUE (parmlist); if (VOID_TYPE_P (parmtype)) { if (arglist) return END_BUILTINS; return DECL_FUNCTION_CODE (fndecl); } if (! arglist) return END_BUILTINS; argtype = TREE_TYPE (TREE_VALUE (arglist)); if (SCALAR_FLOAT_TYPE_P (parmtype)) { if (! SCALAR_FLOAT_TYPE_P (argtype)) return END_BUILTINS; } else if (COMPLEX_FLOAT_TYPE_P (parmtype)) { if (! COMPLEX_FLOAT_TYPE_P (argtype)) return END_BUILTINS; } else if (POINTER_TYPE_P (parmtype)) { if (! POINTER_TYPE_P (argtype)) return END_BUILTINS; } else if (INTEGRAL_TYPE_P (parmtype)) { if (! INTEGRAL_TYPE_P (argtype)) return END_BUILTINS; } else return END_BUILTINS; arglist = TREE_CHAIN (arglist); } /* Variable-length argument list. */ return DECL_FUNCTION_CODE (fndecl); } /* Fold a call to __builtin_constant_p, if we know it will evaluate to a constant. ARGLIST is the argument list of the call. */ static tree fold_builtin_constant_p (tree arglist) { if (arglist == 0) return 0; arglist = TREE_VALUE (arglist); /* We return 1 for a numeric type that's known to be a constant value at compile-time or for an aggregate type that's a literal constant. */ STRIP_NOPS (arglist); /* If we know this is a constant, emit the constant of one. */ if (TREE_CODE_CLASS (TREE_CODE (arglist)) == 'c' || (TREE_CODE (arglist) == CONSTRUCTOR && TREE_CONSTANT (arglist)) || (TREE_CODE (arglist) == ADDR_EXPR && TREE_CODE (TREE_OPERAND (arglist, 0)) == STRING_CST)) return integer_one_node; /* If this expression has side effects, show we don't know it to be a constant. Likewise if it's a pointer or aggregate type since in those case we only want literals, since those are only optimized when generating RTL, not later. And finally, if we are compiling an initializer, not code, we need to return a definite result now; there's not going to be any more optimization done. */ if (TREE_SIDE_EFFECTS (arglist) || AGGREGATE_TYPE_P (TREE_TYPE (arglist)) || POINTER_TYPE_P (TREE_TYPE (arglist)) || cfun == 0) return integer_zero_node; return 0; } /* Fold a call to __builtin_classify_type. */ static tree fold_builtin_classify_type (tree arglist) { if (arglist == 0) return build_int_2 (no_type_class, 0); return build_int_2 (type_to_class (TREE_TYPE (TREE_VALUE (arglist))), 0); } /* Fold a call to __builtin_inf or __builtin_huge_val. */ static tree fold_builtin_inf (tree type, int warn) { REAL_VALUE_TYPE real; if (!MODE_HAS_INFINITIES (TYPE_MODE (type)) && warn) warning ("target format does not support infinity"); real_inf (&real); return build_real (type, real); } /* Fold a call to __builtin_nan or __builtin_nans. */ static tree fold_builtin_nan (tree arglist, tree type, int quiet) { REAL_VALUE_TYPE real; const char *str; if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) return 0; str = c_getstr (TREE_VALUE (arglist)); if (!str) return 0; if (!real_nan (&real, str, quiet, TYPE_MODE (type))) return 0; return build_real (type, real); } /* Return true if the floating point expression T has an integer value. We also allow +Inf, -Inf and NaN to be considered integer values. */ static bool integer_valued_real_p (tree t) { switch (TREE_CODE (t)) { case FLOAT_EXPR: return true; case ABS_EXPR: case SAVE_EXPR: case NON_LVALUE_EXPR: return integer_valued_real_p (TREE_OPERAND (t, 0)); case COMPOUND_EXPR: case MODIFY_EXPR: case BIND_EXPR: return integer_valued_real_p (TREE_OPERAND (t, 1)); case PLUS_EXPR: case MINUS_EXPR: case MULT_EXPR: case MIN_EXPR: case MAX_EXPR: return integer_valued_real_p (TREE_OPERAND (t, 0)) && integer_valued_real_p (TREE_OPERAND (t, 1)); case COND_EXPR: return integer_valued_real_p (TREE_OPERAND (t, 1)) && integer_valued_real_p (TREE_OPERAND (t, 2)); case REAL_CST: if (! TREE_CONSTANT_OVERFLOW (t)) { REAL_VALUE_TYPE c, cint; c = TREE_REAL_CST (t); real_trunc (&cint, TYPE_MODE (TREE_TYPE (t)), &c); return real_identical (&c, &cint); } case NOP_EXPR: { tree type = TREE_TYPE (TREE_OPERAND (t, 0)); if (TREE_CODE (type) == INTEGER_TYPE) return true; if (TREE_CODE (type) == REAL_TYPE) return integer_valued_real_p (TREE_OPERAND (t, 0)); break; } case CALL_EXPR: switch (builtin_mathfn_code (t)) { case BUILT_IN_CEIL: case BUILT_IN_CEILF: case BUILT_IN_CEILL: case BUILT_IN_FLOOR: case BUILT_IN_FLOORF: case BUILT_IN_FLOORL: case BUILT_IN_NEARBYINT: case BUILT_IN_NEARBYINTF: case BUILT_IN_NEARBYINTL: case BUILT_IN_ROUND: case BUILT_IN_ROUNDF: case BUILT_IN_ROUNDL: case BUILT_IN_TRUNC: case BUILT_IN_TRUNCF: case BUILT_IN_TRUNCL: return true; default: break; } break; default: break; } return false; } /* EXP is assumed to be builtin call where truncation can be propagated across (for instance floor((double)f) == (double)floorf (f). Do the transformation. */ static tree fold_trunc_transparent_mathfn (tree exp) { tree fndecl = get_callee_fndecl (exp); tree arglist = TREE_OPERAND (exp, 1); enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); tree arg; if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) return 0; arg = TREE_VALUE (arglist); /* Integer rounding functions are idempotent. */ if (fcode == builtin_mathfn_code (arg)) return arg; /* If argument is already integer valued, and we don't need to worry about setting errno, there's no need to perform rounding. */ if (! flag_errno_math && integer_valued_real_p (arg)) return arg; if (optimize) { tree arg0 = strip_float_extensions (arg); tree ftype = TREE_TYPE (exp); tree newtype = TREE_TYPE (arg0); tree decl; if (TYPE_PRECISION (newtype) < TYPE_PRECISION (ftype) && (decl = mathfn_built_in (newtype, fcode))) { arglist = build_tree_list (NULL_TREE, fold (convert (newtype, arg0))); return convert (ftype, build_function_call_expr (decl, arglist)); } } return 0; } /* Fold function call to builtin cabs, cabsf or cabsl. FNDECL is the function's DECL, ARGLIST is the argument list and TYPE is the return type. Return NULL_TREE if no simplification can be made. */ static tree fold_builtin_cabs (tree fndecl, tree arglist, tree type) { tree arg; if (!arglist || TREE_CHAIN (arglist)) return NULL_TREE; arg = TREE_VALUE (arglist); if (TREE_CODE (TREE_TYPE (arg)) != COMPLEX_TYPE || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) != REAL_TYPE) return NULL_TREE; /* Evaluate cabs of a constant at compile-time. */ if (flag_unsafe_math_optimizations && TREE_CODE (arg) == COMPLEX_CST && TREE_CODE (TREE_REALPART (arg)) == REAL_CST && TREE_CODE (TREE_IMAGPART (arg)) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (TREE_REALPART (arg)) && ! TREE_CONSTANT_OVERFLOW (TREE_IMAGPART (arg))) { REAL_VALUE_TYPE r, i; r = TREE_REAL_CST (TREE_REALPART (arg)); i = TREE_REAL_CST (TREE_IMAGPART (arg)); real_arithmetic (&r, MULT_EXPR, &r, &r); real_arithmetic (&i, MULT_EXPR, &i, &i); real_arithmetic (&r, PLUS_EXPR, &r, &i); if (real_sqrt (&r, TYPE_MODE (type), &r) || ! flag_trapping_math) return build_real (type, r); } /* If either part is zero, cabs is fabs of the other. */ if (TREE_CODE (arg) == COMPLEX_EXPR && real_zerop (TREE_OPERAND (arg, 0))) return fold (build1 (ABS_EXPR, type, TREE_OPERAND (arg, 1))); if (TREE_CODE (arg) == COMPLEX_EXPR && real_zerop (TREE_OPERAND (arg, 1))) return fold (build1 (ABS_EXPR, type, TREE_OPERAND (arg, 0))); if (flag_unsafe_math_optimizations) { enum built_in_function fcode; tree sqrtfn; fcode = DECL_FUNCTION_CODE (fndecl); if (fcode == BUILT_IN_CABS) sqrtfn = implicit_built_in_decls[BUILT_IN_SQRT]; else if (fcode == BUILT_IN_CABSF) sqrtfn = implicit_built_in_decls[BUILT_IN_SQRTF]; else if (fcode == BUILT_IN_CABSL) sqrtfn = implicit_built_in_decls[BUILT_IN_SQRTL]; else sqrtfn = NULL_TREE; if (sqrtfn != NULL_TREE) { tree rpart, ipart, result, arglist; arg = save_expr (arg); rpart = fold (build1 (REALPART_EXPR, type, arg)); ipart = fold (build1 (IMAGPART_EXPR, type, arg)); rpart = save_expr (rpart); ipart = save_expr (ipart); result = fold (build (PLUS_EXPR, type, fold (build (MULT_EXPR, type, rpart, rpart)), fold (build (MULT_EXPR, type, ipart, ipart)))); arglist = build_tree_list (NULL_TREE, result); return build_function_call_expr (sqrtfn, arglist); } } return NULL_TREE; } /* Fold function call to builtin trunc, truncf or truncl. Return NULL_TREE if no simplification can be made. */ static tree fold_builtin_trunc (tree exp) { tree arglist = TREE_OPERAND (exp, 1); tree arg; if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) return 0; /* Optimize trunc of constant value. */ arg = TREE_VALUE (arglist); if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg)) { REAL_VALUE_TYPE r, x; tree type = TREE_TYPE (exp); x = TREE_REAL_CST (arg); real_trunc (&r, TYPE_MODE (type), &x); return build_real (type, r); } return fold_trunc_transparent_mathfn (exp); } /* Fold function call to builtin floor, floorf or floorl. Return NULL_TREE if no simplification can be made. */ static tree fold_builtin_floor (tree exp) { tree arglist = TREE_OPERAND (exp, 1); tree arg; if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) return 0; /* Optimize floor of constant value. */ arg = TREE_VALUE (arglist); if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg)) { REAL_VALUE_TYPE x; x = TREE_REAL_CST (arg); if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math) { tree type = TREE_TYPE (exp); REAL_VALUE_TYPE r; real_floor (&r, TYPE_MODE (type), &x); return build_real (type, r); } } return fold_trunc_transparent_mathfn (exp); } /* Fold function call to builtin ceil, ceilf or ceill. Return NULL_TREE if no simplification can be made. */ static tree fold_builtin_ceil (tree exp) { tree arglist = TREE_OPERAND (exp, 1); tree arg; if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) return 0; /* Optimize ceil of constant value. */ arg = TREE_VALUE (arglist); if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg)) { REAL_VALUE_TYPE x; x = TREE_REAL_CST (arg); if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math) { tree type = TREE_TYPE (exp); REAL_VALUE_TYPE r; real_ceil (&r, TYPE_MODE (type), &x); return build_real (type, r); } } return fold_trunc_transparent_mathfn (exp); } /* Fold function call to builtin ffs, clz, ctz, popcount and parity and their long and long long variants (i.e. ffsl and ffsll). Return NULL_TREE if no simplification can be made. */ static tree fold_builtin_bitop (tree exp) { tree fndecl = get_callee_fndecl (exp); tree arglist = TREE_OPERAND (exp, 1); tree arg; if (! validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE)) return NULL_TREE; /* Optimize for constant argument. */ arg = TREE_VALUE (arglist); if (TREE_CODE (arg) == INTEGER_CST && ! TREE_CONSTANT_OVERFLOW (arg)) { HOST_WIDE_INT hi, width, result; unsigned HOST_WIDE_INT lo; tree type, t; type = TREE_TYPE (arg); width = TYPE_PRECISION (type); lo = TREE_INT_CST_LOW (arg); /* Clear all the bits that are beyond the type's precision. */ if (width > HOST_BITS_PER_WIDE_INT) { hi = TREE_INT_CST_HIGH (arg); if (width < 2 * HOST_BITS_PER_WIDE_INT) hi &= ~((HOST_WIDE_INT) (-1) >> (width - HOST_BITS_PER_WIDE_INT)); } else { hi = 0; if (width < HOST_BITS_PER_WIDE_INT) lo &= ~((unsigned HOST_WIDE_INT) (-1) << width); } switch (DECL_FUNCTION_CODE (fndecl)) { case BUILT_IN_FFS: case BUILT_IN_FFSL: case BUILT_IN_FFSLL: if (lo != 0) result = exact_log2 (lo & -lo) + 1; else if (hi != 0) result = HOST_BITS_PER_WIDE_INT + exact_log2 (hi & -hi) + 1; else result = 0; break; case BUILT_IN_CLZ: case BUILT_IN_CLZL: case BUILT_IN_CLZLL: if (hi != 0) result = width - floor_log2 (hi) - 1 - HOST_BITS_PER_WIDE_INT; else if (lo != 0) result = width - floor_log2 (lo) - 1; else if (! CLZ_DEFINED_VALUE_AT_ZERO (TYPE_MODE (type), result)) result = width; break; case BUILT_IN_CTZ: case BUILT_IN_CTZL: case BUILT_IN_CTZLL: if (lo != 0) result = exact_log2 (lo & -lo); else if (hi != 0) result = HOST_BITS_PER_WIDE_INT + exact_log2 (hi & -hi); else if (! CTZ_DEFINED_VALUE_AT_ZERO (TYPE_MODE (type), result)) result = width; break; case BUILT_IN_POPCOUNT: case BUILT_IN_POPCOUNTL: case BUILT_IN_POPCOUNTLL: result = 0; while (lo) result++, lo &= lo - 1; while (hi) result++, hi &= hi - 1; break; case BUILT_IN_PARITY: case BUILT_IN_PARITYL: case BUILT_IN_PARITYLL: result = 0; while (lo) result++, lo &= lo - 1; while (hi) result++, hi &= hi - 1; result &= 1; break; default: abort(); } t = build_int_2 (result, 0); TREE_TYPE (t) = TREE_TYPE (exp); return t; } return NULL_TREE; } /* Return true if EXPR is the real constant contained in VALUE. */ static bool real_dconstp (tree expr, const REAL_VALUE_TYPE *value) { STRIP_NOPS (expr); return ((TREE_CODE (expr) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (expr) && REAL_VALUES_EQUAL (TREE_REAL_CST (expr), *value)) || (TREE_CODE (expr) == COMPLEX_CST && real_dconstp (TREE_REALPART (expr), value) && real_zerop (TREE_IMAGPART (expr)))); } /* A subroutine of fold_builtin to fold the various logarithmic functions. EXP is the CALL_EXPR of a call to a builtin log* function. VALUE is the base of the log* function. */ static tree fold_builtin_logarithm (tree exp, const REAL_VALUE_TYPE *value) { tree arglist = TREE_OPERAND (exp, 1); if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) { tree fndecl = get_callee_fndecl (exp); tree type = TREE_TYPE (TREE_TYPE (fndecl)); tree arg = TREE_VALUE (arglist); const enum built_in_function fcode = builtin_mathfn_code (arg); /* Optimize log*(1.0) = 0.0. */ if (real_onep (arg)) return build_real (type, dconst0); /* Optimize logN(N) = 1.0. If N can't be truncated to MODE exactly, then only do this if flag_unsafe_math_optimizations. */ if (exact_real_truncate (TYPE_MODE (type), value) || flag_unsafe_math_optimizations) { const REAL_VALUE_TYPE value_truncate = real_value_truncate (TYPE_MODE (type), *value); if (real_dconstp (arg, &value_truncate)) return build_real (type, dconst1); } /* Special case, optimize logN(expN(x)) = x. */ if (flag_unsafe_math_optimizations && ((value == &dconste && (fcode == BUILT_IN_EXP || fcode == BUILT_IN_EXPF || fcode == BUILT_IN_EXPL)) || (value == &dconst2 && (fcode == BUILT_IN_EXP2 || fcode == BUILT_IN_EXP2F || fcode == BUILT_IN_EXP2L)) || (value == &dconst10 && (fcode == BUILT_IN_EXP10 || fcode == BUILT_IN_EXP10F || fcode == BUILT_IN_EXP10L)))) return convert (type, TREE_VALUE (TREE_OPERAND (arg, 1))); /* Optimize log*(func()) for various exponential functions. We want to determine the value "x" and the power "exponent" in order to transform logN(x**exponent) into exponent*logN(x). */ if (flag_unsafe_math_optimizations) { tree exponent = 0, x = 0; switch (fcode) { case BUILT_IN_EXP: case BUILT_IN_EXPF: case BUILT_IN_EXPL: /* Prepare to do logN(exp(exponent) -> exponent*logN(e). */ x = build_real (type, real_value_truncate (TYPE_MODE (type), dconste)); exponent = TREE_VALUE (TREE_OPERAND (arg, 1)); break; case BUILT_IN_EXP2: case BUILT_IN_EXP2F: case BUILT_IN_EXP2L: /* Prepare to do logN(exp2(exponent) -> exponent*logN(2). */ x = build_real (type, dconst2); exponent = TREE_VALUE (TREE_OPERAND (arg, 1)); break; case BUILT_IN_EXP10: case BUILT_IN_EXP10F: case BUILT_IN_EXP10L: case BUILT_IN_POW10: case BUILT_IN_POW10F: case BUILT_IN_POW10L: /* Prepare to do logN(exp10(exponent) -> exponent*logN(10). */ x = build_real (type, dconst10); exponent = TREE_VALUE (TREE_OPERAND (arg, 1)); break; case BUILT_IN_SQRT: case BUILT_IN_SQRTF: case BUILT_IN_SQRTL: /* Prepare to do logN(sqrt(x) -> 0.5*logN(x). */ x = TREE_VALUE (TREE_OPERAND (arg, 1)); exponent = build_real (type, dconsthalf); break; case BUILT_IN_CBRT: case BUILT_IN_CBRTF: case BUILT_IN_CBRTL: /* Prepare to do logN(cbrt(x) -> (1/3)*logN(x). */ x = TREE_VALUE (TREE_OPERAND (arg, 1)); exponent = build_real (type, real_value_truncate (TYPE_MODE (type), dconstthird)); break; case BUILT_IN_POW: case BUILT_IN_POWF: case BUILT_IN_POWL: /* Prepare to do logN(pow(x,exponent) -> exponent*logN(x). */ x = TREE_VALUE (TREE_OPERAND (arg, 1)); exponent = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1))); break; default: break; } /* Now perform the optimization. */ if (x && exponent) { tree logfn; arglist = build_tree_list (NULL_TREE, x); logfn = build_function_call_expr (fndecl, arglist); return fold (build (MULT_EXPR, type, exponent, logfn)); } } } return 0; } /* A subroutine of fold_builtin to fold the various exponent functions. EXP is the CALL_EXPR of a call to a builtin function. VALUE is the value which will be raised to a power. */ static tree fold_builtin_exponent (tree exp, const REAL_VALUE_TYPE *value) { tree arglist = TREE_OPERAND (exp, 1); if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) { tree fndecl = get_callee_fndecl (exp); tree type = TREE_TYPE (TREE_TYPE (fndecl)); tree arg = TREE_VALUE (arglist); /* Optimize exp*(0.0) = 1.0. */ if (real_zerop (arg)) return build_real (type, dconst1); /* Optimize expN(1.0) = N. */ if (real_onep (arg)) { REAL_VALUE_TYPE cst; real_convert (&cst, TYPE_MODE (type), value); return build_real (type, cst); } /* Attempt to evaluate expN(integer) at compile-time. */ if (flag_unsafe_math_optimizations && TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg)) { REAL_VALUE_TYPE cint; REAL_VALUE_TYPE c; HOST_WIDE_INT n; c = TREE_REAL_CST (arg); n = real_to_integer (&c); real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0); if (real_identical (&c, &cint)) { REAL_VALUE_TYPE x; real_powi (&x, TYPE_MODE (type), value, n); return build_real (type, x); } } /* Optimize expN(logN(x)) = x. */ if (flag_unsafe_math_optimizations) { const enum built_in_function fcode = builtin_mathfn_code (arg); if ((value == &dconste && (fcode == BUILT_IN_LOG || fcode == BUILT_IN_LOGF || fcode == BUILT_IN_LOGL)) || (value == &dconst2 && (fcode == BUILT_IN_LOG2 || fcode == BUILT_IN_LOG2F || fcode == BUILT_IN_LOG2L)) || (value == &dconst10 && (fcode == BUILT_IN_LOG10 || fcode == BUILT_IN_LOG10F || fcode == BUILT_IN_LOG10L))) return convert (type, TREE_VALUE (TREE_OPERAND (arg, 1))); } } return 0; } /* Fold function call to builtin memcpy. Return NULL_TREE if no simplification can be made. */ static tree fold_builtin_memcpy (tree exp) { tree arglist = TREE_OPERAND (exp, 1); tree dest, src, len; if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; dest = TREE_VALUE (arglist); src = TREE_VALUE (TREE_CHAIN (arglist)); len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); /* If the LEN parameter is zero, return DEST. */ if (integer_zerop (len)) return omit_one_operand (TREE_TYPE (exp), dest, src); /* If SRC and DEST are the same (and not volatile), return DEST. */ if (operand_equal_p (src, dest, 0)) return omit_one_operand (TREE_TYPE (exp), dest, len); return 0; } /* Fold function call to builtin mempcpy. Return NULL_TREE if no simplification can be made. */ static tree fold_builtin_mempcpy (tree exp) { tree arglist = TREE_OPERAND (exp, 1); tree dest, src, len; if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; dest = TREE_VALUE (arglist); src = TREE_VALUE (TREE_CHAIN (arglist)); len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); /* If the LEN parameter is zero, return DEST. */ if (integer_zerop (len)) return omit_one_operand (TREE_TYPE (exp), dest, src); /* If SRC and DEST are the same (and not volatile), return DEST+LEN. */ if (operand_equal_p (src, dest, 0)) { tree temp = convert (TREE_TYPE (dest), len); temp = fold (build (PLUS_EXPR, TREE_TYPE (dest), dest, len)); return convert (TREE_TYPE (exp), temp); } return 0; } /* Fold function call to builtin memmove. Return NULL_TREE if no simplification can be made. */ static tree fold_builtin_memmove (tree exp) { tree arglist = TREE_OPERAND (exp, 1); tree dest, src, len; if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; dest = TREE_VALUE (arglist); src = TREE_VALUE (TREE_CHAIN (arglist)); len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); /* If the LEN parameter is zero, return DEST. */ if (integer_zerop (len)) return omit_one_operand (TREE_TYPE (exp), dest, src); /* If SRC and DEST are the same (and not volatile), return DEST. */ if (operand_equal_p (src, dest, 0)) return omit_one_operand (TREE_TYPE (exp), dest, len); return 0; } /* Fold function call to builtin strcpy. Return NULL_TREE if no simplification can be made. */ static tree fold_builtin_strcpy (tree exp) { tree arglist = TREE_OPERAND (exp, 1); tree dest, src; if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; dest = TREE_VALUE (arglist); src = TREE_VALUE (TREE_CHAIN (arglist)); /* If SRC and DEST are the same (and not volatile), return DEST. */ if (operand_equal_p (src, dest, 0)) return convert (TREE_TYPE (exp), dest); return 0; } /* Fold function call to builtin strncpy. Return NULL_TREE if no simplification can be made. */ static tree fold_builtin_strncpy (tree exp) { tree arglist = TREE_OPERAND (exp, 1); tree dest, src, len; if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; dest = TREE_VALUE (arglist); src = TREE_VALUE (TREE_CHAIN (arglist)); len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); /* If the LEN parameter is zero, return DEST. */ if (integer_zerop (len)) return omit_one_operand (TREE_TYPE (exp), dest, src); return 0; } /* Fold function call to builtin memcmp. Return NULL_TREE if no simplification can be made. */ static tree fold_builtin_memcmp (tree exp) { tree arglist = TREE_OPERAND (exp, 1); tree arg1, arg2, len; if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; arg1 = TREE_VALUE (arglist); arg2 = TREE_VALUE (TREE_CHAIN (arglist)); len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); /* If the LEN parameter is zero, return zero. */ if (integer_zerop (len)) { tree temp = omit_one_operand (TREE_TYPE (exp), integer_zero_node, arg2); return omit_one_operand (TREE_TYPE (exp), temp, arg1); } /* If ARG1 and ARG2 are the same (and not volatile), return zero. */ if (operand_equal_p (arg1, arg2, 0)) return omit_one_operand (TREE_TYPE (exp), integer_zero_node, len); return 0; } /* Fold function call to builtin strcmp. Return NULL_TREE if no simplification can be made. */ static tree fold_builtin_strcmp (tree exp) { tree arglist = TREE_OPERAND (exp, 1); tree arg1, arg2; const char *p1, *p2; if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; arg1 = TREE_VALUE (arglist); arg2 = TREE_VALUE (TREE_CHAIN (arglist)); /* If ARG1 and ARG2 are the same (and not volatile), return zero. */ if (operand_equal_p (arg1, arg2, 0)) return convert (TREE_TYPE (exp), integer_zero_node); p1 = c_getstr (arg1); p2 = c_getstr (arg2); if (p1 && p2) { tree temp; const int i = strcmp (p1, p2); if (i < 0) temp = integer_minus_one_node; else if (i > 0) temp = integer_one_node; else temp = integer_zero_node; return convert (TREE_TYPE (exp), temp); } return 0; } /* Fold function call to builtin strncmp. Return NULL_TREE if no simplification can be made. */ static tree fold_builtin_strncmp (tree exp) { tree arglist = TREE_OPERAND (exp, 1); tree arg1, arg2, len; const char *p1, *p2; if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; arg1 = TREE_VALUE (arglist); arg2 = TREE_VALUE (TREE_CHAIN (arglist)); len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); /* If the LEN parameter is zero, return zero. */ if (integer_zerop (len)) { tree temp = omit_one_operand (TREE_TYPE (exp), integer_zero_node, arg2); return omit_one_operand (TREE_TYPE (exp), temp, arg1); } /* If ARG1 and ARG2 are the same (and not volatile), return zero. */ if (operand_equal_p (arg1, arg2, 0)) return omit_one_operand (TREE_TYPE (exp), integer_zero_node, len); p1 = c_getstr (arg1); p2 = c_getstr (arg2); if (host_integerp (len, 1) && p1 && p2) { tree temp; const int i = strncmp (p1, p2, tree_low_cst (len, 1)); if (i < 0) temp = integer_minus_one_node; else if (i > 0) temp = integer_one_node; else temp = integer_zero_node; return convert (TREE_TYPE (exp), temp); } return 0; } /* Used by constant folding to eliminate some builtin calls early. EXP is the CALL_EXPR of a call to a builtin function. */ tree fold_builtin (tree exp) { tree fndecl = get_callee_fndecl (exp); tree arglist = TREE_OPERAND (exp, 1); tree type = TREE_TYPE (TREE_TYPE (fndecl)); if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) return 0; switch (DECL_FUNCTION_CODE (fndecl)) { case BUILT_IN_CONSTANT_P: return fold_builtin_constant_p (arglist); case BUILT_IN_CLASSIFY_TYPE: return fold_builtin_classify_type (arglist); case BUILT_IN_STRLEN: if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) { tree len = c_strlen (TREE_VALUE (arglist), 0); if (len) { /* Convert from the internal "sizetype" type to "size_t". */ if (size_type_node) len = convert (size_type_node, len); return len; } } break; case BUILT_IN_FABS: case BUILT_IN_FABSF: case BUILT_IN_FABSL: if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) return fold (build1 (ABS_EXPR, type, TREE_VALUE (arglist))); break; case BUILT_IN_CABS: case BUILT_IN_CABSF: case BUILT_IN_CABSL: return fold_builtin_cabs (fndecl, arglist, type); case BUILT_IN_SQRT: case BUILT_IN_SQRTF: case BUILT_IN_SQRTL: if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) { enum built_in_function fcode; tree arg = TREE_VALUE (arglist); /* Optimize sqrt of constant value. */ if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg)) { REAL_VALUE_TYPE r, x; x = TREE_REAL_CST (arg); if (real_sqrt (&r, TYPE_MODE (type), &x) || (!flag_trapping_math && !flag_errno_math)) return build_real (type, r); } /* Optimize sqrt(exp(x)) = exp(x*0.5). */ fcode = builtin_mathfn_code (arg); if (flag_unsafe_math_optimizations && (fcode == BUILT_IN_EXP || fcode == BUILT_IN_EXPF || fcode == BUILT_IN_EXPL)) { tree expfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0); arg = fold (build (MULT_EXPR, type, TREE_VALUE (TREE_OPERAND (arg, 1)), build_real (type, dconsthalf))); arglist = build_tree_list (NULL_TREE, arg); return build_function_call_expr (expfn, arglist); } /* Optimize sqrt(pow(x,y)) = pow(|x|,y*0.5). */ if (flag_unsafe_math_optimizations && (fcode == BUILT_IN_POW || fcode == BUILT_IN_POWF || fcode == BUILT_IN_POWL)) { tree powfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0); tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1)); tree arg1 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1))); tree narg1; if (!tree_expr_nonnegative_p (arg0)) arg0 = build1 (ABS_EXPR, type, arg0); narg1 = fold (build (MULT_EXPR, type, arg1, build_real (type, dconsthalf))); arglist = tree_cons (NULL_TREE, arg0, build_tree_list (NULL_TREE, narg1)); return build_function_call_expr (powfn, arglist); } } break; case BUILT_IN_SIN: case BUILT_IN_SINF: case BUILT_IN_SINL: if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) { tree arg = TREE_VALUE (arglist); /* Optimize sin(0.0) = 0.0. */ if (real_zerop (arg)) return arg; } break; case BUILT_IN_COS: case BUILT_IN_COSF: case BUILT_IN_COSL: if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) { tree arg = TREE_VALUE (arglist); /* Optimize cos(0.0) = 1.0. */ if (real_zerop (arg)) return build_real (type, dconst1); /* Optimize cos(-x) into cos(x). */ if (TREE_CODE (arg) == NEGATE_EXPR) { tree arglist = build_tree_list (NULL_TREE, TREE_OPERAND (arg, 0)); return build_function_call_expr (fndecl, arglist); } } break; case BUILT_IN_EXP: case BUILT_IN_EXPF: case BUILT_IN_EXPL: return fold_builtin_exponent (exp, &dconste); case BUILT_IN_EXP2: case BUILT_IN_EXP2F: case BUILT_IN_EXP2L: return fold_builtin_exponent (exp, &dconst2); case BUILT_IN_EXP10: case BUILT_IN_EXP10F: case BUILT_IN_EXP10L: case BUILT_IN_POW10: case BUILT_IN_POW10F: case BUILT_IN_POW10L: return fold_builtin_exponent (exp, &dconst10); case BUILT_IN_LOG: case BUILT_IN_LOGF: case BUILT_IN_LOGL: return fold_builtin_logarithm (exp, &dconste); break; case BUILT_IN_LOG2: case BUILT_IN_LOG2F: case BUILT_IN_LOG2L: return fold_builtin_logarithm (exp, &dconst2); break; case BUILT_IN_LOG10: case BUILT_IN_LOG10F: case BUILT_IN_LOG10L: return fold_builtin_logarithm (exp, &dconst10); break; case BUILT_IN_TAN: case BUILT_IN_TANF: case BUILT_IN_TANL: if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) { enum built_in_function fcode; tree arg = TREE_VALUE (arglist); /* Optimize tan(0.0) = 0.0. */ if (real_zerop (arg)) return arg; /* Optimize tan(atan(x)) = x. */ fcode = builtin_mathfn_code (arg); if (flag_unsafe_math_optimizations && (fcode == BUILT_IN_ATAN || fcode == BUILT_IN_ATANF || fcode == BUILT_IN_ATANL)) return TREE_VALUE (TREE_OPERAND (arg, 1)); } break; case BUILT_IN_ATAN: case BUILT_IN_ATANF: case BUILT_IN_ATANL: if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) { tree arg = TREE_VALUE (arglist); /* Optimize atan(0.0) = 0.0. */ if (real_zerop (arg)) return arg; /* Optimize atan(1.0) = pi/4. */ if (real_onep (arg)) { REAL_VALUE_TYPE cst; real_convert (&cst, TYPE_MODE (type), &dconstpi); cst.exp -= 2; return build_real (type, cst); } } break; case BUILT_IN_POW: case BUILT_IN_POWF: case BUILT_IN_POWL: if (validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) { enum built_in_function fcode; tree arg0 = TREE_VALUE (arglist); tree arg1 = TREE_VALUE (TREE_CHAIN (arglist)); /* Optimize pow(1.0,y) = 1.0. */ if (real_onep (arg0)) return omit_one_operand (type, build_real (type, dconst1), arg1); if (TREE_CODE (arg1) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg1)) { REAL_VALUE_TYPE c; c = TREE_REAL_CST (arg1); /* Optimize pow(x,0.0) = 1.0. */ if (REAL_VALUES_EQUAL (c, dconst0)) return omit_one_operand (type, build_real (type, dconst1), arg0); /* Optimize pow(x,1.0) = x. */ if (REAL_VALUES_EQUAL (c, dconst1)) return arg0; /* Optimize pow(x,-1.0) = 1.0/x. */ if (REAL_VALUES_EQUAL (c, dconstm1)) return fold (build (RDIV_EXPR, type, build_real (type, dconst1), arg0)); /* Optimize pow(x,0.5) = sqrt(x). */ if (flag_unsafe_math_optimizations && REAL_VALUES_EQUAL (c, dconsthalf)) { tree sqrtfn; fcode = DECL_FUNCTION_CODE (fndecl); if (fcode == BUILT_IN_POW) sqrtfn = implicit_built_in_decls[BUILT_IN_SQRT]; else if (fcode == BUILT_IN_POWF) sqrtfn = implicit_built_in_decls[BUILT_IN_SQRTF]; else if (fcode == BUILT_IN_POWL) sqrtfn = implicit_built_in_decls[BUILT_IN_SQRTL]; else sqrtfn = NULL_TREE; if (sqrtfn != NULL_TREE) { tree arglist = build_tree_list (NULL_TREE, arg0); return build_function_call_expr (sqrtfn, arglist); } } /* Attempt to evaluate pow at compile-time. */ if (TREE_CODE (arg0) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg0)) { REAL_VALUE_TYPE cint; HOST_WIDE_INT n; n = real_to_integer (&c); real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0); if (real_identical (&c, &cint)) { REAL_VALUE_TYPE x; bool inexact; x = TREE_REAL_CST (arg0); inexact = real_powi (&x, TYPE_MODE (type), &x, n); if (flag_unsafe_math_optimizations || !inexact) return build_real (type, x); } } } /* Optimize pow(exp(x),y) = exp(x*y). */ fcode = builtin_mathfn_code (arg0); if (flag_unsafe_math_optimizations && (fcode == BUILT_IN_EXP || fcode == BUILT_IN_EXPF || fcode == BUILT_IN_EXPL)) { tree expfn = TREE_OPERAND (TREE_OPERAND (arg0, 0), 0); tree arg = TREE_VALUE (TREE_OPERAND (arg0, 1)); arg = fold (build (MULT_EXPR, type, arg, arg1)); arglist = build_tree_list (NULL_TREE, arg); return build_function_call_expr (expfn, arglist); } /* Optimize pow(sqrt(x),y) = pow(x,y*0.5). */ if (flag_unsafe_math_optimizations && (fcode == BUILT_IN_SQRT || fcode == BUILT_IN_SQRTF || fcode == BUILT_IN_SQRTL)) { tree narg0 = TREE_VALUE (TREE_OPERAND (arg0, 1)); tree narg1 = fold (build (MULT_EXPR, type, arg1, build_real (type, dconsthalf))); arglist = tree_cons (NULL_TREE, narg0, build_tree_list (NULL_TREE, narg1)); return build_function_call_expr (fndecl, arglist); } /* Optimize pow(pow(x,y),z) = pow(x,y*z). */ if (flag_unsafe_math_optimizations && (fcode == BUILT_IN_POW || fcode == BUILT_IN_POWF || fcode == BUILT_IN_POWL)) { tree arg00 = TREE_VALUE (TREE_OPERAND (arg0, 1)); tree arg01 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg0, 1))); tree narg1 = fold (build (MULT_EXPR, type, arg01, arg1)); arglist = tree_cons (NULL_TREE, arg00, build_tree_list (NULL_TREE, narg1)); return build_function_call_expr (fndecl, arglist); } } break; case BUILT_IN_INF: case BUILT_IN_INFF: case BUILT_IN_INFL: return fold_builtin_inf (type, true); case BUILT_IN_HUGE_VAL: case BUILT_IN_HUGE_VALF: case BUILT_IN_HUGE_VALL: return fold_builtin_inf (type, false); case BUILT_IN_NAN: case BUILT_IN_NANF: case BUILT_IN_NANL: return fold_builtin_nan (arglist, type, true); case BUILT_IN_NANS: case BUILT_IN_NANSF: case BUILT_IN_NANSL: return fold_builtin_nan (arglist, type, false); case BUILT_IN_FLOOR: case BUILT_IN_FLOORF: case BUILT_IN_FLOORL: return fold_builtin_floor (exp); case BUILT_IN_CEIL: case BUILT_IN_CEILF: case BUILT_IN_CEILL: return fold_builtin_ceil (exp); case BUILT_IN_TRUNC: case BUILT_IN_TRUNCF: case BUILT_IN_TRUNCL: return fold_builtin_trunc (exp); case BUILT_IN_ROUND: case BUILT_IN_ROUNDF: case BUILT_IN_ROUNDL: case BUILT_IN_NEARBYINT: case BUILT_IN_NEARBYINTF: case BUILT_IN_NEARBYINTL: return fold_trunc_transparent_mathfn (exp); case BUILT_IN_FFS: case BUILT_IN_FFSL: case BUILT_IN_FFSLL: case BUILT_IN_CLZ: case BUILT_IN_CLZL: case BUILT_IN_CLZLL: case BUILT_IN_CTZ: case BUILT_IN_CTZL: case BUILT_IN_CTZLL: case BUILT_IN_POPCOUNT: case BUILT_IN_POPCOUNTL: case BUILT_IN_POPCOUNTLL: case BUILT_IN_PARITY: case BUILT_IN_PARITYL: case BUILT_IN_PARITYLL: return fold_builtin_bitop (exp); case BUILT_IN_MEMCPY: return fold_builtin_memcpy (exp); case BUILT_IN_MEMPCPY: return fold_builtin_mempcpy (exp); case BUILT_IN_MEMMOVE: return fold_builtin_memmove (exp); case BUILT_IN_STRCPY: return fold_builtin_strcpy (exp); case BUILT_IN_STRNCPY: return fold_builtin_strncpy (exp); case BUILT_IN_MEMCMP: return fold_builtin_memcmp (exp); case BUILT_IN_STRCMP: return fold_builtin_strcmp (exp); case BUILT_IN_STRNCMP: return fold_builtin_strncmp (exp); default: break; } return 0; } /* Conveniently construct a function call expression. */ tree build_function_call_expr (tree fn, tree arglist) { tree call_expr; call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn); call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)), call_expr, arglist); return fold (call_expr); } /* This function validates the types of a function call argument list represented as a tree chain of parameters against a specified list of tree_codes. If the last specifier is a 0, that represents an ellipses, otherwise the last specifier must be a VOID_TYPE. */ static int validate_arglist (tree arglist, ...) { enum tree_code code; int res = 0; va_list ap; va_start (ap, arglist); do { code = va_arg (ap, enum tree_code); switch (code) { case 0: /* This signifies an ellipses, any further arguments are all ok. */ res = 1; goto end; case VOID_TYPE: /* This signifies an endlink, if no arguments remain, return true, otherwise return false. */ res = arglist == 0; goto end; default: /* If no parameters remain or the parameter's code does not match the specified code, return false. Otherwise continue checking any remaining arguments. */ if (arglist == 0 || code != TREE_CODE (TREE_TYPE (TREE_VALUE (arglist)))) goto end; break; } arglist = TREE_CHAIN (arglist); } while (1); /* We need gotos here since we can only have one VA_CLOSE in a function. */ end: ; va_end (ap); return res; } /* Default target-specific builtin expander that does nothing. */ rtx default_expand_builtin (tree exp ATTRIBUTE_UNUSED, rtx target ATTRIBUTE_UNUSED, rtx subtarget ATTRIBUTE_UNUSED, enum machine_mode mode ATTRIBUTE_UNUSED, int ignore ATTRIBUTE_UNUSED) { return NULL_RTX; } /* Instantiate all remaining CONSTANT_P_RTX nodes. */ void purge_builtin_constant_p (void) { rtx insn, set, arg, new, note; for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) if (INSN_P (insn) && (set = single_set (insn)) != NULL_RTX && (GET_CODE (arg = SET_SRC (set)) == CONSTANT_P_RTX || (GET_CODE (arg) == SUBREG && (GET_CODE (arg = SUBREG_REG (arg)) == CONSTANT_P_RTX)))) { arg = XEXP (arg, 0); new = CONSTANT_P (arg) ? const1_rtx : const0_rtx; validate_change (insn, &SET_SRC (set), new, 0); /* Remove the REG_EQUAL note from the insn. */ if ((note = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0) remove_note (insn, note); } } /* Returns true is EXP represents data that would potentially reside in a readonly section. */ static bool readonly_data_expr (tree exp) { STRIP_NOPS (exp); if (TREE_CODE (exp) == ADDR_EXPR) return decl_readonly_section (TREE_OPERAND (exp, 0), 0); else return false; } diff --git a/contrib/gcc/c-common.c b/contrib/gcc/c-common.c index 50516f48de5d..7305b3c66bfa 100644 --- a/contrib/gcc/c-common.c +++ b/contrib/gcc/c-common.c @@ -1,5900 +1,5912 @@ /* Subroutines shared by all languages that are variants of C. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include "system.h" #include "coretypes.h" #include "tm.h" #include "intl.h" #include "tree.h" #include "flags.h" #include "output.h" #include "c-pragma.h" #include "rtl.h" #include "ggc.h" #include "varray.h" #include "expr.h" #include "c-common.h" #include "diagnostic.h" #include "tm_p.h" #include "obstack.h" #include "cpplib.h" #include "target.h" #include "langhooks.h" #include "tree-inline.h" #include "c-tree.h" #include "toplev.h" cpp_reader *parse_in; /* Declared in c-pragma.h. */ /* We let tm.h override the types used here, to handle trivial differences such as the choice of unsigned int or long unsigned int for size_t. When machines start needing nontrivial differences in the size type, it would be best to do something here to figure out automatically from other information what type to use. */ #ifndef SIZE_TYPE #define SIZE_TYPE "long unsigned int" #endif #ifndef WCHAR_TYPE #define WCHAR_TYPE "int" #endif /* WCHAR_TYPE gets overridden by -fshort-wchar. */ #define MODIFIED_WCHAR_TYPE \ (flag_short_wchar ? "short unsigned int" : WCHAR_TYPE) #ifndef PTRDIFF_TYPE #define PTRDIFF_TYPE "long int" #endif #ifndef WINT_TYPE #define WINT_TYPE "unsigned int" #endif #ifndef INTMAX_TYPE #define INTMAX_TYPE ((INT_TYPE_SIZE == LONG_LONG_TYPE_SIZE) \ ? "int" \ : ((LONG_TYPE_SIZE == LONG_LONG_TYPE_SIZE) \ ? "long int" \ : "long long int")) #endif #ifndef UINTMAX_TYPE #define UINTMAX_TYPE ((INT_TYPE_SIZE == LONG_LONG_TYPE_SIZE) \ ? "unsigned int" \ : ((LONG_TYPE_SIZE == LONG_LONG_TYPE_SIZE) \ ? "long unsigned int" \ : "long long unsigned int")) #endif /* The following symbols are subsumed in the c_global_trees array, and listed here individually for documentation purposes. INTEGER_TYPE and REAL_TYPE nodes for the standard data types. tree short_integer_type_node; tree long_integer_type_node; tree long_long_integer_type_node; tree short_unsigned_type_node; tree long_unsigned_type_node; tree long_long_unsigned_type_node; tree truthvalue_type_node; tree truthvalue_false_node; tree truthvalue_true_node; tree ptrdiff_type_node; tree unsigned_char_type_node; tree signed_char_type_node; tree wchar_type_node; tree signed_wchar_type_node; tree unsigned_wchar_type_node; tree float_type_node; tree double_type_node; tree long_double_type_node; tree complex_integer_type_node; tree complex_float_type_node; tree complex_double_type_node; tree complex_long_double_type_node; tree intQI_type_node; tree intHI_type_node; tree intSI_type_node; tree intDI_type_node; tree intTI_type_node; tree unsigned_intQI_type_node; tree unsigned_intHI_type_node; tree unsigned_intSI_type_node; tree unsigned_intDI_type_node; tree unsigned_intTI_type_node; tree widest_integer_literal_type_node; tree widest_unsigned_literal_type_node; Nodes for types `void *' and `const void *'. tree ptr_type_node, const_ptr_type_node; Nodes for types `char *' and `const char *'. tree string_type_node, const_string_type_node; Type `char[SOMENUMBER]'. Used when an array of char is needed and the size is irrelevant. tree char_array_type_node; Type `int[SOMENUMBER]' or something like it. Used when an array of int needed and the size is irrelevant. tree int_array_type_node; Type `wchar_t[SOMENUMBER]' or something like it. Used when a wide string literal is created. tree wchar_array_type_node; Type `int ()' -- used for implicit declaration of functions. tree default_function_type; A VOID_TYPE node, packaged in a TREE_LIST. tree void_list_node; The lazily created VAR_DECLs for __FUNCTION__, __PRETTY_FUNCTION__, and __func__. (C doesn't generate __FUNCTION__ and__PRETTY_FUNCTION__ VAR_DECLS, but C++ does.) tree function_name_decl_node; tree pretty_function_name_decl_node; tree c99_function_name_decl_node; Stack of nested function name VAR_DECLs. tree saved_function_name_decls; */ tree c_global_trees[CTI_MAX]; /* TRUE if a code represents a statement. The front end init langhook should take care of initialization of this array. */ bool statement_code_p[MAX_TREE_CODES]; /* Switches common to the C front ends. */ /* Nonzero if prepreprocessing only. */ int flag_preprocess_only; /* Nonzero means don't output line number information. */ char flag_no_line_commands; /* Nonzero causes -E output not to be done, but directives such as #define that have side effects are still obeyed. */ char flag_no_output; /* Nonzero means dump macros in some fashion. */ char flag_dump_macros; /* Nonzero means pass #include lines through to the output. */ char flag_dump_includes; /* The file name to which we should write a precompiled header, or NULL if no header will be written in this compile. */ const char *pch_file; /* Nonzero if an ISO standard was selected. It rejects macros in the user's namespace. */ int flag_iso; /* Nonzero if -undef was given. It suppresses target built-in macros and assertions. */ int flag_undef; /* Nonzero means don't recognize the non-ANSI builtin functions. */ int flag_no_builtin; /* Nonzero means don't recognize the non-ANSI builtin functions. -ansi sets this. */ int flag_no_nonansi_builtin; /* Nonzero means give `double' the same size as `float'. */ int flag_short_double; /* Nonzero means give `wchar_t' the same size as `short'. */ int flag_short_wchar; /* Nonzero means allow Microsoft extensions without warnings or errors. */ int flag_ms_extensions; /* Nonzero means don't recognize the keyword `asm'. */ int flag_no_asm; /* Nonzero means give string constants the type `const char *', as mandated by the standard. */ int flag_const_strings; /* Nonzero means to treat bitfields as signed unless they say `unsigned'. */ int flag_signed_bitfields = 1; int explicit_flag_signed_bitfields; /* Nonzero means warn about pointer casts that can drop a type qualifier from the pointer target type. */ int warn_cast_qual; /* Warn about functions which might be candidates for format attributes. */ int warn_missing_format_attribute; /* Nonzero means warn about sizeof(function) or addition/subtraction of function pointers. */ int warn_pointer_arith; /* Nonzero means do not warn that K&R style main() is not a function prototype. */ int flag_bsd_no_warn_kr_main; /* Nonzero means warn for any global function def without separate previous prototype decl. */ int warn_missing_prototypes; /* Warn if adding () is suggested. */ int warn_parentheses; /* Warn if initializer is not completely bracketed. */ int warn_missing_braces; /* Warn about comparison of signed and unsigned values. If -1, neither -Wsign-compare nor -Wno-sign-compare has been specified (in which case -Wextra gets to decide). */ int warn_sign_compare = -1; /* Nonzero means warn about usage of long long when `-pedantic'. */ int warn_long_long = 1; /* Nonzero means warn about deprecated conversion from string constant to `char *'. */ int warn_write_strings; /* Nonzero means warn about multiple (redundant) decls for the same single variable or function. */ int warn_redundant_decls; /* Warn about testing equality of floating point numbers. */ int warn_float_equal; /* Warn about a subscript that has type char. */ int warn_char_subscripts; /* Warn if a type conversion is done that might have confusing results. */ int warn_conversion; /* Warn about #pragma directives that are not recognized. */ int warn_unknown_pragmas; /* Tri state variable. */ /* Warn about format/argument anomalies in calls to formatted I/O functions (*printf, *scanf, strftime, strfmon, etc.). */ int warn_format; /* Warn about Y2K problems with strftime formats. */ int warn_format_y2k; /* Warn about excess arguments to formats. */ int warn_format_extra_args; /* Warn about zero-length formats. */ int warn_format_zero_length; /* Warn about non-literal format arguments. */ int warn_format_nonliteral; /* Warn about possible security problems with calls to format functions. */ int warn_format_security; /* Zero means that faster, ...NonNil variants of objc_msgSend... calls will be used in ObjC; passing nil receivers to such calls will most likely result in crashes. */ int flag_nil_receivers = 1; /* Nonzero means that we will allow new ObjC exception syntax (@throw, @try, etc.) in source code. */ int flag_objc_exceptions = 0; /* Nonzero means that code generation will be altered to support "zero-link" execution. This currently affects ObjC only, but may affect other languages in the future. */ int flag_zero_link = 0; /* Nonzero means emit an '__OBJC, __image_info' for the current translation unit. It will inform the ObjC runtime that class definition(s) herein contained are to replace one(s) previously loaded. */ int flag_replace_objc_classes = 0; /* C/ObjC language option variables. */ /* Nonzero means message about use of implicit function declarations; 1 means warning; 2 means error. */ int mesg_implicit_function_declaration = -1; /* Nonzero means allow type mismatches in conditional expressions; just make their values `void'. */ int flag_cond_mismatch; /* Nonzero means enable C89 Amendment 1 features. */ int flag_isoc94; /* Nonzero means use the ISO C99 dialect of C. */ int flag_isoc99; /* Nonzero means allow the BSD kernel printf enhancements. */ int flag_bsd_format; /* Nonzero means that we have builtin functions, and main is an int. */ int flag_hosted = 1; /* Nonzero means warn when casting a function call to a type that does not match the return type (e.g. (float)sqrt() or (anything*)malloc() when there is no previous declaration of sqrt or malloc. */ int warn_bad_function_cast; /* Warn about traditional constructs whose meanings changed in ANSI C. */ int warn_traditional; /* Nonzero means warn for a declaration found after a statement. */ int warn_declaration_after_statement; /* Nonzero means warn for non-prototype function decls or non-prototyped defs without previous prototype. */ int warn_strict_prototypes; /* Nonzero means warn for any global function def without separate previous decl. */ int warn_missing_declarations; /* Nonzero means warn about declarations of objects not at file-scope level and about *all* declarations of functions (whether or static) not at file-scope level. Note that we exclude implicit function declarations. To get warnings about those, use -Wimplicit. */ int warn_nested_externs; /* Warn if main is suspicious. */ int warn_main; /* Nonzero means warn about possible violations of sequence point rules. */ int warn_sequence_point; /* Nonzero means warn about uninitialized variable when it is initialized with itself. For example: int i = i;, GCC will not warn about this when warn_init_self is nonzero. */ int warn_init_self; /* Nonzero means to warn about compile-time division by zero. */ int warn_div_by_zero = 1; /* Nonzero means warn about use of implicit int. */ int warn_implicit_int; /* Warn about NULL being passed to argument slots marked as requiring non-NULL. */ int warn_nonnull; /* Warn about old-style parameter declaration. */ int warn_old_style_definition; /* ObjC language option variables. */ /* Open and close the file for outputting class declarations, if requested (ObjC). */ int flag_gen_declaration; /* Generate code for GNU or NeXT runtime environment. */ #ifdef NEXT_OBJC_RUNTIME int flag_next_runtime = 1; #else int flag_next_runtime = 0; #endif /* Tells the compiler that this is a special run. Do not perform any compiling, instead we are to test some platform dependent features and output a C header file with appropriate definitions. */ int print_struct_values; /* ???. Undocumented. */ const char *constant_string_class_name; /* Warn if multiple methods are seen for the same selector, but with different argument types. Performs the check on the whole selector table at the end of compilation. */ int warn_selector; /* Warn if a @selector() is found, and no method with that selector has been previously declared. The check is done on each @selector() as soon as it is found - so it warns about forward declarations. */ int warn_undeclared_selector; /* Warn if methods required by a protocol are not implemented in the class adopting it. When turned off, methods inherited to that class are also considered implemented. */ int warn_protocol = 1; /* C++ language option variables. */ /* Nonzero means don't recognize any extension keywords. */ int flag_no_gnu_keywords; /* Nonzero means do emit exported implementations of functions even if they can be inlined. */ int flag_implement_inlines = 1; /* Nonzero means that implicit instantiations will be emitted if needed. */ int flag_implicit_templates = 1; /* Nonzero means that implicit instantiations of inline templates will be emitted if needed, even if instantiations of non-inline templates aren't. */ int flag_implicit_inline_templates = 1; /* Nonzero means generate separate instantiation control files and juggle them at link time. */ int flag_use_repository; /* Nonzero if we want to issue diagnostics that the standard says are not required. */ int flag_optional_diags = 1; /* Nonzero means we should attempt to elide constructors when possible. */ int flag_elide_constructors = 1; /* Nonzero means that member functions defined in class scope are inline by default. */ int flag_default_inline = 1; /* Controls whether compiler generates 'type descriptor' that give run-time type information. */ int flag_rtti = 1; /* Nonzero if we want to conserve space in the .o files. We do this by putting uninitialized data and runtime initialized data into .common instead of .data at the expense of not flagging multiple definitions. */ int flag_conserve_space; /* Nonzero if we want to obey access control semantics. */ int flag_access_control = 1; /* Nonzero if we want to check the return value of new and avoid calling constructors if it is a null pointer. */ int flag_check_new; /* Nonzero if we want the new ISO rules for pushing a new scope for `for' initialization variables. 0: Old rules, set by -fno-for-scope. 2: New ISO rules, set by -ffor-scope. 1: Try to implement new ISO rules, but with backup compatibility (and warnings). This is the default, for now. */ int flag_new_for_scope = 1; /* Nonzero if we want to emit defined symbols with common-like linkage as weak symbols where possible, in order to conform to C++ semantics. Otherwise, emit them as local symbols. */ int flag_weak = 1; /* 0 means we want the preprocessor to not emit line directives for the current working directory. 1 means we want it to do it. -1 means we should decide depending on whether debugging information is being emitted or not. */ int flag_working_directory = -1; /* Nonzero to use __cxa_atexit, rather than atexit, to register destructors for local statics and global objects. */ int flag_use_cxa_atexit = DEFAULT_USE_CXA_ATEXIT; /* Nonzero means make the default pedwarns warnings instead of errors. The value of this flag is ignored if -pedantic is specified. */ int flag_permissive; /* Nonzero means to implement standard semantics for exception specifications, calling unexpected if an exception is thrown that doesn't match the specification. Zero means to treat them as assertions and optimize accordingly, but not check them. */ int flag_enforce_eh_specs = 1; /* Nonzero means warn about things that will change when compiling with an ABI-compliant compiler. */ int warn_abi = 0; /* Nonzero means warn about invalid uses of offsetof. */ int warn_invalid_offsetof = 1; /* Nonzero means warn about implicit declarations. */ int warn_implicit = 1; /* Nonzero means warn when all ctors or dtors are private, and the class has no friends. */ int warn_ctor_dtor_privacy = 0; /* Nonzero means warn in function declared in derived class has the same name as a virtual in the base class, but fails to match the type signature of any virtual function in the base class. */ int warn_overloaded_virtual; /* Nonzero means warn when declaring a class that has a non virtual destructor, when it really ought to have a virtual one. */ int warn_nonvdtor; /* Nonzero means warn when the compiler will reorder code. */ int warn_reorder; /* Nonzero means warn when synthesis behavior differs from Cfront's. */ int warn_synth; /* Nonzero means warn when we convert a pointer to member function into a pointer to (void or function). */ int warn_pmf2ptr = 1; /* Nonzero means warn about violation of some Effective C++ style rules. */ int warn_ecpp; /* Nonzero means warn where overload resolution chooses a promotion from unsigned to signed over a conversion to an unsigned of the same size. */ int warn_sign_promo; /* Nonzero means warn when an old-style cast is used. */ int warn_old_style_cast; /* Nonzero means warn when non-templatized friend functions are declared within a template */ int warn_nontemplate_friend = 1; /* Nonzero means complain about deprecated features. */ int warn_deprecated = 1; /* Maximum template instantiation depth. This limit is rather arbitrary, but it exists to limit the time it takes to notice infinite template instantiations. */ int max_tinst_depth = 500; /* The elements of `ridpointers' are identifier nodes for the reserved type names and storage classes. It is indexed by a RID_... value. */ tree *ridpointers; tree (*make_fname_decl) (tree, int); /* If non-NULL, the address of a language-specific function that takes any action required right before expand_function_end is called. */ void (*lang_expand_function_end) (void); /* Nonzero means the expression being parsed will never be evaluated. This is a count, since unevaluated expressions can nest. */ int skip_evaluation; /* Information about how a function name is generated. */ struct fname_var_t { tree *const decl; /* pointer to the VAR_DECL. */ const unsigned rid; /* RID number for the identifier. */ const int pretty; /* How pretty is it? */ }; /* The three ways of getting then name of the current function. */ const struct fname_var_t fname_vars[] = { /* C99 compliant __func__, must be first. */ {&c99_function_name_decl_node, RID_C99_FUNCTION_NAME, 0}, /* GCC __FUNCTION__ compliant. */ {&function_name_decl_node, RID_FUNCTION_NAME, 0}, /* GCC __PRETTY_FUNCTION__ compliant. */ {&pretty_function_name_decl_node, RID_PRETTY_FUNCTION_NAME, 1}, {NULL, 0, 0}, }; static int constant_fits_type_p (tree, tree); /* Keep a stack of if statements. We record the number of compound statements seen up to the if keyword, as well as the line number and file of the if. If a potentially ambiguous else is seen, that fact is recorded; the warning is issued when we can be sure that the enclosing if statement does not have an else branch. */ typedef struct { int compstmt_count; location_t locus; int needs_warning; tree if_stmt; } if_elt; static if_elt *if_stack; /* Amount of space in the if statement stack. */ static int if_stack_space = 0; /* Stack pointer. */ static int if_stack_pointer = 0; static tree handle_packed_attribute (tree *, tree, tree, int, bool *); static tree handle_nocommon_attribute (tree *, tree, tree, int, bool *); static tree handle_common_attribute (tree *, tree, tree, int, bool *); static tree handle_noreturn_attribute (tree *, tree, tree, int, bool *); static tree handle_noinline_attribute (tree *, tree, tree, int, bool *); static tree handle_always_inline_attribute (tree *, tree, tree, int, bool *); static tree handle_used_attribute (tree *, tree, tree, int, bool *); static tree handle_unused_attribute (tree *, tree, tree, int, bool *); static tree handle_const_attribute (tree *, tree, tree, int, bool *); static tree handle_transparent_union_attribute (tree *, tree, tree, int, bool *); static tree handle_constructor_attribute (tree *, tree, tree, int, bool *); static tree handle_destructor_attribute (tree *, tree, tree, int, bool *); static tree handle_mode_attribute (tree *, tree, tree, int, bool *); static tree handle_section_attribute (tree *, tree, tree, int, bool *); static tree handle_aligned_attribute (tree *, tree, tree, int, bool *); static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ; static tree handle_alias_attribute (tree *, tree, tree, int, bool *); static tree handle_visibility_attribute (tree *, tree, tree, int, bool *); static tree handle_tls_model_attribute (tree *, tree, tree, int, bool *); static tree handle_no_instrument_function_attribute (tree *, tree, tree, int, bool *); static tree handle_malloc_attribute (tree *, tree, tree, int, bool *); static tree handle_no_limit_stack_attribute (tree *, tree, tree, int, bool *); static tree handle_pure_attribute (tree *, tree, tree, int, bool *); static tree handle_deprecated_attribute (tree *, tree, tree, int, bool *); static tree handle_vector_size_attribute (tree *, tree, tree, int, bool *); static tree handle_nonnull_attribute (tree *, tree, tree, int, bool *); static tree handle_nothrow_attribute (tree *, tree, tree, int, bool *); static tree handle_cleanup_attribute (tree *, tree, tree, int, bool *); static tree handle_warn_unused_result_attribute (tree *, tree, tree, int, bool *); static void check_function_nonnull (tree, tree); static void check_nonnull_arg (void *, tree, unsigned HOST_WIDE_INT); static bool nonnull_check_p (tree, unsigned HOST_WIDE_INT); static bool get_nonnull_operand (tree, unsigned HOST_WIDE_INT *); static int resort_field_decl_cmp (const void *, const void *); /* Table of machine-independent attributes common to all C-like languages. */ const struct attribute_spec c_common_attribute_table[] = { /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ { "packed", 0, 0, false, false, false, handle_packed_attribute }, { "nocommon", 0, 0, true, false, false, handle_nocommon_attribute }, { "common", 0, 0, true, false, false, handle_common_attribute }, /* FIXME: logically, noreturn attributes should be listed as "false, true, true" and apply to function types. But implementing this would require all the places in the compiler that use TREE_THIS_VOLATILE on a decl to identify non-returning functions to be located and fixed to check the function type instead. */ { "noreturn", 0, 0, true, false, false, handle_noreturn_attribute }, { "volatile", 0, 0, true, false, false, handle_noreturn_attribute }, { "noinline", 0, 0, true, false, false, handle_noinline_attribute }, { "always_inline", 0, 0, true, false, false, handle_always_inline_attribute }, { "used", 0, 0, true, false, false, handle_used_attribute }, { "unused", 0, 0, false, false, false, handle_unused_attribute }, /* The same comments as for noreturn attributes apply to const ones. */ { "const", 0, 0, true, false, false, handle_const_attribute }, { "transparent_union", 0, 0, false, false, false, handle_transparent_union_attribute }, { "constructor", 0, 0, true, false, false, handle_constructor_attribute }, { "destructor", 0, 0, true, false, false, handle_destructor_attribute }, { "mode", 1, 1, false, true, false, handle_mode_attribute }, { "section", 1, 1, true, false, false, handle_section_attribute }, { "aligned", 0, 1, false, false, false, handle_aligned_attribute }, { "weak", 0, 0, true, false, false, handle_weak_attribute }, { "alias", 1, 1, true, false, false, handle_alias_attribute }, { "no_instrument_function", 0, 0, true, false, false, handle_no_instrument_function_attribute }, { "malloc", 0, 0, true, false, false, handle_malloc_attribute }, { "no_stack_limit", 0, 0, true, false, false, handle_no_limit_stack_attribute }, { "pure", 0, 0, true, false, false, handle_pure_attribute }, { "deprecated", 0, 0, false, false, false, handle_deprecated_attribute }, { "vector_size", 1, 1, false, true, false, handle_vector_size_attribute }, { "visibility", 1, 1, true, false, false, handle_visibility_attribute }, { "tls_model", 1, 1, true, false, false, handle_tls_model_attribute }, { "nonnull", 0, -1, false, true, true, handle_nonnull_attribute }, { "nothrow", 0, 0, true, false, false, handle_nothrow_attribute }, { "may_alias", 0, 0, false, true, false, NULL }, { "cleanup", 1, 1, true, false, false, handle_cleanup_attribute }, { "warn_unused_result", 0, 0, false, true, true, handle_warn_unused_result_attribute }, { NULL, 0, 0, false, false, false, NULL } }; /* Give the specifications for the format attributes, used by C and all descendants. */ const struct attribute_spec c_common_format_attribute_table[] = { /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ { "format", 3, 3, false, true, true, handle_format_attribute }, { "format_arg", 1, 1, false, true, true, handle_format_arg_attribute }, { NULL, 0, 0, false, false, false, NULL } }; /* Record the start of an if-then, and record the start of it for ambiguous else detection. COND is the condition for the if-then statement. IF_STMT is the statement node that has already been created for this if-then statement. It is created before parsing the condition to keep line number information accurate. */ void c_expand_start_cond (tree cond, int compstmt_count, tree if_stmt) { /* Make sure there is enough space on the stack. */ if (if_stack_space == 0) { if_stack_space = 10; if_stack = xmalloc (10 * sizeof (if_elt)); } else if (if_stack_space == if_stack_pointer) { if_stack_space += 10; if_stack = xrealloc (if_stack, if_stack_space * sizeof (if_elt)); } IF_COND (if_stmt) = cond; add_stmt (if_stmt); /* Record this if statement. */ if_stack[if_stack_pointer].compstmt_count = compstmt_count; if_stack[if_stack_pointer].locus = input_location; if_stack[if_stack_pointer].needs_warning = 0; if_stack[if_stack_pointer].if_stmt = if_stmt; if_stack_pointer++; } /* Called after the then-clause for an if-statement is processed. */ void c_finish_then (void) { tree if_stmt = if_stack[if_stack_pointer - 1].if_stmt; RECHAIN_STMTS (if_stmt, THEN_CLAUSE (if_stmt)); } /* Record the end of an if-then. Optionally warn if a nested if statement had an ambiguous else clause. */ void c_expand_end_cond (void) { if_stack_pointer--; if (if_stack[if_stack_pointer].needs_warning) warning ("%Hsuggest explicit braces to avoid ambiguous `else'", &if_stack[if_stack_pointer].locus); last_expr_type = NULL_TREE; } /* Called between the then-clause and the else-clause of an if-then-else. */ void c_expand_start_else (void) { /* An ambiguous else warning must be generated for the enclosing if statement, unless we see an else branch for that one, too. */ if (warn_parentheses && if_stack_pointer > 1 && (if_stack[if_stack_pointer - 1].compstmt_count == if_stack[if_stack_pointer - 2].compstmt_count)) if_stack[if_stack_pointer - 2].needs_warning = 1; /* Even if a nested if statement had an else branch, it can't be ambiguous if this one also has an else. So don't warn in that case. Also don't warn for any if statements nested in this else. */ if_stack[if_stack_pointer - 1].needs_warning = 0; if_stack[if_stack_pointer - 1].compstmt_count--; } /* Called after the else-clause for an if-statement is processed. */ void c_finish_else (void) { tree if_stmt = if_stack[if_stack_pointer - 1].if_stmt; RECHAIN_STMTS (if_stmt, ELSE_CLAUSE (if_stmt)); } /* Begin an if-statement. Returns a newly created IF_STMT if appropriate. Unlike the C++ front-end, we do not call add_stmt here; it is probably safe to do so, but I am not very familiar with this code so I am being extra careful not to change its behavior beyond what is strictly necessary for correctness. */ tree c_begin_if_stmt (void) { tree r; r = build_stmt (IF_STMT, NULL_TREE, NULL_TREE, NULL_TREE); return r; } /* Begin a while statement. Returns a newly created WHILE_STMT if appropriate. Unlike the C++ front-end, we do not call add_stmt here; it is probably safe to do so, but I am not very familiar with this code so I am being extra careful not to change its behavior beyond what is strictly necessary for correctness. */ tree c_begin_while_stmt (void) { tree r; r = build_stmt (WHILE_STMT, NULL_TREE, NULL_TREE); return r; } void c_finish_while_stmt_cond (tree cond, tree while_stmt) { WHILE_COND (while_stmt) = cond; } /* Push current bindings for the function name VAR_DECLS. */ void start_fname_decls (void) { unsigned ix; tree saved = NULL_TREE; for (ix = 0; fname_vars[ix].decl; ix++) { tree decl = *fname_vars[ix].decl; if (decl) { saved = tree_cons (decl, build_int_2 (ix, 0), saved); *fname_vars[ix].decl = NULL_TREE; } } if (saved || saved_function_name_decls) /* Normally they'll have been NULL, so only push if we've got a stack, or they are non-NULL. */ saved_function_name_decls = tree_cons (saved, NULL_TREE, saved_function_name_decls); } /* Finish up the current bindings, adding them into the current function's statement tree. This is done by wrapping the function's body in a COMPOUND_STMT containing these decls too. This must be done _before_ finish_stmt_tree is called. If there is no current function, we must be at file scope and no statements are involved. Pop the previous bindings. */ void finish_fname_decls (void) { unsigned ix; tree body = NULL_TREE; tree stack = saved_function_name_decls; for (; stack && TREE_VALUE (stack); stack = TREE_CHAIN (stack)) body = chainon (TREE_VALUE (stack), body); if (body) { /* They were called into existence, so add to statement tree. Add the DECL_STMTs inside the outermost scope. */ tree *p = &DECL_SAVED_TREE (current_function_decl); /* Skip the dummy EXPR_STMT and any EH_SPEC_BLOCK. */ while (TREE_CODE (*p) != COMPOUND_STMT) { if (TREE_CODE (*p) == EXPR_STMT) p = &TREE_CHAIN (*p); else p = &TREE_OPERAND(*p, 0); } p = &COMPOUND_BODY (*p); if (TREE_CODE (*p) == SCOPE_STMT) p = &TREE_CHAIN (*p); body = chainon (body, *p); *p = body; } for (ix = 0; fname_vars[ix].decl; ix++) *fname_vars[ix].decl = NULL_TREE; if (stack) { /* We had saved values, restore them. */ tree saved; for (saved = TREE_PURPOSE (stack); saved; saved = TREE_CHAIN (saved)) { tree decl = TREE_PURPOSE (saved); unsigned ix = TREE_INT_CST_LOW (TREE_VALUE (saved)); *fname_vars[ix].decl = decl; } stack = TREE_CHAIN (stack); } saved_function_name_decls = stack; } /* Return the text name of the current function, suitably prettified by PRETTY_P. */ const char * fname_as_string (int pretty_p) { const char *name = "top level"; int vrb = 2; if (! pretty_p) { name = ""; vrb = 0; } if (current_function_decl) name = (*lang_hooks.decl_printable_name) (current_function_decl, vrb); return name; } /* Return the VAR_DECL for a const char array naming the current function. If the VAR_DECL has not yet been created, create it now. RID indicates how it should be formatted and IDENTIFIER_NODE ID is its name (unfortunately C and C++ hold the RID values of keywords in different places, so we can't derive RID from ID in this language independent code. */ tree fname_decl (unsigned int rid, tree id) { unsigned ix; tree decl = NULL_TREE; for (ix = 0; fname_vars[ix].decl; ix++) if (fname_vars[ix].rid == rid) break; decl = *fname_vars[ix].decl; if (!decl) { tree saved_last_tree = last_tree; /* If a tree is built here, it would normally have the lineno of the current statement. Later this tree will be moved to the beginning of the function and this line number will be wrong. To avoid this problem set the lineno to 0 here; that prevents it from appearing in the RTL. */ int saved_lineno = input_line; input_line = 0; decl = (*make_fname_decl) (id, fname_vars[ix].pretty); if (last_tree != saved_last_tree) { /* We created some statement tree for the decl. This belongs at the start of the function, so remove it now and reinsert it after the function is complete. */ tree stmts = TREE_CHAIN (saved_last_tree); TREE_CHAIN (saved_last_tree) = NULL_TREE; last_tree = saved_last_tree; saved_function_name_decls = tree_cons (decl, stmts, saved_function_name_decls); } *fname_vars[ix].decl = decl; input_line = saved_lineno; } if (!ix && !current_function_decl) pedwarn ("'%D' is not defined outside of function scope", decl); return decl; } /* Given a STRING_CST, give it a suitable array-of-chars data type. */ tree fix_string_type (tree value) { const int wchar_bytes = TYPE_PRECISION (wchar_type_node) / BITS_PER_UNIT; const int wide_flag = TREE_TYPE (value) == wchar_array_type_node; const int nchars_max = flag_isoc99 ? 4095 : 509; int length = TREE_STRING_LENGTH (value); int nchars; /* Compute the number of elements, for the array type. */ nchars = wide_flag ? length / wchar_bytes : length; if (pedantic && nchars - 1 > nchars_max && !c_dialect_cxx ()) pedwarn ("string length `%d' is greater than the length `%d' ISO C%d compilers are required to support", nchars - 1, nchars_max, flag_isoc99 ? 99 : 89); /* Create the array type for the string constant. -Wwrite-strings says make the string constant an array of const char so that copying it to a non-const pointer will get a warning. For C++, this is the standard behavior. */ if (flag_const_strings && ! flag_writable_strings) { tree elements = build_type_variant (wide_flag ? wchar_type_node : char_type_node, 1, 0); TREE_TYPE (value) = build_array_type (elements, build_index_type (build_int_2 (nchars - 1, 0))); } else TREE_TYPE (value) = build_array_type (wide_flag ? wchar_type_node : char_type_node, build_index_type (build_int_2 (nchars - 1, 0))); TREE_CONSTANT (value) = 1; TREE_READONLY (value) = ! flag_writable_strings; TREE_STATIC (value) = 1; return value; } /* Print a warning if a constant expression had overflow in folding. Invoke this function on every expression that the language requires to be a constant expression. Note the ANSI C standard says it is erroneous for a constant expression to overflow. */ void constant_expression_warning (tree value) { if ((TREE_CODE (value) == INTEGER_CST || TREE_CODE (value) == REAL_CST || TREE_CODE (value) == VECTOR_CST || TREE_CODE (value) == COMPLEX_CST) && TREE_CONSTANT_OVERFLOW (value) && pedantic) pedwarn ("overflow in constant expression"); } /* Print a warning if an expression had overflow in folding. Invoke this function on every expression that (1) appears in the source code, and (2) might be a constant expression that overflowed, and (3) is not already checked by convert_and_check; however, do not invoke this function on operands of explicit casts. */ void overflow_warning (tree value) { if ((TREE_CODE (value) == INTEGER_CST || (TREE_CODE (value) == COMPLEX_CST && TREE_CODE (TREE_REALPART (value)) == INTEGER_CST)) && TREE_OVERFLOW (value)) { TREE_OVERFLOW (value) = 0; if (skip_evaluation == 0) warning ("integer overflow in expression"); } else if ((TREE_CODE (value) == REAL_CST || (TREE_CODE (value) == COMPLEX_CST && TREE_CODE (TREE_REALPART (value)) == REAL_CST)) && TREE_OVERFLOW (value)) { TREE_OVERFLOW (value) = 0; if (skip_evaluation == 0) warning ("floating point overflow in expression"); } else if (TREE_CODE (value) == VECTOR_CST && TREE_OVERFLOW (value)) { TREE_OVERFLOW (value) = 0; if (skip_evaluation == 0) warning ("vector overflow in expression"); } } /* Print a warning if a large constant is truncated to unsigned, or if -Wconversion is used and a constant < 0 is converted to unsigned. Invoke this function on every expression that might be implicitly converted to an unsigned type. */ void unsigned_conversion_warning (tree result, tree operand) { tree type = TREE_TYPE (result); if (TREE_CODE (operand) == INTEGER_CST && TREE_CODE (type) == INTEGER_TYPE && TREE_UNSIGNED (type) && skip_evaluation == 0 && !int_fits_type_p (operand, type)) { if (!int_fits_type_p (operand, c_common_signed_type (type))) /* This detects cases like converting -129 or 256 to unsigned char. */ warning ("large integer implicitly truncated to unsigned type"); else if (warn_conversion) warning ("negative integer implicitly converted to unsigned type"); } } /* Nonzero if constant C has a value that is permissible for type TYPE (an INTEGER_TYPE). */ static int constant_fits_type_p (tree c, tree type) { if (TREE_CODE (c) == INTEGER_CST) return int_fits_type_p (c, type); c = convert (type, c); return !TREE_OVERFLOW (c); } +/* Nonzero if vector types T1 and T2 can be converted to each other + without an explicit cast. */ +int +vector_types_convertible_p (tree t1, tree t2) +{ + return targetm.vector_opaque_p (t1) + || targetm.vector_opaque_p (t2) + || (tree_int_cst_equal (TYPE_SIZE (t1), TYPE_SIZE (t2)) + && INTEGRAL_TYPE_P (TREE_TYPE (t1)) + == INTEGRAL_TYPE_P (TREE_TYPE (t2))); +} + /* Convert EXPR to TYPE, warning about conversion problems with constants. Invoke this function on every expression that is converted implicitly, i.e. because of language rules and not because of an explicit cast. */ tree convert_and_check (tree type, tree expr) { tree t = convert (type, expr); if (TREE_CODE (t) == INTEGER_CST) { if (TREE_OVERFLOW (t)) { TREE_OVERFLOW (t) = 0; /* Do not diagnose overflow in a constant expression merely because a conversion overflowed. */ TREE_CONSTANT_OVERFLOW (t) = TREE_CONSTANT_OVERFLOW (expr); /* No warning for converting 0x80000000 to int. */ if (!(TREE_UNSIGNED (type) < TREE_UNSIGNED (TREE_TYPE (expr)) && TREE_CODE (TREE_TYPE (expr)) == INTEGER_TYPE && TYPE_PRECISION (type) == TYPE_PRECISION (TREE_TYPE (expr)))) /* If EXPR fits in the unsigned version of TYPE, don't warn unless pedantic. */ if ((pedantic || TREE_UNSIGNED (type) || ! constant_fits_type_p (expr, c_common_unsigned_type (type))) && skip_evaluation == 0) warning ("overflow in implicit constant conversion"); } else unsigned_conversion_warning (t, expr); } return t; } /* A node in a list that describes references to variables (EXPR), which are either read accesses if WRITER is zero, or write accesses, in which case WRITER is the parent of EXPR. */ struct tlist { struct tlist *next; tree expr, writer; }; /* Used to implement a cache the results of a call to verify_tree. We only use this for SAVE_EXPRs. */ struct tlist_cache { struct tlist_cache *next; struct tlist *cache_before_sp; struct tlist *cache_after_sp; tree expr; }; /* Obstack to use when allocating tlist structures, and corresponding firstobj. */ static struct obstack tlist_obstack; static char *tlist_firstobj = 0; /* Keep track of the identifiers we've warned about, so we can avoid duplicate warnings. */ static struct tlist *warned_ids; /* SAVE_EXPRs need special treatment. We process them only once and then cache the results. */ static struct tlist_cache *save_expr_cache; static void add_tlist (struct tlist **, struct tlist *, tree, int); static void merge_tlist (struct tlist **, struct tlist *, int); static void verify_tree (tree, struct tlist **, struct tlist **, tree); static int warning_candidate_p (tree); static void warn_for_collisions (struct tlist *); static void warn_for_collisions_1 (tree, tree, struct tlist *, int); static struct tlist *new_tlist (struct tlist *, tree, tree); static void verify_sequence_points (tree); /* Create a new struct tlist and fill in its fields. */ static struct tlist * new_tlist (struct tlist *next, tree t, tree writer) { struct tlist *l; l = obstack_alloc (&tlist_obstack, sizeof *l); l->next = next; l->expr = t; l->writer = writer; return l; } /* Add duplicates of the nodes found in ADD to the list *TO. If EXCLUDE_WRITER is nonnull, we ignore any node we find which has a writer equal to it. */ static void add_tlist (struct tlist **to, struct tlist *add, tree exclude_writer, int copy) { while (add) { struct tlist *next = add->next; if (! copy) add->next = *to; if (! exclude_writer || add->writer != exclude_writer) *to = copy ? new_tlist (*to, add->expr, add->writer) : add; add = next; } } /* Merge the nodes of ADD into TO. This merging process is done so that for each variable that already exists in TO, no new node is added; however if there is a write access recorded in ADD, and an occurrence on TO is only a read access, then the occurrence in TO will be modified to record the write. */ static void merge_tlist (struct tlist **to, struct tlist *add, int copy) { struct tlist **end = to; while (*end) end = &(*end)->next; while (add) { int found = 0; struct tlist *tmp2; struct tlist *next = add->next; for (tmp2 = *to; tmp2; tmp2 = tmp2->next) if (tmp2->expr == add->expr) { found = 1; if (! tmp2->writer) tmp2->writer = add->writer; } if (! found) { *end = copy ? add : new_tlist (NULL, add->expr, add->writer); end = &(*end)->next; *end = 0; } add = next; } } /* WRITTEN is a variable, WRITER is its parent. Warn if any of the variable references in list LIST conflict with it, excluding reads if ONLY writers is nonzero. */ static void warn_for_collisions_1 (tree written, tree writer, struct tlist *list, int only_writes) { struct tlist *tmp; /* Avoid duplicate warnings. */ for (tmp = warned_ids; tmp; tmp = tmp->next) if (tmp->expr == written) return; while (list) { if (list->expr == written && list->writer != writer && (! only_writes || list->writer)) { warned_ids = new_tlist (warned_ids, written, NULL_TREE); warning ("operation on `%s' may be undefined", IDENTIFIER_POINTER (DECL_NAME (list->expr))); } list = list->next; } } /* Given a list LIST of references to variables, find whether any of these can cause conflicts due to missing sequence points. */ static void warn_for_collisions (struct tlist *list) { struct tlist *tmp; for (tmp = list; tmp; tmp = tmp->next) { if (tmp->writer) warn_for_collisions_1 (tmp->expr, tmp->writer, list, 0); } } /* Return nonzero if X is a tree that can be verified by the sequence point warnings. */ static int warning_candidate_p (tree x) { return TREE_CODE (x) == VAR_DECL || TREE_CODE (x) == PARM_DECL; } /* Walk the tree X, and record accesses to variables. If X is written by the parent tree, WRITER is the parent. We store accesses in one of the two lists: PBEFORE_SP, and PNO_SP. If this expression or its only operand forces a sequence point, then everything up to the sequence point is stored in PBEFORE_SP. Everything else gets stored in PNO_SP. Once we return, we will have emitted warnings if any subexpression before such a sequence point could be undefined. On a higher level, however, the sequence point may not be relevant, and we'll merge the two lists. Example: (b++, a) + b; The call that processes the COMPOUND_EXPR will store the increment of B in PBEFORE_SP, and the use of A in PNO_SP. The higher-level call that processes the PLUS_EXPR will need to merge the two lists so that eventually, all accesses end up on the same list (and we'll warn about the unordered subexpressions b++ and b. A note on merging. If we modify the former example so that our expression becomes (b++, b) + a care must be taken not simply to add all three expressions into the final PNO_SP list. The function merge_tlist takes care of that by merging the before-SP list of the COMPOUND_EXPR into its after-SP list in a special way, so that no more than one access to B is recorded. */ static void verify_tree (tree x, struct tlist **pbefore_sp, struct tlist **pno_sp, tree writer) { struct tlist *tmp_before, *tmp_nosp, *tmp_list2, *tmp_list3; enum tree_code code; char class; /* X may be NULL if it is the operand of an empty statement expression ({ }). */ if (x == NULL) return; restart: code = TREE_CODE (x); class = TREE_CODE_CLASS (code); if (warning_candidate_p (x)) { *pno_sp = new_tlist (*pno_sp, x, writer); return; } switch (code) { case CONSTRUCTOR: return; case COMPOUND_EXPR: case TRUTH_ANDIF_EXPR: case TRUTH_ORIF_EXPR: tmp_before = tmp_nosp = tmp_list3 = 0; verify_tree (TREE_OPERAND (x, 0), &tmp_before, &tmp_nosp, NULL_TREE); warn_for_collisions (tmp_nosp); merge_tlist (pbefore_sp, tmp_before, 0); merge_tlist (pbefore_sp, tmp_nosp, 0); verify_tree (TREE_OPERAND (x, 1), &tmp_list3, pno_sp, NULL_TREE); merge_tlist (pbefore_sp, tmp_list3, 0); return; case COND_EXPR: tmp_before = tmp_list2 = 0; verify_tree (TREE_OPERAND (x, 0), &tmp_before, &tmp_list2, NULL_TREE); warn_for_collisions (tmp_list2); merge_tlist (pbefore_sp, tmp_before, 0); merge_tlist (pbefore_sp, tmp_list2, 1); tmp_list3 = tmp_nosp = 0; verify_tree (TREE_OPERAND (x, 1), &tmp_list3, &tmp_nosp, NULL_TREE); warn_for_collisions (tmp_nosp); merge_tlist (pbefore_sp, tmp_list3, 0); tmp_list3 = tmp_list2 = 0; verify_tree (TREE_OPERAND (x, 2), &tmp_list3, &tmp_list2, NULL_TREE); warn_for_collisions (tmp_list2); merge_tlist (pbefore_sp, tmp_list3, 0); /* Rather than add both tmp_nosp and tmp_list2, we have to merge the two first, to avoid warning for (a ? b++ : b++). */ merge_tlist (&tmp_nosp, tmp_list2, 0); add_tlist (pno_sp, tmp_nosp, NULL_TREE, 0); return; case PREDECREMENT_EXPR: case PREINCREMENT_EXPR: case POSTDECREMENT_EXPR: case POSTINCREMENT_EXPR: verify_tree (TREE_OPERAND (x, 0), pno_sp, pno_sp, x); return; case MODIFY_EXPR: tmp_before = tmp_nosp = tmp_list3 = 0; verify_tree (TREE_OPERAND (x, 1), &tmp_before, &tmp_nosp, NULL_TREE); verify_tree (TREE_OPERAND (x, 0), &tmp_list3, &tmp_list3, x); /* Expressions inside the LHS are not ordered wrt. the sequence points in the RHS. Example: *a = (a++, 2) Despite the fact that the modification of "a" is in the before_sp list (tmp_before), it conflicts with the use of "a" in the LHS. We can handle this by adding the contents of tmp_list3 to those of tmp_before, and redoing the collision warnings for that list. */ add_tlist (&tmp_before, tmp_list3, x, 1); warn_for_collisions (tmp_before); /* Exclude the LHS itself here; we first have to merge it into the tmp_nosp list. This is done to avoid warning for "a = a"; if we didn't exclude the LHS, we'd get it twice, once as a read and once as a write. */ add_tlist (pno_sp, tmp_list3, x, 0); warn_for_collisions_1 (TREE_OPERAND (x, 0), x, tmp_nosp, 1); merge_tlist (pbefore_sp, tmp_before, 0); if (warning_candidate_p (TREE_OPERAND (x, 0))) merge_tlist (&tmp_nosp, new_tlist (NULL, TREE_OPERAND (x, 0), x), 0); add_tlist (pno_sp, tmp_nosp, NULL_TREE, 1); return; case CALL_EXPR: /* We need to warn about conflicts among arguments and conflicts between args and the function address. Side effects of the function address, however, are not ordered by the sequence point of the call. */ tmp_before = tmp_nosp = tmp_list2 = tmp_list3 = 0; verify_tree (TREE_OPERAND (x, 0), &tmp_before, &tmp_nosp, NULL_TREE); if (TREE_OPERAND (x, 1)) verify_tree (TREE_OPERAND (x, 1), &tmp_list2, &tmp_list3, NULL_TREE); merge_tlist (&tmp_list3, tmp_list2, 0); add_tlist (&tmp_before, tmp_list3, NULL_TREE, 0); add_tlist (&tmp_before, tmp_nosp, NULL_TREE, 0); warn_for_collisions (tmp_before); add_tlist (pbefore_sp, tmp_before, NULL_TREE, 0); return; case TREE_LIST: /* Scan all the list, e.g. indices of multi dimensional array. */ while (x) { tmp_before = tmp_nosp = 0; verify_tree (TREE_VALUE (x), &tmp_before, &tmp_nosp, NULL_TREE); merge_tlist (&tmp_nosp, tmp_before, 0); add_tlist (pno_sp, tmp_nosp, NULL_TREE, 0); x = TREE_CHAIN (x); } return; case SAVE_EXPR: { struct tlist_cache *t; for (t = save_expr_cache; t; t = t->next) if (t->expr == x) break; if (! t) { t = obstack_alloc (&tlist_obstack, sizeof *t); t->next = save_expr_cache; t->expr = x; save_expr_cache = t; tmp_before = tmp_nosp = 0; verify_tree (TREE_OPERAND (x, 0), &tmp_before, &tmp_nosp, NULL_TREE); warn_for_collisions (tmp_nosp); tmp_list3 = 0; while (tmp_nosp) { struct tlist *t = tmp_nosp; tmp_nosp = t->next; merge_tlist (&tmp_list3, t, 0); } t->cache_before_sp = tmp_before; t->cache_after_sp = tmp_list3; } merge_tlist (pbefore_sp, t->cache_before_sp, 1); add_tlist (pno_sp, t->cache_after_sp, NULL_TREE, 1); return; } default: break; } if (class == '1') { if (first_rtl_op (code) == 0) return; x = TREE_OPERAND (x, 0); writer = 0; goto restart; } switch (class) { case 'r': case '<': case '2': case 'b': case 'e': case 's': case 'x': { int lp; int max = first_rtl_op (TREE_CODE (x)); for (lp = 0; lp < max; lp++) { tmp_before = tmp_nosp = 0; verify_tree (TREE_OPERAND (x, lp), &tmp_before, &tmp_nosp, NULL_TREE); merge_tlist (&tmp_nosp, tmp_before, 0); add_tlist (pno_sp, tmp_nosp, NULL_TREE, 0); } break; } } } /* Try to warn for undefined behavior in EXPR due to missing sequence points. */ static void verify_sequence_points (tree expr) { struct tlist *before_sp = 0, *after_sp = 0; warned_ids = 0; save_expr_cache = 0; if (tlist_firstobj == 0) { gcc_obstack_init (&tlist_obstack); tlist_firstobj = obstack_alloc (&tlist_obstack, 0); } verify_tree (expr, &before_sp, &after_sp, 0); warn_for_collisions (after_sp); obstack_free (&tlist_obstack, tlist_firstobj); } tree c_expand_expr_stmt (tree expr) { /* Do default conversion if safe and possibly important, in case within ({...}). */ if ((TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE && (flag_isoc99 || lvalue_p (expr))) || TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE) expr = default_conversion (expr); if (warn_sequence_point) verify_sequence_points (expr); if (TREE_TYPE (expr) != error_mark_node && !COMPLETE_OR_VOID_TYPE_P (TREE_TYPE (expr)) && TREE_CODE (TREE_TYPE (expr)) != ARRAY_TYPE) error ("expression statement has incomplete type"); last_expr_type = TREE_TYPE (expr); return add_stmt (build_stmt (EXPR_STMT, expr)); } /* Validate the expression after `case' and apply default promotions. */ tree check_case_value (tree value) { if (value == NULL_TREE) return value; /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */ STRIP_TYPE_NOPS (value); /* In C++, the following is allowed: const int i = 3; switch (...) { case i: ... } So, we try to reduce the VALUE to a constant that way. */ if (c_dialect_cxx ()) { value = decl_constant_value (value); STRIP_TYPE_NOPS (value); value = fold (value); } if (TREE_CODE (value) != INTEGER_CST && value != error_mark_node) { error ("case label does not reduce to an integer constant"); value = error_mark_node; } else /* Promote char or short to int. */ value = default_conversion (value); constant_expression_warning (value); return value; } /* Return an integer type with BITS bits of precision, that is unsigned if UNSIGNEDP is nonzero, otherwise signed. */ tree c_common_type_for_size (unsigned int bits, int unsignedp) { if (bits == TYPE_PRECISION (integer_type_node)) return unsignedp ? unsigned_type_node : integer_type_node; if (bits == TYPE_PRECISION (signed_char_type_node)) return unsignedp ? unsigned_char_type_node : signed_char_type_node; if (bits == TYPE_PRECISION (short_integer_type_node)) return unsignedp ? short_unsigned_type_node : short_integer_type_node; if (bits == TYPE_PRECISION (long_integer_type_node)) return unsignedp ? long_unsigned_type_node : long_integer_type_node; if (bits == TYPE_PRECISION (long_long_integer_type_node)) return (unsignedp ? long_long_unsigned_type_node : long_long_integer_type_node); if (bits == TYPE_PRECISION (widest_integer_literal_type_node)) return (unsignedp ? widest_unsigned_literal_type_node : widest_integer_literal_type_node); if (bits <= TYPE_PRECISION (intQI_type_node)) return unsignedp ? unsigned_intQI_type_node : intQI_type_node; if (bits <= TYPE_PRECISION (intHI_type_node)) return unsignedp ? unsigned_intHI_type_node : intHI_type_node; if (bits <= TYPE_PRECISION (intSI_type_node)) return unsignedp ? unsigned_intSI_type_node : intSI_type_node; if (bits <= TYPE_PRECISION (intDI_type_node)) return unsignedp ? unsigned_intDI_type_node : intDI_type_node; return 0; } /* Used for communication between c_common_type_for_mode and c_register_builtin_type. */ static GTY(()) tree registered_builtin_types; /* Return a data type that has machine mode MODE. If the mode is an integer, then UNSIGNEDP selects between signed and unsigned types. */ tree c_common_type_for_mode (enum machine_mode mode, int unsignedp) { tree t; if (mode == TYPE_MODE (integer_type_node)) return unsignedp ? unsigned_type_node : integer_type_node; if (mode == TYPE_MODE (signed_char_type_node)) return unsignedp ? unsigned_char_type_node : signed_char_type_node; if (mode == TYPE_MODE (short_integer_type_node)) return unsignedp ? short_unsigned_type_node : short_integer_type_node; if (mode == TYPE_MODE (long_integer_type_node)) return unsignedp ? long_unsigned_type_node : long_integer_type_node; if (mode == TYPE_MODE (long_long_integer_type_node)) return unsignedp ? long_long_unsigned_type_node : long_long_integer_type_node; if (mode == TYPE_MODE (widest_integer_literal_type_node)) return unsignedp ? widest_unsigned_literal_type_node : widest_integer_literal_type_node; if (mode == QImode) return unsignedp ? unsigned_intQI_type_node : intQI_type_node; if (mode == HImode) return unsignedp ? unsigned_intHI_type_node : intHI_type_node; if (mode == SImode) return unsignedp ? unsigned_intSI_type_node : intSI_type_node; if (mode == DImode) return unsignedp ? unsigned_intDI_type_node : intDI_type_node; #if HOST_BITS_PER_WIDE_INT >= 64 if (mode == TYPE_MODE (intTI_type_node)) return unsignedp ? unsigned_intTI_type_node : intTI_type_node; #endif if (mode == TYPE_MODE (float_type_node)) return float_type_node; if (mode == TYPE_MODE (double_type_node)) return double_type_node; if (mode == TYPE_MODE (long_double_type_node)) return long_double_type_node; if (mode == TYPE_MODE (void_type_node)) return void_type_node; if (mode == TYPE_MODE (build_pointer_type (char_type_node))) return unsignedp ? make_unsigned_type (mode) : make_signed_type (mode); if (mode == TYPE_MODE (build_pointer_type (integer_type_node))) return unsignedp ? make_unsigned_type (mode) : make_signed_type (mode); switch (mode) { case V16QImode: return unsignedp ? unsigned_V16QI_type_node : V16QI_type_node; case V8HImode: return unsignedp ? unsigned_V8HI_type_node : V8HI_type_node; case V4SImode: return unsignedp ? unsigned_V4SI_type_node : V4SI_type_node; case V2DImode: return unsignedp ? unsigned_V2DI_type_node : V2DI_type_node; case V2SImode: return unsignedp ? unsigned_V2SI_type_node : V2SI_type_node; case V2HImode: return unsignedp ? unsigned_V2HI_type_node : V2HI_type_node; case V4HImode: return unsignedp ? unsigned_V4HI_type_node : V4HI_type_node; case V8QImode: return unsignedp ? unsigned_V8QI_type_node : V8QI_type_node; case V1DImode: return unsignedp ? unsigned_V1DI_type_node : V1DI_type_node; case V16SFmode: return V16SF_type_node; case V4SFmode: return V4SF_type_node; case V2SFmode: return V2SF_type_node; case V2DFmode: return V2DF_type_node; case V4DFmode: return V4DF_type_node; default: break; } for (t = registered_builtin_types; t; t = TREE_CHAIN (t)) if (TYPE_MODE (TREE_VALUE (t)) == mode) return TREE_VALUE (t); return 0; } /* Return an unsigned type the same as TYPE in other respects. */ tree c_common_unsigned_type (tree type) { tree type1 = TYPE_MAIN_VARIANT (type); if (type1 == signed_char_type_node || type1 == char_type_node) return unsigned_char_type_node; if (type1 == integer_type_node) return unsigned_type_node; if (type1 == short_integer_type_node) return short_unsigned_type_node; if (type1 == long_integer_type_node) return long_unsigned_type_node; if (type1 == long_long_integer_type_node) return long_long_unsigned_type_node; if (type1 == widest_integer_literal_type_node) return widest_unsigned_literal_type_node; #if HOST_BITS_PER_WIDE_INT >= 64 if (type1 == intTI_type_node) return unsigned_intTI_type_node; #endif if (type1 == intDI_type_node) return unsigned_intDI_type_node; if (type1 == intSI_type_node) return unsigned_intSI_type_node; if (type1 == intHI_type_node) return unsigned_intHI_type_node; if (type1 == intQI_type_node) return unsigned_intQI_type_node; return c_common_signed_or_unsigned_type (1, type); } /* Return a signed type the same as TYPE in other respects. */ tree c_common_signed_type (tree type) { tree type1 = TYPE_MAIN_VARIANT (type); if (type1 == unsigned_char_type_node || type1 == char_type_node) return signed_char_type_node; if (type1 == unsigned_type_node) return integer_type_node; if (type1 == short_unsigned_type_node) return short_integer_type_node; if (type1 == long_unsigned_type_node) return long_integer_type_node; if (type1 == long_long_unsigned_type_node) return long_long_integer_type_node; if (type1 == widest_unsigned_literal_type_node) return widest_integer_literal_type_node; #if HOST_BITS_PER_WIDE_INT >= 64 if (type1 == unsigned_intTI_type_node) return intTI_type_node; #endif if (type1 == unsigned_intDI_type_node) return intDI_type_node; if (type1 == unsigned_intSI_type_node) return intSI_type_node; if (type1 == unsigned_intHI_type_node) return intHI_type_node; if (type1 == unsigned_intQI_type_node) return intQI_type_node; return c_common_signed_or_unsigned_type (0, type); } /* Return a type the same as TYPE except unsigned or signed according to UNSIGNEDP. */ tree c_common_signed_or_unsigned_type (int unsignedp, tree type) { if (! INTEGRAL_TYPE_P (type) || TREE_UNSIGNED (type) == unsignedp) return type; /* Must check the mode of the types, not the precision. Enumeral types in C++ have precision set to match their range, but may use a wider mode to match an ABI. If we change modes, we may wind up with bad conversions. */ if (TYPE_MODE (type) == TYPE_MODE (signed_char_type_node)) return unsignedp ? unsigned_char_type_node : signed_char_type_node; if (TYPE_MODE (type) == TYPE_MODE (integer_type_node)) return unsignedp ? unsigned_type_node : integer_type_node; if (TYPE_MODE (type) == TYPE_MODE (short_integer_type_node)) return unsignedp ? short_unsigned_type_node : short_integer_type_node; if (TYPE_MODE (type) == TYPE_MODE (long_integer_type_node)) return unsignedp ? long_unsigned_type_node : long_integer_type_node; if (TYPE_MODE (type) == TYPE_MODE (long_long_integer_type_node)) return (unsignedp ? long_long_unsigned_type_node : long_long_integer_type_node); if (TYPE_MODE (type) == TYPE_MODE (widest_integer_literal_type_node)) return (unsignedp ? widest_unsigned_literal_type_node : widest_integer_literal_type_node); #if HOST_BITS_PER_WIDE_INT >= 64 if (TYPE_MODE (type) == TYPE_MODE (intTI_type_node)) return unsignedp ? unsigned_intTI_type_node : intTI_type_node; #endif if (TYPE_MODE (type) == TYPE_MODE (intDI_type_node)) return unsignedp ? unsigned_intDI_type_node : intDI_type_node; if (TYPE_MODE (type) == TYPE_MODE (intSI_type_node)) return unsignedp ? unsigned_intSI_type_node : intSI_type_node; if (TYPE_MODE (type) == TYPE_MODE (intHI_type_node)) return unsignedp ? unsigned_intHI_type_node : intHI_type_node; if (TYPE_MODE (type) == TYPE_MODE (intQI_type_node)) return unsignedp ? unsigned_intQI_type_node : intQI_type_node; return type; } /* The C version of the register_builtin_type langhook. */ void c_register_builtin_type (tree type, const char* name) { tree decl; decl = build_decl (TYPE_DECL, get_identifier (name), type); DECL_ARTIFICIAL (decl) = 1; if (!TYPE_NAME (type)) TYPE_NAME (type) = decl; pushdecl (decl); registered_builtin_types = tree_cons (0, type, registered_builtin_types); } /* Return the minimum number of bits needed to represent VALUE in a signed or unsigned type, UNSIGNEDP says which. */ unsigned int min_precision (tree value, int unsignedp) { int log; /* If the value is negative, compute its negative minus 1. The latter adjustment is because the absolute value of the largest negative value is one larger than the largest positive value. This is equivalent to a bit-wise negation, so use that operation instead. */ if (tree_int_cst_sgn (value) < 0) value = fold (build1 (BIT_NOT_EXPR, TREE_TYPE (value), value)); /* Return the number of bits needed, taking into account the fact that we need one more bit for a signed than unsigned type. */ if (integer_zerop (value)) log = 0; else log = tree_floor_log2 (value); return log + 1 + ! unsignedp; } /* Print an error message for invalid operands to arith operation CODE. NOP_EXPR is used as a special case (see c_common_truthvalue_conversion). */ void binary_op_error (enum tree_code code) { const char *opname; switch (code) { case NOP_EXPR: error ("invalid truth-value expression"); return; case PLUS_EXPR: opname = "+"; break; case MINUS_EXPR: opname = "-"; break; case MULT_EXPR: opname = "*"; break; case MAX_EXPR: opname = "max"; break; case MIN_EXPR: opname = "min"; break; case EQ_EXPR: opname = "=="; break; case NE_EXPR: opname = "!="; break; case LE_EXPR: opname = "<="; break; case GE_EXPR: opname = ">="; break; case LT_EXPR: opname = "<"; break; case GT_EXPR: opname = ">"; break; case LSHIFT_EXPR: opname = "<<"; break; case RSHIFT_EXPR: opname = ">>"; break; case TRUNC_MOD_EXPR: case FLOOR_MOD_EXPR: opname = "%"; break; case TRUNC_DIV_EXPR: case FLOOR_DIV_EXPR: opname = "/"; break; case BIT_AND_EXPR: opname = "&"; break; case BIT_IOR_EXPR: opname = "|"; break; case TRUTH_ANDIF_EXPR: opname = "&&"; break; case TRUTH_ORIF_EXPR: opname = "||"; break; case BIT_XOR_EXPR: opname = "^"; break; case LROTATE_EXPR: case RROTATE_EXPR: opname = "rotate"; break; default: opname = "unknown"; break; } error ("invalid operands to binary %s", opname); } /* Subroutine of build_binary_op, used for comparison operations. See if the operands have both been converted from subword integer types and, if so, perhaps change them both back to their original type. This function is also responsible for converting the two operands to the proper common type for comparison. The arguments of this function are all pointers to local variables of build_binary_op: OP0_PTR is &OP0, OP1_PTR is &OP1, RESTYPE_PTR is &RESULT_TYPE and RESCODE_PTR is &RESULTCODE. If this function returns nonzero, it means that the comparison has a constant value. What this function returns is an expression for that value. */ tree shorten_compare (tree *op0_ptr, tree *op1_ptr, tree *restype_ptr, enum tree_code *rescode_ptr) { tree type; tree op0 = *op0_ptr; tree op1 = *op1_ptr; int unsignedp0, unsignedp1; int real1, real2; tree primop0, primop1; enum tree_code code = *rescode_ptr; /* Throw away any conversions to wider types already present in the operands. */ primop0 = get_narrower (op0, &unsignedp0); primop1 = get_narrower (op1, &unsignedp1); /* Handle the case that OP0 does not *contain* a conversion but it *requires* conversion to FINAL_TYPE. */ if (op0 == primop0 && TREE_TYPE (op0) != *restype_ptr) unsignedp0 = TREE_UNSIGNED (TREE_TYPE (op0)); if (op1 == primop1 && TREE_TYPE (op1) != *restype_ptr) unsignedp1 = TREE_UNSIGNED (TREE_TYPE (op1)); /* If one of the operands must be floated, we cannot optimize. */ real1 = TREE_CODE (TREE_TYPE (primop0)) == REAL_TYPE; real2 = TREE_CODE (TREE_TYPE (primop1)) == REAL_TYPE; /* If first arg is constant, swap the args (changing operation so value is preserved), for canonicalization. Don't do this if the second arg is 0. */ if (TREE_CONSTANT (primop0) && ! integer_zerop (primop1) && ! real_zerop (primop1)) { tree tem = primop0; int temi = unsignedp0; primop0 = primop1; primop1 = tem; tem = op0; op0 = op1; op1 = tem; *op0_ptr = op0; *op1_ptr = op1; unsignedp0 = unsignedp1; unsignedp1 = temi; temi = real1; real1 = real2; real2 = temi; switch (code) { case LT_EXPR: code = GT_EXPR; break; case GT_EXPR: code = LT_EXPR; break; case LE_EXPR: code = GE_EXPR; break; case GE_EXPR: code = LE_EXPR; break; default: break; } *rescode_ptr = code; } /* If comparing an integer against a constant more bits wide, maybe we can deduce a value of 1 or 0 independent of the data. Or else truncate the constant now rather than extend the variable at run time. This is only interesting if the constant is the wider arg. Also, it is not safe if the constant is unsigned and the variable arg is signed, since in this case the variable would be sign-extended and then regarded as unsigned. Our technique fails in this case because the lowest/highest possible unsigned results don't follow naturally from the lowest/highest possible values of the variable operand. For just EQ_EXPR and NE_EXPR there is another technique that could be used: see if the constant can be faithfully represented in the other operand's type, by truncating it and reextending it and see if that preserves the constant's value. */ if (!real1 && !real2 && TREE_CODE (primop1) == INTEGER_CST && TYPE_PRECISION (TREE_TYPE (primop0)) < TYPE_PRECISION (*restype_ptr)) { int min_gt, max_gt, min_lt, max_lt; tree maxval, minval; /* 1 if comparison is nominally unsigned. */ int unsignedp = TREE_UNSIGNED (*restype_ptr); tree val; type = c_common_signed_or_unsigned_type (unsignedp0, TREE_TYPE (primop0)); /* In C, if TYPE is an enumeration, then we need to get its min/max values from it's underlying integral type, not the enumerated type itself. In C++, TYPE_MAX_VALUE and TYPE_MIN_VALUE have already been set correctly on the enumeration type. */ if (!c_dialect_cxx() && TREE_CODE (type) == ENUMERAL_TYPE) type = c_common_type_for_size (TYPE_PRECISION (type), unsignedp0); maxval = TYPE_MAX_VALUE (type); minval = TYPE_MIN_VALUE (type); if (unsignedp && !unsignedp0) *restype_ptr = c_common_signed_type (*restype_ptr); if (TREE_TYPE (primop1) != *restype_ptr) primop1 = convert (*restype_ptr, primop1); if (type != *restype_ptr) { minval = convert (*restype_ptr, minval); maxval = convert (*restype_ptr, maxval); } if (unsignedp && unsignedp0) { min_gt = INT_CST_LT_UNSIGNED (primop1, minval); max_gt = INT_CST_LT_UNSIGNED (primop1, maxval); min_lt = INT_CST_LT_UNSIGNED (minval, primop1); max_lt = INT_CST_LT_UNSIGNED (maxval, primop1); } else { min_gt = INT_CST_LT (primop1, minval); max_gt = INT_CST_LT (primop1, maxval); min_lt = INT_CST_LT (minval, primop1); max_lt = INT_CST_LT (maxval, primop1); } val = 0; /* This used to be a switch, but Genix compiler can't handle that. */ if (code == NE_EXPR) { if (max_lt || min_gt) val = truthvalue_true_node; } else if (code == EQ_EXPR) { if (max_lt || min_gt) val = truthvalue_false_node; } else if (code == LT_EXPR) { if (max_lt) val = truthvalue_true_node; if (!min_lt) val = truthvalue_false_node; } else if (code == GT_EXPR) { if (min_gt) val = truthvalue_true_node; if (!max_gt) val = truthvalue_false_node; } else if (code == LE_EXPR) { if (!max_gt) val = truthvalue_true_node; if (min_gt) val = truthvalue_false_node; } else if (code == GE_EXPR) { if (!min_lt) val = truthvalue_true_node; if (max_lt) val = truthvalue_false_node; } /* If primop0 was sign-extended and unsigned comparison specd, we did a signed comparison above using the signed type bounds. But the comparison we output must be unsigned. Also, for inequalities, VAL is no good; but if the signed comparison had *any* fixed result, it follows that the unsigned comparison just tests the sign in reverse (positive values are LE, negative ones GE). So we can generate an unsigned comparison against an extreme value of the signed type. */ if (unsignedp && !unsignedp0) { if (val != 0) switch (code) { case LT_EXPR: case GE_EXPR: primop1 = TYPE_MIN_VALUE (type); val = 0; break; case LE_EXPR: case GT_EXPR: primop1 = TYPE_MAX_VALUE (type); val = 0; break; default: break; } type = c_common_unsigned_type (type); } if (TREE_CODE (primop0) != INTEGER_CST) { if (val == truthvalue_false_node) warning ("comparison is always false due to limited range of data type"); if (val == truthvalue_true_node) warning ("comparison is always true due to limited range of data type"); } if (val != 0) { /* Don't forget to evaluate PRIMOP0 if it has side effects. */ if (TREE_SIDE_EFFECTS (primop0)) return build (COMPOUND_EXPR, TREE_TYPE (val), primop0, val); return val; } /* Value is not predetermined, but do the comparison in the type of the operand that is not constant. TYPE is already properly set. */ } else if (real1 && real2 && (TYPE_PRECISION (TREE_TYPE (primop0)) == TYPE_PRECISION (TREE_TYPE (primop1)))) type = TREE_TYPE (primop0); /* If args' natural types are both narrower than nominal type and both extend in the same manner, compare them in the type of the wider arg. Otherwise must actually extend both to the nominal common type lest different ways of extending alter the result. (eg, (short)-1 == (unsigned short)-1 should be 0.) */ else if (unsignedp0 == unsignedp1 && real1 == real2 && TYPE_PRECISION (TREE_TYPE (primop0)) < TYPE_PRECISION (*restype_ptr) && TYPE_PRECISION (TREE_TYPE (primop1)) < TYPE_PRECISION (*restype_ptr)) { type = common_type (TREE_TYPE (primop0), TREE_TYPE (primop1)); type = c_common_signed_or_unsigned_type (unsignedp0 || TREE_UNSIGNED (*restype_ptr), type); /* Make sure shorter operand is extended the right way to match the longer operand. */ primop0 = convert (c_common_signed_or_unsigned_type (unsignedp0, TREE_TYPE (primop0)), primop0); primop1 = convert (c_common_signed_or_unsigned_type (unsignedp1, TREE_TYPE (primop1)), primop1); } else { /* Here we must do the comparison on the nominal type using the args exactly as we received them. */ type = *restype_ptr; primop0 = op0; primop1 = op1; if (!real1 && !real2 && integer_zerop (primop1) && TREE_UNSIGNED (*restype_ptr)) { tree value = 0; switch (code) { case GE_EXPR: /* All unsigned values are >= 0, so we warn if extra warnings are requested. However, if OP0 is a constant that is >= 0, the signedness of the comparison isn't an issue, so suppress the warning. */ if (extra_warnings && !in_system_header && ! (TREE_CODE (primop0) == INTEGER_CST && ! TREE_OVERFLOW (convert (c_common_signed_type (type), primop0)))) warning ("comparison of unsigned expression >= 0 is always true"); value = truthvalue_true_node; break; case LT_EXPR: if (extra_warnings && !in_system_header && ! (TREE_CODE (primop0) == INTEGER_CST && ! TREE_OVERFLOW (convert (c_common_signed_type (type), primop0)))) warning ("comparison of unsigned expression < 0 is always false"); value = truthvalue_false_node; break; default: break; } if (value != 0) { /* Don't forget to evaluate PRIMOP0 if it has side effects. */ if (TREE_SIDE_EFFECTS (primop0)) return build (COMPOUND_EXPR, TREE_TYPE (value), primop0, value); return value; } } } *op0_ptr = convert (type, primop0); *op1_ptr = convert (type, primop1); *restype_ptr = truthvalue_type_node; return 0; } /* Return a tree for the sum or difference (RESULTCODE says which) of pointer PTROP and integer INTOP. */ tree pointer_int_sum (enum tree_code resultcode, tree ptrop, tree intop) { tree size_exp; tree result; tree folded; /* The result is a pointer of the same type that is being added. */ tree result_type = TREE_TYPE (ptrop); if (TREE_CODE (TREE_TYPE (result_type)) == VOID_TYPE) { if (pedantic || warn_pointer_arith) pedwarn ("pointer of type `void *' used in arithmetic"); size_exp = integer_one_node; } else if (TREE_CODE (TREE_TYPE (result_type)) == FUNCTION_TYPE) { if (pedantic || warn_pointer_arith) pedwarn ("pointer to a function used in arithmetic"); size_exp = integer_one_node; } else if (TREE_CODE (TREE_TYPE (result_type)) == METHOD_TYPE) { if (pedantic || warn_pointer_arith) pedwarn ("pointer to member function used in arithmetic"); size_exp = integer_one_node; } else size_exp = size_in_bytes (TREE_TYPE (result_type)); /* If what we are about to multiply by the size of the elements contains a constant term, apply distributive law and multiply that constant term separately. This helps produce common subexpressions. */ if ((TREE_CODE (intop) == PLUS_EXPR || TREE_CODE (intop) == MINUS_EXPR) && ! TREE_CONSTANT (intop) && TREE_CONSTANT (TREE_OPERAND (intop, 1)) && TREE_CONSTANT (size_exp) /* If the constant comes from pointer subtraction, skip this optimization--it would cause an error. */ && TREE_CODE (TREE_TYPE (TREE_OPERAND (intop, 0))) == INTEGER_TYPE /* If the constant is unsigned, and smaller than the pointer size, then we must skip this optimization. This is because it could cause an overflow error if the constant is negative but INTOP is not. */ && (! TREE_UNSIGNED (TREE_TYPE (intop)) || (TYPE_PRECISION (TREE_TYPE (intop)) == TYPE_PRECISION (TREE_TYPE (ptrop))))) { enum tree_code subcode = resultcode; tree int_type = TREE_TYPE (intop); if (TREE_CODE (intop) == MINUS_EXPR) subcode = (subcode == PLUS_EXPR ? MINUS_EXPR : PLUS_EXPR); /* Convert both subexpression types to the type of intop, because weird cases involving pointer arithmetic can result in a sum or difference with different type args. */ ptrop = build_binary_op (subcode, ptrop, convert (int_type, TREE_OPERAND (intop, 1)), 1); intop = convert (int_type, TREE_OPERAND (intop, 0)); } /* Convert the integer argument to a type the same size as sizetype so the multiply won't overflow spuriously. */ if (TYPE_PRECISION (TREE_TYPE (intop)) != TYPE_PRECISION (sizetype) || TREE_UNSIGNED (TREE_TYPE (intop)) != TREE_UNSIGNED (sizetype)) intop = convert (c_common_type_for_size (TYPE_PRECISION (sizetype), TREE_UNSIGNED (sizetype)), intop); /* Replace the integer argument with a suitable product by the object size. Do this multiplication as signed, then convert to the appropriate pointer type (actually unsigned integral). */ intop = convert (result_type, build_binary_op (MULT_EXPR, intop, convert (TREE_TYPE (intop), size_exp), 1)); /* Create the sum or difference. */ result = build (resultcode, result_type, ptrop, intop); folded = fold (result); if (folded == result) TREE_CONSTANT (folded) = TREE_CONSTANT (ptrop) & TREE_CONSTANT (intop); return folded; } /* Prepare expr to be an argument of a TRUTH_NOT_EXPR, or validate its data type for an `if' or `while' statement or ?..: exp. This preparation consists of taking the ordinary representation of an expression expr and producing a valid tree boolean expression describing whether expr is nonzero. We could simply always do build_binary_op (NE_EXPR, expr, truthvalue_false_node, 1), but we optimize comparisons, &&, ||, and !. The resulting type should always be `truthvalue_type_node'. */ tree c_common_truthvalue_conversion (tree expr) { if (TREE_CODE (expr) == ERROR_MARK) return expr; if (TREE_CODE (expr) == FUNCTION_DECL) expr = build_unary_op (ADDR_EXPR, expr, 0); #if 0 /* This appears to be wrong for C++. */ /* These really should return error_mark_node after 2.4 is stable. But not all callers handle ERROR_MARK properly. */ switch (TREE_CODE (TREE_TYPE (expr))) { case RECORD_TYPE: error ("struct type value used where scalar is required"); return truthvalue_false_node; case UNION_TYPE: error ("union type value used where scalar is required"); return truthvalue_false_node; case ARRAY_TYPE: error ("array type value used where scalar is required"); return truthvalue_false_node; default: break; } #endif /* 0 */ switch (TREE_CODE (expr)) { case EQ_EXPR: case NE_EXPR: case LE_EXPR: case GE_EXPR: case LT_EXPR: case GT_EXPR: case TRUTH_ANDIF_EXPR: case TRUTH_ORIF_EXPR: case TRUTH_AND_EXPR: case TRUTH_OR_EXPR: case TRUTH_XOR_EXPR: case TRUTH_NOT_EXPR: TREE_TYPE (expr) = truthvalue_type_node; return expr; case ERROR_MARK: return expr; case INTEGER_CST: return integer_zerop (expr) ? truthvalue_false_node : truthvalue_true_node; case REAL_CST: return real_zerop (expr) ? truthvalue_false_node : truthvalue_true_node; case ADDR_EXPR: { if (TREE_CODE (TREE_OPERAND (expr, 0)) == FUNCTION_DECL && ! DECL_WEAK (TREE_OPERAND (expr, 0))) { /* Common Ada/Pascal programmer's mistake. We always warn about this since it is so bad. */ warning ("the address of `%D', will always evaluate as `true'", TREE_OPERAND (expr, 0)); return truthvalue_true_node; } /* If we are taking the address of an external decl, it might be zero if it is weak, so we cannot optimize. */ if (DECL_P (TREE_OPERAND (expr, 0)) && DECL_EXTERNAL (TREE_OPERAND (expr, 0))) break; if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 0))) return build (COMPOUND_EXPR, truthvalue_type_node, TREE_OPERAND (expr, 0), truthvalue_true_node); else return truthvalue_true_node; } case COMPLEX_EXPR: return build_binary_op ((TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 1)) ? TRUTH_OR_EXPR : TRUTH_ORIF_EXPR), c_common_truthvalue_conversion (TREE_OPERAND (expr, 0)), c_common_truthvalue_conversion (TREE_OPERAND (expr, 1)), 0); case NEGATE_EXPR: case ABS_EXPR: case FLOAT_EXPR: /* These don't change whether an object is nonzero or zero. */ return c_common_truthvalue_conversion (TREE_OPERAND (expr, 0)); case LROTATE_EXPR: case RROTATE_EXPR: /* These don't change whether an object is zero or nonzero, but we can't ignore them if their second arg has side-effects. */ if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 1))) return build (COMPOUND_EXPR, truthvalue_type_node, TREE_OPERAND (expr, 1), c_common_truthvalue_conversion (TREE_OPERAND (expr, 0))); else return c_common_truthvalue_conversion (TREE_OPERAND (expr, 0)); case COND_EXPR: /* Distribute the conversion into the arms of a COND_EXPR. */ return fold (build (COND_EXPR, truthvalue_type_node, TREE_OPERAND (expr, 0), c_common_truthvalue_conversion (TREE_OPERAND (expr, 1)), c_common_truthvalue_conversion (TREE_OPERAND (expr, 2)))); case CONVERT_EXPR: /* Don't cancel the effect of a CONVERT_EXPR from a REFERENCE_TYPE, since that affects how `default_conversion' will behave. */ if (TREE_CODE (TREE_TYPE (expr)) == REFERENCE_TYPE || TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == REFERENCE_TYPE) break; /* Fall through.... */ case NOP_EXPR: /* If this is widening the argument, we can ignore it. */ if (TYPE_PRECISION (TREE_TYPE (expr)) >= TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (expr, 0)))) return c_common_truthvalue_conversion (TREE_OPERAND (expr, 0)); break; case MINUS_EXPR: /* Perhaps reduce (x - y) != 0 to (x != y). The expressions aren't guaranteed to the be same for modes that can represent infinity, since if x and y are both +infinity, or both -infinity, then x - y is not a number. Note that this transformation is safe when x or y is NaN. (x - y) is then NaN, and both (x - y) != 0 and x != y will be false. */ if (HONOR_INFINITIES (TYPE_MODE (TREE_TYPE (TREE_OPERAND (expr, 0))))) break; /* Fall through.... */ case BIT_XOR_EXPR: /* This and MINUS_EXPR can be changed into a comparison of the two objects. */ if (TREE_TYPE (TREE_OPERAND (expr, 0)) == TREE_TYPE (TREE_OPERAND (expr, 1))) return build_binary_op (NE_EXPR, TREE_OPERAND (expr, 0), TREE_OPERAND (expr, 1), 1); return build_binary_op (NE_EXPR, TREE_OPERAND (expr, 0), fold (build1 (NOP_EXPR, TREE_TYPE (TREE_OPERAND (expr, 0)), TREE_OPERAND (expr, 1))), 1); case BIT_AND_EXPR: if (integer_onep (TREE_OPERAND (expr, 1)) && TREE_TYPE (expr) != truthvalue_type_node) /* Using convert here would cause infinite recursion. */ return build1 (NOP_EXPR, truthvalue_type_node, expr); break; case MODIFY_EXPR: if (warn_parentheses && C_EXP_ORIGINAL_CODE (expr) == MODIFY_EXPR) warning ("suggest parentheses around assignment used as truth value"); break; default: break; } if (TREE_CODE (TREE_TYPE (expr)) == COMPLEX_TYPE) { tree t = save_expr (expr); return (build_binary_op ((TREE_SIDE_EFFECTS (expr) ? TRUTH_OR_EXPR : TRUTH_ORIF_EXPR), c_common_truthvalue_conversion (build_unary_op (REALPART_EXPR, t, 0)), c_common_truthvalue_conversion (build_unary_op (IMAGPART_EXPR, t, 0)), 0)); } return build_binary_op (NE_EXPR, expr, integer_zero_node, 1); } static tree builtin_function_2 (const char *, const char *, tree, tree, int, enum built_in_class, int, int, tree); /* Make a variant type in the proper way for C/C++, propagating qualifiers down to the element type of an array. */ tree c_build_qualified_type (tree type, int type_quals) { if (type == error_mark_node) return type; if (TREE_CODE (type) == ARRAY_TYPE) return build_array_type (c_build_qualified_type (TREE_TYPE (type), type_quals), TYPE_DOMAIN (type)); /* A restrict-qualified pointer type must be a pointer to object or incomplete type. Note that the use of POINTER_TYPE_P also allows REFERENCE_TYPEs, which is appropriate for C++. */ if ((type_quals & TYPE_QUAL_RESTRICT) && (!POINTER_TYPE_P (type) || !C_TYPE_OBJECT_OR_INCOMPLETE_P (TREE_TYPE (type)))) { error ("invalid use of `restrict'"); type_quals &= ~TYPE_QUAL_RESTRICT; } return build_qualified_type (type, type_quals); } /* Apply the TYPE_QUALS to the new DECL. */ void c_apply_type_quals_to_decl (int type_quals, tree decl) { tree type = TREE_TYPE (decl); if (type == error_mark_node) return; if (((type_quals & TYPE_QUAL_CONST) || (type && TREE_CODE (type) == REFERENCE_TYPE)) /* An object declared 'const' is only readonly after it is initialized. We don't have any way of expressing this currently, so we need to be conservative and unset TREE_READONLY for types with constructors. Otherwise aliasing code will ignore stores in an inline constructor. */ && !(type && TYPE_NEEDS_CONSTRUCTING (type))) TREE_READONLY (decl) = 1; if (type_quals & TYPE_QUAL_VOLATILE) { TREE_SIDE_EFFECTS (decl) = 1; TREE_THIS_VOLATILE (decl) = 1; } if (type_quals & TYPE_QUAL_RESTRICT) { while (type && TREE_CODE (type) == ARRAY_TYPE) /* Allow 'restrict' on arrays of pointers. FIXME currently we just ignore it. */ type = TREE_TYPE (type); if (!type || !POINTER_TYPE_P (type) || !C_TYPE_OBJECT_OR_INCOMPLETE_P (TREE_TYPE (type))) error ("invalid use of `restrict'"); else if (flag_strict_aliasing && type == TREE_TYPE (decl)) /* Indicate we need to make a unique alias set for this pointer. We can't do it here because it might be pointing to an incomplete type. */ DECL_POINTER_ALIAS_SET (decl) = -2; } } /* Return the typed-based alias set for T, which may be an expression or a type. Return -1 if we don't do anything special. */ HOST_WIDE_INT c_common_get_alias_set (tree t) { tree u; /* Permit type-punning when accessing a union, provided the access is directly through the union. For example, this code does not permit taking the address of a union member and then storing through it. Even the type-punning allowed here is a GCC extension, albeit a common and useful one; the C standard says that such accesses have implementation-defined behavior. */ for (u = t; TREE_CODE (u) == COMPONENT_REF || TREE_CODE (u) == ARRAY_REF; u = TREE_OPERAND (u, 0)) if (TREE_CODE (u) == COMPONENT_REF && TREE_CODE (TREE_TYPE (TREE_OPERAND (u, 0))) == UNION_TYPE) return 0; /* That's all the expressions we handle specially. */ if (! TYPE_P (t)) return -1; /* The C standard guarantees that any object may be accessed via an lvalue that has character type. */ if (t == char_type_node || t == signed_char_type_node || t == unsigned_char_type_node) return 0; /* If it has the may_alias attribute, it can alias anything. */ if (lookup_attribute ("may_alias", TYPE_ATTRIBUTES (t))) return 0; /* The C standard specifically allows aliasing between signed and unsigned variants of the same type. We treat the signed variant as canonical. */ if (TREE_CODE (t) == INTEGER_TYPE && TREE_UNSIGNED (t)) { tree t1 = c_common_signed_type (t); /* t1 == t can happen for boolean nodes which are always unsigned. */ if (t1 != t) return get_alias_set (t1); } else if (POINTER_TYPE_P (t)) { tree t1; /* Unfortunately, there is no canonical form of a pointer type. In particular, if we have `typedef int I', then `int *', and `I *' are different types. So, we have to pick a canonical representative. We do this below. Technically, this approach is actually more conservative that it needs to be. In particular, `const int *' and `int *' should be in different alias sets, according to the C and C++ standard, since their types are not the same, and so, technically, an `int **' and `const int **' cannot point at the same thing. But, the standard is wrong. In particular, this code is legal C++: int *ip; int **ipp = &ip; const int* const* cipp = &ipp; And, it doesn't make sense for that to be legal unless you can dereference IPP and CIPP. So, we ignore cv-qualifiers on the pointed-to types. This issue has been reported to the C++ committee. */ t1 = build_type_no_quals (t); if (t1 != t) return get_alias_set (t1); } return -1; } /* Compute the value of 'sizeof (TYPE)' or '__alignof__ (TYPE)', where the second parameter indicates which OPERATOR is being applied. The COMPLAIN flag controls whether we should diagnose possibly ill-formed constructs or not. */ tree c_sizeof_or_alignof_type (tree type, enum tree_code op, int complain) { const char *op_name; tree value = NULL; enum tree_code type_code = TREE_CODE (type); my_friendly_assert (op == SIZEOF_EXPR || op == ALIGNOF_EXPR, 20020720); op_name = op == SIZEOF_EXPR ? "sizeof" : "__alignof__"; if (type_code == FUNCTION_TYPE) { if (op == SIZEOF_EXPR) { if (complain && (pedantic || warn_pointer_arith)) pedwarn ("invalid application of `sizeof' to a function type"); value = size_one_node; } else value = size_int (FUNCTION_BOUNDARY / BITS_PER_UNIT); } else if (type_code == VOID_TYPE || type_code == ERROR_MARK) { if (type_code == VOID_TYPE && complain && (pedantic || warn_pointer_arith)) pedwarn ("invalid application of `%s' to a void type", op_name); value = size_one_node; } else if (!COMPLETE_TYPE_P (type)) { if (complain) error ("invalid application of `%s' to incomplete type `%T' ", op_name, type); value = size_zero_node; } else { if (op == SIZEOF_EXPR) /* Convert in case a char is more than one unit. */ value = size_binop (CEIL_DIV_EXPR, TYPE_SIZE_UNIT (type), size_int (TYPE_PRECISION (char_type_node) / BITS_PER_UNIT)); else value = size_int (TYPE_ALIGN (type) / BITS_PER_UNIT); } /* VALUE will have an integer type with TYPE_IS_SIZETYPE set. TYPE_IS_SIZETYPE means that certain things (like overflow) will never happen. However, this node should really have type `size_t', which is just a typedef for an ordinary integer type. */ value = fold (build1 (NOP_EXPR, size_type_node, value)); my_friendly_assert (!TYPE_IS_SIZETYPE (TREE_TYPE (value)), 20001021); return value; } /* Implement the __alignof keyword: Return the minimum required alignment of EXPR, measured in bytes. For VAR_DECL's and FIELD_DECL's return DECL_ALIGN (which can be set from an "aligned" __attribute__ specification). */ tree c_alignof_expr (tree expr) { tree t; if (TREE_CODE (expr) == VAR_DECL) t = size_int (DECL_ALIGN (expr) / BITS_PER_UNIT); else if (TREE_CODE (expr) == COMPONENT_REF && DECL_C_BIT_FIELD (TREE_OPERAND (expr, 1))) { error ("`__alignof' applied to a bit-field"); t = size_one_node; } else if (TREE_CODE (expr) == COMPONENT_REF && TREE_CODE (TREE_OPERAND (expr, 1)) == FIELD_DECL) t = size_int (DECL_ALIGN (TREE_OPERAND (expr, 1)) / BITS_PER_UNIT); else if (TREE_CODE (expr) == INDIRECT_REF) { tree t = TREE_OPERAND (expr, 0); tree best = t; int bestalign = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (t))); while (TREE_CODE (t) == NOP_EXPR && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == POINTER_TYPE) { int thisalign; t = TREE_OPERAND (t, 0); thisalign = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (t))); if (thisalign > bestalign) best = t, bestalign = thisalign; } return c_alignof (TREE_TYPE (TREE_TYPE (best))); } else return c_alignof (TREE_TYPE (expr)); return fold (build1 (NOP_EXPR, size_type_node, t)); } /* Handle C and C++ default attributes. */ enum built_in_attribute { #define DEF_ATTR_NULL_TREE(ENUM) ENUM, #define DEF_ATTR_INT(ENUM, VALUE) ENUM, #define DEF_ATTR_IDENT(ENUM, STRING) ENUM, #define DEF_ATTR_TREE_LIST(ENUM, PURPOSE, VALUE, CHAIN) ENUM, #include "builtin-attrs.def" #undef DEF_ATTR_NULL_TREE #undef DEF_ATTR_INT #undef DEF_ATTR_IDENT #undef DEF_ATTR_TREE_LIST ATTR_LAST }; static GTY(()) tree built_in_attributes[(int) ATTR_LAST]; static void c_init_attributes (void); /* Build tree nodes and builtin functions common to both C and C++ language frontends. */ void c_common_nodes_and_builtins (void) { enum builtin_type { #define DEF_PRIMITIVE_TYPE(NAME, VALUE) NAME, #define DEF_FUNCTION_TYPE_0(NAME, RETURN) NAME, #define DEF_FUNCTION_TYPE_1(NAME, RETURN, ARG1) NAME, #define DEF_FUNCTION_TYPE_2(NAME, RETURN, ARG1, ARG2) NAME, #define DEF_FUNCTION_TYPE_3(NAME, RETURN, ARG1, ARG2, ARG3) NAME, #define DEF_FUNCTION_TYPE_4(NAME, RETURN, ARG1, ARG2, ARG3, ARG4) NAME, #define DEF_FUNCTION_TYPE_VAR_0(NAME, RETURN) NAME, #define DEF_FUNCTION_TYPE_VAR_1(NAME, RETURN, ARG1) NAME, #define DEF_FUNCTION_TYPE_VAR_2(NAME, RETURN, ARG1, ARG2) NAME, #define DEF_FUNCTION_TYPE_VAR_3(NAME, RETURN, ARG1, ARG2, ARG3) NAME, #define DEF_POINTER_TYPE(NAME, TYPE) NAME, #include "builtin-types.def" #undef DEF_PRIMITIVE_TYPE #undef DEF_FUNCTION_TYPE_0 #undef DEF_FUNCTION_TYPE_1 #undef DEF_FUNCTION_TYPE_2 #undef DEF_FUNCTION_TYPE_3 #undef DEF_FUNCTION_TYPE_4 #undef DEF_FUNCTION_TYPE_VAR_0 #undef DEF_FUNCTION_TYPE_VAR_1 #undef DEF_FUNCTION_TYPE_VAR_2 #undef DEF_FUNCTION_TYPE_VAR_3 #undef DEF_POINTER_TYPE BT_LAST }; typedef enum builtin_type builtin_type; tree builtin_types[(int) BT_LAST]; int wchar_type_size; tree array_domain_type; tree va_list_ref_type_node; tree va_list_arg_type_node; /* Define `int' and `char' first so that dbx will output them first. */ record_builtin_type (RID_INT, NULL, integer_type_node); record_builtin_type (RID_CHAR, "char", char_type_node); /* `signed' is the same as `int'. FIXME: the declarations of "signed", "unsigned long", "long long unsigned" and "unsigned short" were in C++ but not C. Are the conditionals here needed? */ if (c_dialect_cxx ()) record_builtin_type (RID_SIGNED, NULL, integer_type_node); record_builtin_type (RID_LONG, "long int", long_integer_type_node); record_builtin_type (RID_UNSIGNED, "unsigned int", unsigned_type_node); record_builtin_type (RID_MAX, "long unsigned int", long_unsigned_type_node); if (c_dialect_cxx ()) record_builtin_type (RID_MAX, "unsigned long", long_unsigned_type_node); record_builtin_type (RID_MAX, "long long int", long_long_integer_type_node); record_builtin_type (RID_MAX, "long long unsigned int", long_long_unsigned_type_node); if (c_dialect_cxx ()) record_builtin_type (RID_MAX, "long long unsigned", long_long_unsigned_type_node); record_builtin_type (RID_SHORT, "short int", short_integer_type_node); record_builtin_type (RID_MAX, "short unsigned int", short_unsigned_type_node); if (c_dialect_cxx ()) record_builtin_type (RID_MAX, "unsigned short", short_unsigned_type_node); /* Define both `signed char' and `unsigned char'. */ record_builtin_type (RID_MAX, "signed char", signed_char_type_node); record_builtin_type (RID_MAX, "unsigned char", unsigned_char_type_node); /* These are types that c_common_type_for_size and c_common_type_for_mode use. */ (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, NULL_TREE, intQI_type_node)); (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, NULL_TREE, intHI_type_node)); (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, NULL_TREE, intSI_type_node)); (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, NULL_TREE, intDI_type_node)); #if HOST_BITS_PER_WIDE_INT >= 64 (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, get_identifier ("__int128_t"), intTI_type_node)); #endif (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, NULL_TREE, unsigned_intQI_type_node)); (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, NULL_TREE, unsigned_intHI_type_node)); (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, NULL_TREE, unsigned_intSI_type_node)); (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, NULL_TREE, unsigned_intDI_type_node)); #if HOST_BITS_PER_WIDE_INT >= 64 (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, get_identifier ("__uint128_t"), unsigned_intTI_type_node)); #endif /* Create the widest literal types. */ widest_integer_literal_type_node = make_signed_type (HOST_BITS_PER_WIDE_INT * 2); (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, NULL_TREE, widest_integer_literal_type_node)); widest_unsigned_literal_type_node = make_unsigned_type (HOST_BITS_PER_WIDE_INT * 2); (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, NULL_TREE, widest_unsigned_literal_type_node)); /* `unsigned long' is the standard type for sizeof. Note that stddef.h uses `unsigned long', and this must agree, even if long and int are the same size. */ size_type_node = TREE_TYPE (identifier_global_value (get_identifier (SIZE_TYPE))); signed_size_type_node = c_common_signed_type (size_type_node); set_sizetype (size_type_node); build_common_tree_nodes_2 (flag_short_double); record_builtin_type (RID_FLOAT, NULL, float_type_node); record_builtin_type (RID_DOUBLE, NULL, double_type_node); record_builtin_type (RID_MAX, "long double", long_double_type_node); (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, get_identifier ("complex int"), complex_integer_type_node)); (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, get_identifier ("complex float"), complex_float_type_node)); (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, get_identifier ("complex double"), complex_double_type_node)); (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, get_identifier ("complex long double"), complex_long_double_type_node)); /* Types which are common to the fortran compiler and libf2c. When changing these, you also need to be concerned with f/com.h. */ if (TYPE_PRECISION (float_type_node) == TYPE_PRECISION (long_integer_type_node)) { g77_integer_type_node = long_integer_type_node; g77_uinteger_type_node = long_unsigned_type_node; } else if (TYPE_PRECISION (float_type_node) == TYPE_PRECISION (integer_type_node)) { g77_integer_type_node = integer_type_node; g77_uinteger_type_node = unsigned_type_node; } else g77_integer_type_node = g77_uinteger_type_node = NULL_TREE; if (g77_integer_type_node != NULL_TREE) { (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, get_identifier ("__g77_integer"), g77_integer_type_node)); (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, get_identifier ("__g77_uinteger"), g77_uinteger_type_node)); } if (TYPE_PRECISION (float_type_node) * 2 == TYPE_PRECISION (long_integer_type_node)) { g77_longint_type_node = long_integer_type_node; g77_ulongint_type_node = long_unsigned_type_node; } else if (TYPE_PRECISION (float_type_node) * 2 == TYPE_PRECISION (long_long_integer_type_node)) { g77_longint_type_node = long_long_integer_type_node; g77_ulongint_type_node = long_long_unsigned_type_node; } else g77_longint_type_node = g77_ulongint_type_node = NULL_TREE; if (g77_longint_type_node != NULL_TREE) { (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, get_identifier ("__g77_longint"), g77_longint_type_node)); (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, get_identifier ("__g77_ulongint"), g77_ulongint_type_node)); } record_builtin_type (RID_VOID, NULL, void_type_node); void_zero_node = build_int_2 (0, 0); TREE_TYPE (void_zero_node) = void_type_node; void_list_node = build_void_list_node (); /* Make a type to be the domain of a few array types whose domains don't really matter. 200 is small enough that it always fits in size_t and large enough that it can hold most function names for the initializations of __FUNCTION__ and __PRETTY_FUNCTION__. */ array_domain_type = build_index_type (size_int (200)); /* Make a type for arrays of characters. With luck nothing will ever really depend on the length of this array type. */ char_array_type_node = build_array_type (char_type_node, array_domain_type); /* Likewise for arrays of ints. */ int_array_type_node = build_array_type (integer_type_node, array_domain_type); string_type_node = build_pointer_type (char_type_node); const_string_type_node = build_pointer_type (build_qualified_type (char_type_node, TYPE_QUAL_CONST)); /* This is special for C++ so functions can be overloaded. */ wchar_type_node = get_identifier (MODIFIED_WCHAR_TYPE); wchar_type_node = TREE_TYPE (identifier_global_value (wchar_type_node)); wchar_type_size = TYPE_PRECISION (wchar_type_node); if (c_dialect_cxx ()) { if (TREE_UNSIGNED (wchar_type_node)) wchar_type_node = make_unsigned_type (wchar_type_size); else wchar_type_node = make_signed_type (wchar_type_size); record_builtin_type (RID_WCHAR, "wchar_t", wchar_type_node); } else { signed_wchar_type_node = c_common_signed_type (wchar_type_node); unsigned_wchar_type_node = c_common_unsigned_type (wchar_type_node); } /* This is for wide string constants. */ wchar_array_type_node = build_array_type (wchar_type_node, array_domain_type); wint_type_node = TREE_TYPE (identifier_global_value (get_identifier (WINT_TYPE))); intmax_type_node = TREE_TYPE (identifier_global_value (get_identifier (INTMAX_TYPE))); uintmax_type_node = TREE_TYPE (identifier_global_value (get_identifier (UINTMAX_TYPE))); default_function_type = build_function_type (integer_type_node, NULL_TREE); ptrdiff_type_node = TREE_TYPE (identifier_global_value (get_identifier (PTRDIFF_TYPE))); unsigned_ptrdiff_type_node = c_common_unsigned_type (ptrdiff_type_node); (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, get_identifier ("__builtin_va_list"), va_list_type_node)); (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, get_identifier ("__builtin_ptrdiff_t"), ptrdiff_type_node)); (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, get_identifier ("__builtin_size_t"), sizetype)); if (TREE_CODE (va_list_type_node) == ARRAY_TYPE) { va_list_arg_type_node = va_list_ref_type_node = build_pointer_type (TREE_TYPE (va_list_type_node)); } else { va_list_arg_type_node = va_list_type_node; va_list_ref_type_node = build_reference_type (va_list_type_node); } #define DEF_PRIMITIVE_TYPE(ENUM, VALUE) \ builtin_types[(int) ENUM] = VALUE; #define DEF_FUNCTION_TYPE_0(ENUM, RETURN) \ builtin_types[(int) ENUM] \ = build_function_type (builtin_types[(int) RETURN], \ void_list_node); #define DEF_FUNCTION_TYPE_1(ENUM, RETURN, ARG1) \ builtin_types[(int) ENUM] \ = build_function_type (builtin_types[(int) RETURN], \ tree_cons (NULL_TREE, \ builtin_types[(int) ARG1], \ void_list_node)); #define DEF_FUNCTION_TYPE_2(ENUM, RETURN, ARG1, ARG2) \ builtin_types[(int) ENUM] \ = build_function_type \ (builtin_types[(int) RETURN], \ tree_cons (NULL_TREE, \ builtin_types[(int) ARG1], \ tree_cons (NULL_TREE, \ builtin_types[(int) ARG2], \ void_list_node))); #define DEF_FUNCTION_TYPE_3(ENUM, RETURN, ARG1, ARG2, ARG3) \ builtin_types[(int) ENUM] \ = build_function_type \ (builtin_types[(int) RETURN], \ tree_cons (NULL_TREE, \ builtin_types[(int) ARG1], \ tree_cons (NULL_TREE, \ builtin_types[(int) ARG2], \ tree_cons (NULL_TREE, \ builtin_types[(int) ARG3], \ void_list_node)))); #define DEF_FUNCTION_TYPE_4(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4) \ builtin_types[(int) ENUM] \ = build_function_type \ (builtin_types[(int) RETURN], \ tree_cons (NULL_TREE, \ builtin_types[(int) ARG1], \ tree_cons (NULL_TREE, \ builtin_types[(int) ARG2], \ tree_cons \ (NULL_TREE, \ builtin_types[(int) ARG3], \ tree_cons (NULL_TREE, \ builtin_types[(int) ARG4], \ void_list_node))))); #define DEF_FUNCTION_TYPE_VAR_0(ENUM, RETURN) \ builtin_types[(int) ENUM] \ = build_function_type (builtin_types[(int) RETURN], NULL_TREE); #define DEF_FUNCTION_TYPE_VAR_1(ENUM, RETURN, ARG1) \ builtin_types[(int) ENUM] \ = build_function_type (builtin_types[(int) RETURN], \ tree_cons (NULL_TREE, \ builtin_types[(int) ARG1], \ NULL_TREE)); #define DEF_FUNCTION_TYPE_VAR_2(ENUM, RETURN, ARG1, ARG2) \ builtin_types[(int) ENUM] \ = build_function_type \ (builtin_types[(int) RETURN], \ tree_cons (NULL_TREE, \ builtin_types[(int) ARG1], \ tree_cons (NULL_TREE, \ builtin_types[(int) ARG2], \ NULL_TREE))); #define DEF_FUNCTION_TYPE_VAR_3(ENUM, RETURN, ARG1, ARG2, ARG3) \ builtin_types[(int) ENUM] \ = build_function_type \ (builtin_types[(int) RETURN], \ tree_cons (NULL_TREE, \ builtin_types[(int) ARG1], \ tree_cons (NULL_TREE, \ builtin_types[(int) ARG2], \ tree_cons (NULL_TREE, \ builtin_types[(int) ARG3], \ NULL_TREE)))); #define DEF_POINTER_TYPE(ENUM, TYPE) \ builtin_types[(int) ENUM] \ = build_pointer_type (builtin_types[(int) TYPE]); #include "builtin-types.def" #undef DEF_PRIMITIVE_TYPE #undef DEF_FUNCTION_TYPE_1 #undef DEF_FUNCTION_TYPE_2 #undef DEF_FUNCTION_TYPE_3 #undef DEF_FUNCTION_TYPE_4 #undef DEF_FUNCTION_TYPE_VAR_0 #undef DEF_FUNCTION_TYPE_VAR_1 #undef DEF_FUNCTION_TYPE_VAR_2 #undef DEF_FUNCTION_TYPE_VAR_3 #undef DEF_POINTER_TYPE c_init_attributes (); #define DEF_BUILTIN(ENUM, NAME, CLASS, TYPE, LIBTYPE, \ BOTH_P, FALLBACK_P, NONANSI_P, ATTRS, IMPLICIT) \ if (NAME) \ { \ tree decl; \ \ if (strncmp (NAME, "__builtin_", strlen ("__builtin_")) != 0) \ abort (); \ \ if (!BOTH_P) \ decl = builtin_function (NAME, builtin_types[TYPE], ENUM, \ CLASS, \ (FALLBACK_P \ ? (NAME + strlen ("__builtin_")) \ : NULL), \ built_in_attributes[(int) ATTRS]); \ else \ decl = builtin_function_2 (NAME, \ NAME + strlen ("__builtin_"), \ builtin_types[TYPE], \ builtin_types[LIBTYPE], \ ENUM, \ CLASS, \ FALLBACK_P, \ NONANSI_P, \ built_in_attributes[(int) ATTRS]); \ \ built_in_decls[(int) ENUM] = decl; \ if (IMPLICIT) \ implicit_built_in_decls[(int) ENUM] = decl; \ } #include "builtins.def" #undef DEF_BUILTIN (*targetm.init_builtins) (); main_identifier_node = get_identifier ("main"); } tree build_va_arg (tree expr, tree type) { return build1 (VA_ARG_EXPR, type, expr); } /* Linked list of disabled built-in functions. */ typedef struct disabled_builtin { const char *name; struct disabled_builtin *next; } disabled_builtin; static disabled_builtin *disabled_builtins = NULL; static bool builtin_function_disabled_p (const char *); /* Disable a built-in function specified by -fno-builtin-NAME. If NAME begins with "__builtin_", give an error. */ void disable_builtin_function (const char *name) { if (strncmp (name, "__builtin_", strlen ("__builtin_")) == 0) error ("cannot disable built-in function `%s'", name); else { disabled_builtin *new = xmalloc (sizeof (disabled_builtin)); new->name = name; new->next = disabled_builtins; disabled_builtins = new; } } /* Return true if the built-in function NAME has been disabled, false otherwise. */ static bool builtin_function_disabled_p (const char *name) { disabled_builtin *p; for (p = disabled_builtins; p != NULL; p = p->next) { if (strcmp (name, p->name) == 0) return true; } return false; } /* Possibly define a builtin function with one or two names. BUILTIN_NAME is an __builtin_-prefixed name; NAME is the ordinary name; one or both of these may be NULL (though both being NULL is useless). BUILTIN_TYPE is the type of the __builtin_-prefixed function; TYPE is the type of the function with the ordinary name. These may differ if the ordinary name is declared with a looser type to avoid conflicts with headers. FUNCTION_CODE and CLASS are as for builtin_function. If LIBRARY_NAME_P is nonzero, NAME is passed as the LIBRARY_NAME parameter to builtin_function when declaring BUILTIN_NAME. If NONANSI_P is nonzero, the name NAME is treated as a non-ANSI name; ATTRS is the tree list representing the builtin's function attributes. Returns the declaration of BUILTIN_NAME, if any, otherwise the declaration of NAME. Does not declare NAME if flag_no_builtin, or if NONANSI_P and flag_no_nonansi_builtin. */ static tree builtin_function_2 (const char *builtin_name, const char *name, tree builtin_type, tree type, int function_code, enum built_in_class class, int library_name_p, int nonansi_p, tree attrs) { tree bdecl = NULL_TREE; tree decl = NULL_TREE; if (builtin_name != 0) bdecl = builtin_function (builtin_name, builtin_type, function_code, class, library_name_p ? name : NULL, attrs); if (name != 0 && !flag_no_builtin && !builtin_function_disabled_p (name) && !(nonansi_p && flag_no_nonansi_builtin)) decl = builtin_function (name, type, function_code, class, NULL, attrs); return (bdecl != 0 ? bdecl : decl); } /* Nonzero if the type T promotes to int. This is (nearly) the integral promotions defined in ISO C99 6.3.1.1/2. */ bool c_promoting_integer_type_p (tree t) { switch (TREE_CODE (t)) { case INTEGER_TYPE: return (TYPE_MAIN_VARIANT (t) == char_type_node || TYPE_MAIN_VARIANT (t) == signed_char_type_node || TYPE_MAIN_VARIANT (t) == unsigned_char_type_node || TYPE_MAIN_VARIANT (t) == short_integer_type_node || TYPE_MAIN_VARIANT (t) == short_unsigned_type_node || TYPE_PRECISION (t) < TYPE_PRECISION (integer_type_node)); case ENUMERAL_TYPE: /* ??? Technically all enumerations not larger than an int promote to an int. But this is used along code paths that only want to notice a size change. */ return TYPE_PRECISION (t) < TYPE_PRECISION (integer_type_node); case BOOLEAN_TYPE: return 1; default: return 0; } } /* Return 1 if PARMS specifies a fixed number of parameters and none of their types is affected by default promotions. */ int self_promoting_args_p (tree parms) { tree t; for (t = parms; t; t = TREE_CHAIN (t)) { tree type = TREE_VALUE (t); if (TREE_CHAIN (t) == 0 && type != void_type_node) return 0; if (type == 0) return 0; if (TYPE_MAIN_VARIANT (type) == float_type_node) return 0; if (c_promoting_integer_type_p (type)) return 0; } return 1; } /* Recursively examines the array elements of TYPE, until a non-array element type is found. */ tree strip_array_types (tree type) { while (TREE_CODE (type) == ARRAY_TYPE) type = TREE_TYPE (type); return type; } /* Recursively remove any '*' or '&' operator from TYPE. */ tree strip_pointer_operator (tree t) { while (POINTER_TYPE_P (t)) t = TREE_TYPE (t); return t; } static tree expand_unordered_cmp (tree, tree, enum tree_code, enum tree_code); /* Expand a call to an unordered comparison function such as __builtin_isgreater(). FUNCTION is the function's declaration and PARAMS a list of the values passed. For __builtin_isunordered(), UNORDERED_CODE is UNORDERED_EXPR and ORDERED_CODE is NOP_EXPR. In other cases, UNORDERED_CODE and ORDERED_CODE are comparison codes that give the opposite of the desired result. UNORDERED_CODE is used for modes that can hold NaNs and ORDERED_CODE is used for the rest. */ static tree expand_unordered_cmp (tree function, tree params, enum tree_code unordered_code, enum tree_code ordered_code) { tree arg0, arg1, type; enum tree_code code0, code1; /* Check that we have exactly two arguments. */ if (params == 0 || TREE_CHAIN (params) == 0) { error ("too few arguments to function `%s'", IDENTIFIER_POINTER (DECL_NAME (function))); return error_mark_node; } else if (TREE_CHAIN (TREE_CHAIN (params)) != 0) { error ("too many arguments to function `%s'", IDENTIFIER_POINTER (DECL_NAME (function))); return error_mark_node; } arg0 = TREE_VALUE (params); arg1 = TREE_VALUE (TREE_CHAIN (params)); code0 = TREE_CODE (TREE_TYPE (arg0)); code1 = TREE_CODE (TREE_TYPE (arg1)); /* Make sure that the arguments have a common type of REAL. */ type = 0; if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE) && (code1 == INTEGER_TYPE || code1 == REAL_TYPE)) type = common_type (TREE_TYPE (arg0), TREE_TYPE (arg1)); if (type == 0 || TREE_CODE (type) != REAL_TYPE) { error ("non-floating-point argument to function `%s'", IDENTIFIER_POINTER (DECL_NAME (function))); return error_mark_node; } if (unordered_code == UNORDERED_EXPR) { if (MODE_HAS_NANS (TYPE_MODE (type))) return build_binary_op (unordered_code, convert (type, arg0), convert (type, arg1), 0); else return integer_zero_node; } return build_unary_op (TRUTH_NOT_EXPR, build_binary_op (MODE_HAS_NANS (TYPE_MODE (type)) ? unordered_code : ordered_code, convert (type, arg0), convert (type, arg1), 0), 0); } /* Recognize certain built-in functions so we can make tree-codes other than CALL_EXPR. We do this when it enables fold-const.c to do something useful. */ /* ??? By rights this should go in builtins.c, but only C and C++ implement build_{binary,unary}_op. Not exactly sure what bits of functionality are actually needed from those functions, or where the similar functionality exists in the other front ends. */ tree expand_tree_builtin (tree function, tree params, tree coerced_params) { if (DECL_BUILT_IN_CLASS (function) != BUILT_IN_NORMAL) return NULL_TREE; switch (DECL_FUNCTION_CODE (function)) { case BUILT_IN_ABS: case BUILT_IN_LABS: case BUILT_IN_LLABS: case BUILT_IN_IMAXABS: case BUILT_IN_FABS: case BUILT_IN_FABSL: case BUILT_IN_FABSF: if (coerced_params == 0) return integer_zero_node; return build_unary_op (ABS_EXPR, TREE_VALUE (coerced_params), 0); case BUILT_IN_CONJ: case BUILT_IN_CONJF: case BUILT_IN_CONJL: if (coerced_params == 0) return integer_zero_node; return build_unary_op (CONJ_EXPR, TREE_VALUE (coerced_params), 0); case BUILT_IN_CREAL: case BUILT_IN_CREALF: case BUILT_IN_CREALL: if (coerced_params == 0) return integer_zero_node; return non_lvalue (build_unary_op (REALPART_EXPR, TREE_VALUE (coerced_params), 0)); case BUILT_IN_CIMAG: case BUILT_IN_CIMAGF: case BUILT_IN_CIMAGL: if (coerced_params == 0) return integer_zero_node; return non_lvalue (build_unary_op (IMAGPART_EXPR, TREE_VALUE (coerced_params), 0)); case BUILT_IN_ISGREATER: return expand_unordered_cmp (function, params, UNLE_EXPR, LE_EXPR); case BUILT_IN_ISGREATEREQUAL: return expand_unordered_cmp (function, params, UNLT_EXPR, LT_EXPR); case BUILT_IN_ISLESS: return expand_unordered_cmp (function, params, UNGE_EXPR, GE_EXPR); case BUILT_IN_ISLESSEQUAL: return expand_unordered_cmp (function, params, UNGT_EXPR, GT_EXPR); case BUILT_IN_ISLESSGREATER: return expand_unordered_cmp (function, params, UNEQ_EXPR, EQ_EXPR); case BUILT_IN_ISUNORDERED: return expand_unordered_cmp (function, params, UNORDERED_EXPR, NOP_EXPR); default: break; } return NULL_TREE; } /* Walk the statement tree, rooted at *tp. Apply FUNC to all the sub-trees of *TP in a pre-order traversal. FUNC is called with the DATA and the address of each sub-tree. If FUNC returns a non-NULL value, the traversal is aborted, and the value returned by FUNC is returned. If FUNC sets WALK_SUBTREES to zero, then the subtrees of the node being visited are not walked. We don't need a without_duplicates variant of this one because the statement tree is a tree, not a graph. */ tree walk_stmt_tree (tree *tp, walk_tree_fn func, void *data) { enum tree_code code; int walk_subtrees; tree result; int i, len; #define WALK_SUBTREE(NODE) \ do \ { \ result = walk_stmt_tree (&(NODE), func, data); \ if (result) \ return result; \ } \ while (0) /* Skip empty subtrees. */ if (!*tp) return NULL_TREE; /* Skip subtrees below non-statement nodes. */ if (!STATEMENT_CODE_P (TREE_CODE (*tp))) return NULL_TREE; /* Call the function. */ walk_subtrees = 1; result = (*func) (tp, &walk_subtrees, data); /* If we found something, return it. */ if (result) return result; /* FUNC may have modified the tree, recheck that we're looking at a statement node. */ code = TREE_CODE (*tp); if (!STATEMENT_CODE_P (code)) return NULL_TREE; /* Visit the subtrees unless FUNC decided that there was nothing interesting below this point in the tree. */ if (walk_subtrees) { /* Walk over all the sub-trees of this operand. Statement nodes never contain RTL, and we needn't worry about TARGET_EXPRs. */ len = TREE_CODE_LENGTH (code); /* Go through the subtrees. We need to do this in forward order so that the scope of a FOR_EXPR is handled properly. */ for (i = 0; i < len; ++i) WALK_SUBTREE (TREE_OPERAND (*tp, i)); } /* Finally visit the chain. This can be tail-recursion optimized if we write it this way. */ return walk_stmt_tree (&TREE_CHAIN (*tp), func, data); #undef WALK_SUBTREE } /* Used to compare case labels. K1 and K2 are actually tree nodes representing case labels, or NULL_TREE for a `default' label. Returns -1 if K1 is ordered before K2, -1 if K1 is ordered after K2, and 0 if K1 and K2 are equal. */ int case_compare (splay_tree_key k1, splay_tree_key k2) { /* Consider a NULL key (such as arises with a `default' label) to be smaller than anything else. */ if (!k1) return k2 ? -1 : 0; else if (!k2) return k1 ? 1 : 0; return tree_int_cst_compare ((tree) k1, (tree) k2); } /* Process a case label for the range LOW_VALUE ... HIGH_VALUE. If LOW_VALUE and HIGH_VALUE are both NULL_TREE then this case label is actually a `default' label. If only HIGH_VALUE is NULL_TREE, then case label was declared using the usual C/C++ syntax, rather than the GNU case range extension. CASES is a tree containing all the case ranges processed so far; COND is the condition for the switch-statement itself. Returns the CASE_LABEL created, or ERROR_MARK_NODE if no CASE_LABEL is created. */ tree c_add_case_label (splay_tree cases, tree cond, tree low_value, tree high_value) { tree type; tree label; tree case_label; splay_tree_node node; /* Create the LABEL_DECL itself. */ label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); DECL_CONTEXT (label) = current_function_decl; /* If there was an error processing the switch condition, bail now before we get more confused. */ if (!cond || cond == error_mark_node) { /* Add a label anyhow so that the back-end doesn't think that the beginning of the switch is unreachable. */ if (!cases->root) add_stmt (build_case_label (NULL_TREE, NULL_TREE, label)); return error_mark_node; } if ((low_value && TREE_TYPE (low_value) && POINTER_TYPE_P (TREE_TYPE (low_value))) || (high_value && TREE_TYPE (high_value) && POINTER_TYPE_P (TREE_TYPE (high_value)))) error ("pointers are not permitted as case values"); /* Case ranges are a GNU extension. */ if (high_value && pedantic) pedwarn ("range expressions in switch statements are non-standard"); type = TREE_TYPE (cond); if (low_value) { low_value = check_case_value (low_value); low_value = convert_and_check (type, low_value); } if (high_value) { high_value = check_case_value (high_value); high_value = convert_and_check (type, high_value); } /* If an error has occurred, bail out now. */ if (low_value == error_mark_node || high_value == error_mark_node) { if (!cases->root) add_stmt (build_case_label (NULL_TREE, NULL_TREE, label)); return error_mark_node; } /* If the LOW_VALUE and HIGH_VALUE are the same, then this isn't really a case range, even though it was written that way. Remove the HIGH_VALUE to simplify later processing. */ if (tree_int_cst_equal (low_value, high_value)) high_value = NULL_TREE; if (low_value && high_value && !tree_int_cst_lt (low_value, high_value)) warning ("empty range specified"); /* Look up the LOW_VALUE in the table of case labels we already have. */ node = splay_tree_lookup (cases, (splay_tree_key) low_value); /* If there was not an exact match, check for overlapping ranges. There's no need to do this if there's no LOW_VALUE or HIGH_VALUE; that's a `default' label and the only overlap is an exact match. */ if (!node && (low_value || high_value)) { splay_tree_node low_bound; splay_tree_node high_bound; /* Even though there wasn't an exact match, there might be an overlap between this case range and another case range. Since we've (inductively) not allowed any overlapping case ranges, we simply need to find the greatest low case label that is smaller that LOW_VALUE, and the smallest low case label that is greater than LOW_VALUE. If there is an overlap it will occur in one of these two ranges. */ low_bound = splay_tree_predecessor (cases, (splay_tree_key) low_value); high_bound = splay_tree_successor (cases, (splay_tree_key) low_value); /* Check to see if the LOW_BOUND overlaps. It is smaller than the LOW_VALUE, so there is no need to check unless the LOW_BOUND is in fact itself a case range. */ if (low_bound && CASE_HIGH ((tree) low_bound->value) && tree_int_cst_compare (CASE_HIGH ((tree) low_bound->value), low_value) >= 0) node = low_bound; /* Check to see if the HIGH_BOUND overlaps. The low end of that range is bigger than the low end of the current range, so we are only interested if the current range is a real range, and not an ordinary case label. */ else if (high_bound && high_value && (tree_int_cst_compare ((tree) high_bound->key, high_value) <= 0)) node = high_bound; } /* If there was an overlap, issue an error. */ if (node) { tree duplicate = CASE_LABEL_DECL ((tree) node->value); if (high_value) { error ("duplicate (or overlapping) case value"); error ("%Jthis is the first entry overlapping that value", duplicate); } else if (low_value) { error ("duplicate case value") ; error ("%Jpreviously used here", duplicate); } else { error ("multiple default labels in one switch"); error ("%Jthis is the first default label", duplicate); } if (!cases->root) add_stmt (build_case_label (NULL_TREE, NULL_TREE, label)); } /* Add a CASE_LABEL to the statement-tree. */ case_label = add_stmt (build_case_label (low_value, high_value, label)); /* Register this case label in the splay tree. */ splay_tree_insert (cases, (splay_tree_key) low_value, (splay_tree_value) case_label); return case_label; } /* Finish an expression taking the address of LABEL (an IDENTIFIER_NODE). Returns an expression for the address. */ tree finish_label_address_expr (tree label) { tree result; if (pedantic) pedwarn ("taking the address of a label is non-standard"); if (label == error_mark_node) return error_mark_node; label = lookup_label (label); if (label == NULL_TREE) result = null_pointer_node; else { TREE_USED (label) = 1; result = build1 (ADDR_EXPR, ptr_type_node, label); TREE_CONSTANT (result) = 1; /* The current function in not necessarily uninlinable. Computed gotos are incompatible with inlining, but the value here could be used only in a diagnostic, for example. */ } return result; } /* Hook used by expand_expr to expand language-specific tree codes. */ rtx c_expand_expr (tree exp, rtx target, enum machine_mode tmode, int modifier /* Actually enum_modifier. */, rtx *alt_rtl) { switch (TREE_CODE (exp)) { case STMT_EXPR: { tree rtl_expr; rtx result; bool preserve_result = false; if (STMT_EXPR_WARN_UNUSED_RESULT (exp) && target == const0_rtx) { tree stmt = STMT_EXPR_STMT (exp); tree scope; for (scope = COMPOUND_BODY (stmt); scope && TREE_CODE (scope) != SCOPE_STMT; scope = TREE_CHAIN (scope)); if (scope && SCOPE_STMT_BLOCK (scope)) warning ("%Hignoring return value of `%D', " "declared with attribute warn_unused_result", &expr_wfl_stack->location, BLOCK_ABSTRACT_ORIGIN (SCOPE_STMT_BLOCK (scope))); else warning ("%Hignoring return value of function " "declared with attribute warn_unused_result", &expr_wfl_stack->location); } /* Since expand_expr_stmt calls free_temp_slots after every expression statement, we must call push_temp_slots here. Otherwise, any temporaries in use now would be considered out-of-scope after the first EXPR_STMT from within the STMT_EXPR. */ push_temp_slots (); rtl_expr = expand_start_stmt_expr (!STMT_EXPR_NO_SCOPE (exp)); /* If we want the result of this expression, find the last EXPR_STMT in the COMPOUND_STMT and mark it as addressable. */ if (target != const0_rtx && TREE_CODE (STMT_EXPR_STMT (exp)) == COMPOUND_STMT && TREE_CODE (COMPOUND_BODY (STMT_EXPR_STMT (exp))) == SCOPE_STMT) { tree expr = COMPOUND_BODY (STMT_EXPR_STMT (exp)); tree last = TREE_CHAIN (expr); while (TREE_CHAIN (last)) { expr = last; last = TREE_CHAIN (last); } if (TREE_CODE (last) == SCOPE_STMT && TREE_CODE (expr) == EXPR_STMT) { /* Otherwise, note that we want the value from the last expression. */ TREE_ADDRESSABLE (expr) = 1; preserve_result = true; } } expand_stmt (STMT_EXPR_STMT (exp)); expand_end_stmt_expr (rtl_expr); result = expand_expr_real (rtl_expr, target, tmode, modifier, alt_rtl); if (preserve_result && GET_CODE (result) == MEM) { if (GET_MODE (result) != BLKmode) result = copy_to_reg (result); else preserve_temp_slots (result); } /* If the statment-expression does not have a scope, then the new temporaries we created within it must live beyond the statement-expression. */ if (STMT_EXPR_NO_SCOPE (exp)) preserve_temp_slots (NULL_RTX); pop_temp_slots (); return result; } break; case COMPOUND_LITERAL_EXPR: { /* Initialize the anonymous variable declared in the compound literal, then return the variable. */ tree decl = COMPOUND_LITERAL_EXPR_DECL (exp); emit_local_var (decl); return expand_expr_real (decl, target, tmode, modifier, alt_rtl); } default: abort (); } abort (); return NULL; } /* Hook used by safe_from_p to handle language-specific tree codes. */ int c_safe_from_p (rtx target, tree exp) { /* We can see statements here when processing the body of a statement-expression. For a declaration statement declaring a variable, look at the variable's initializer. */ if (TREE_CODE (exp) == DECL_STMT) { tree decl = DECL_STMT_DECL (exp); if (TREE_CODE (decl) == VAR_DECL && DECL_INITIAL (decl) && !safe_from_p (target, DECL_INITIAL (decl), /*top_p=*/0)) return 0; } /* For any statement, we must follow the statement-chain. */ if (STATEMENT_CODE_P (TREE_CODE (exp)) && TREE_CHAIN (exp)) return safe_from_p (target, TREE_CHAIN (exp), /*top_p=*/0); /* Assume everything else is safe. */ return 1; } /* Hook used by unsafe_for_reeval to handle language-specific tree codes. */ int c_common_unsafe_for_reeval (tree exp) { /* Statement expressions may not be reevaluated, likewise compound literals. */ if (TREE_CODE (exp) == STMT_EXPR || TREE_CODE (exp) == COMPOUND_LITERAL_EXPR) return 2; /* Walk all other expressions. */ return -1; } /* Hook used by staticp to handle language-specific tree codes. */ int c_staticp (tree exp) { if (TREE_CODE (exp) == COMPOUND_LITERAL_EXPR && TREE_STATIC (COMPOUND_LITERAL_EXPR_DECL (exp))) return 1; return 0; } /* Given a boolean expression ARG, return a tree representing an increment or decrement (as indicated by CODE) of ARG. The front end must check for invalid cases (e.g., decrement in C++). */ tree boolean_increment (enum tree_code code, tree arg) { tree val; tree true_res = boolean_true_node; arg = stabilize_reference (arg); switch (code) { case PREINCREMENT_EXPR: val = build (MODIFY_EXPR, TREE_TYPE (arg), arg, true_res); break; case POSTINCREMENT_EXPR: val = build (MODIFY_EXPR, TREE_TYPE (arg), arg, true_res); arg = save_expr (arg); val = build (COMPOUND_EXPR, TREE_TYPE (arg), val, arg); val = build (COMPOUND_EXPR, TREE_TYPE (arg), arg, val); break; case PREDECREMENT_EXPR: val = build (MODIFY_EXPR, TREE_TYPE (arg), arg, invert_truthvalue (arg)); break; case POSTDECREMENT_EXPR: val = build (MODIFY_EXPR, TREE_TYPE (arg), arg, invert_truthvalue (arg)); arg = save_expr (arg); val = build (COMPOUND_EXPR, TREE_TYPE (arg), val, arg); val = build (COMPOUND_EXPR, TREE_TYPE (arg), arg, val); break; default: abort (); } TREE_SIDE_EFFECTS (val) = 1; return val; } /* Built-in macros for stddef.h, that require macros defined in this file. */ void c_stddef_cpp_builtins(void) { builtin_define_with_value ("__SIZE_TYPE__", SIZE_TYPE, 0); builtin_define_with_value ("__PTRDIFF_TYPE__", PTRDIFF_TYPE, 0); builtin_define_with_value ("__WCHAR_TYPE__", MODIFIED_WCHAR_TYPE, 0); builtin_define_with_value ("__WINT_TYPE__", WINT_TYPE, 0); } static void c_init_attributes (void) { /* Fill in the built_in_attributes array. */ #define DEF_ATTR_NULL_TREE(ENUM) \ built_in_attributes[(int) ENUM] = NULL_TREE; #define DEF_ATTR_INT(ENUM, VALUE) \ built_in_attributes[(int) ENUM] = build_int_2 (VALUE, VALUE < 0 ? -1 : 0); #define DEF_ATTR_IDENT(ENUM, STRING) \ built_in_attributes[(int) ENUM] = get_identifier (STRING); #define DEF_ATTR_TREE_LIST(ENUM, PURPOSE, VALUE, CHAIN) \ built_in_attributes[(int) ENUM] \ = tree_cons (built_in_attributes[(int) PURPOSE], \ built_in_attributes[(int) VALUE], \ built_in_attributes[(int) CHAIN]); #include "builtin-attrs.def" #undef DEF_ATTR_NULL_TREE #undef DEF_ATTR_INT #undef DEF_ATTR_IDENT #undef DEF_ATTR_TREE_LIST } /* Attribute handlers common to C front ends. */ /* Handle a "packed" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_packed_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags, bool *no_add_attrs) { if (TYPE_P (*node)) { if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE)) *node = build_type_copy (*node); TYPE_PACKED (*node) = 1; if (TYPE_MAIN_VARIANT (*node) == *node) { /* If it is the main variant, then pack the other variants too. This happens in, struct Foo { struct Foo const *ptr; // creates a variant w/o packed flag } __ attribute__((packed)); // packs it now. */ tree probe; for (probe = *node; probe; probe = TYPE_NEXT_VARIANT (probe)) TYPE_PACKED (probe) = 1; } } else if (TREE_CODE (*node) == FIELD_DECL) DECL_PACKED (*node) = 1; /* We can't set DECL_PACKED for a VAR_DECL, because the bit is used for DECL_REGISTER. It wouldn't mean anything anyway. We can't set DECL_PACKED on the type of a TYPE_DECL, because that changes what the typedef is typing. */ else { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "nocommon" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_nocommon_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { if (TREE_CODE (*node) == VAR_DECL) DECL_COMMON (*node) = 0; else { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "common" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_common_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { if (TREE_CODE (*node) == VAR_DECL) DECL_COMMON (*node) = 1; else { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "noreturn" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_noreturn_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { tree type = TREE_TYPE (*node); /* See FIXME comment in c_common_attribute_table. */ if (TREE_CODE (*node) == FUNCTION_DECL) TREE_THIS_VOLATILE (*node) = 1; else if (TREE_CODE (type) == POINTER_TYPE && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE) TREE_TYPE (*node) = build_pointer_type (build_type_variant (TREE_TYPE (type), TREE_READONLY (TREE_TYPE (type)), 1)); else { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "noinline" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_noinline_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { if (TREE_CODE (*node) == FUNCTION_DECL) DECL_UNINLINABLE (*node) = 1; else { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "always_inline" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_always_inline_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { if (TREE_CODE (*node) == FUNCTION_DECL) { /* Do nothing else, just set the attribute. We'll get at it later with lookup_attribute. */ } else { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "used" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_used_attribute (tree *pnode, tree name, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { tree node = *pnode; if (TREE_CODE (node) == FUNCTION_DECL || (TREE_CODE (node) == VAR_DECL && TREE_STATIC (node))) { TREE_USED (node) = 1; } else { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "unused" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_unused_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { if (DECL_P (*node)) { tree decl = *node; if (TREE_CODE (decl) == PARM_DECL || TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == FUNCTION_DECL || TREE_CODE (decl) == LABEL_DECL || TREE_CODE (decl) == TYPE_DECL) TREE_USED (decl) = 1; else { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } } else { if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE)) *node = build_type_copy (*node); TREE_USED (*node) = 1; } return NULL_TREE; } /* Handle a "const" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_const_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { tree type = TREE_TYPE (*node); /* See FIXME comment on noreturn in c_common_attribute_table. */ if (TREE_CODE (*node) == FUNCTION_DECL) TREE_READONLY (*node) = 1; else if (TREE_CODE (type) == POINTER_TYPE && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE) TREE_TYPE (*node) = build_pointer_type (build_type_variant (TREE_TYPE (type), 1, TREE_THIS_VOLATILE (TREE_TYPE (type)))); else { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "transparent_union" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_transparent_union_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags, bool *no_add_attrs) { tree decl = NULL_TREE; tree *type = NULL; int is_type = 0; if (DECL_P (*node)) { decl = *node; type = &TREE_TYPE (decl); is_type = TREE_CODE (*node) == TYPE_DECL; } else if (TYPE_P (*node)) type = node, is_type = 1; if (is_type && TREE_CODE (*type) == UNION_TYPE && (decl == 0 || (TYPE_FIELDS (*type) != 0 && TYPE_MODE (*type) == DECL_MODE (TYPE_FIELDS (*type))))) { if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE)) *type = build_type_copy (*type); TYPE_TRANSPARENT_UNION (*type) = 1; } else if (decl != 0 && TREE_CODE (decl) == PARM_DECL && TREE_CODE (*type) == UNION_TYPE && TYPE_MODE (*type) == DECL_MODE (TYPE_FIELDS (*type))) DECL_TRANSPARENT_UNION (decl) = 1; else { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "constructor" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_constructor_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { tree decl = *node; tree type = TREE_TYPE (decl); if (TREE_CODE (decl) == FUNCTION_DECL && TREE_CODE (type) == FUNCTION_TYPE && decl_function_context (decl) == 0) { DECL_STATIC_CONSTRUCTOR (decl) = 1; TREE_USED (decl) = 1; } else { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "destructor" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_destructor_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { tree decl = *node; tree type = TREE_TYPE (decl); if (TREE_CODE (decl) == FUNCTION_DECL && TREE_CODE (type) == FUNCTION_TYPE && decl_function_context (decl) == 0) { DECL_STATIC_DESTRUCTOR (decl) = 1; TREE_USED (decl) = 1; } else { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "mode" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_mode_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { tree type = *node; *no_add_attrs = true; if (TREE_CODE (TREE_VALUE (args)) != IDENTIFIER_NODE) warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); else { int j; const char *p = IDENTIFIER_POINTER (TREE_VALUE (args)); int len = strlen (p); enum machine_mode mode = VOIDmode; tree typefm; tree ptr_type; if (len > 4 && p[0] == '_' && p[1] == '_' && p[len - 1] == '_' && p[len - 2] == '_') { char *newp = alloca (len - 1); strcpy (newp, &p[2]); newp[len - 4] = '\0'; p = newp; } /* Change this type to have a type with the specified mode. First check for the special modes. */ if (! strcmp (p, "byte")) mode = byte_mode; else if (!strcmp (p, "word")) mode = word_mode; else if (! strcmp (p, "pointer")) mode = ptr_mode; else for (j = 0; j < NUM_MACHINE_MODES; j++) if (!strcmp (p, GET_MODE_NAME (j))) { mode = (enum machine_mode) j; break; } if (mode == VOIDmode) error ("unknown machine mode `%s'", p); else if (0 == (typefm = (*lang_hooks.types.type_for_mode) (mode, TREE_UNSIGNED (type)))) error ("no data type for mode `%s'", p); else if ((TREE_CODE (type) == POINTER_TYPE || TREE_CODE (type) == REFERENCE_TYPE) && !(*targetm.valid_pointer_mode) (mode)) error ("invalid pointer mode `%s'", p); else { /* If this is a vector, make sure we either have hardware support, or we can emulate it. */ if (VECTOR_MODE_P (mode) && !vector_mode_valid_p (mode)) { error ("unable to emulate '%s'", GET_MODE_NAME (mode)); return NULL_TREE; } if (TREE_CODE (type) == POINTER_TYPE) { ptr_type = build_pointer_type_for_mode (TREE_TYPE (type), mode); *node = ptr_type; } else if (TREE_CODE (type) == REFERENCE_TYPE) { ptr_type = build_reference_type_for_mode (TREE_TYPE (type), mode); *node = ptr_type; } else if (TREE_CODE (type) == ENUMERAL_TYPE) { /* For enumeral types, copy the precision from the integer type returned above. If not an INTEGER_TYPE, we can't use this mode for this type. */ if (TREE_CODE (typefm) != INTEGER_TYPE) { error ("cannot use mode %qs for enumeral types", p); return NULL_TREE; } if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE)) type = build_type_copy (type); /* We cannot use layout_type here, because that will attempt to re-layout all variants, corrupting our original. */ TYPE_PRECISION (type) = TYPE_PRECISION (typefm); TYPE_MIN_VALUE (type) = TYPE_MIN_VALUE (typefm); TYPE_MAX_VALUE (type) = TYPE_MAX_VALUE (typefm); TYPE_SIZE (type) = TYPE_SIZE (typefm); TYPE_SIZE_UNIT (type) = TYPE_SIZE_UNIT (typefm); TYPE_MODE (type) = TYPE_MODE (typefm); if (!TYPE_USER_ALIGN (type)) TYPE_ALIGN (type) = TYPE_ALIGN (typefm); *node = type; } else if (VECTOR_MODE_P (mode) ? TREE_CODE (type) != TREE_CODE (TREE_TYPE (typefm)) : TREE_CODE (type) != TREE_CODE (typefm)) { error ("mode `%s' applied to inappropriate type", p); return NULL_TREE; } else *node = typefm; /* No need to layout the type here. The caller should do this. */ } } return NULL_TREE; } /* Handle a "section" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_section_attribute (tree *node, tree name ATTRIBUTE_UNUSED, tree args, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { tree decl = *node; if (targetm.have_named_sections) { if ((TREE_CODE (decl) == FUNCTION_DECL || TREE_CODE (decl) == VAR_DECL) && TREE_CODE (TREE_VALUE (args)) == STRING_CST) { if (TREE_CODE (decl) == VAR_DECL && current_function_decl != NULL_TREE && ! TREE_STATIC (decl)) { error ("%Jsection attribute cannot be specified for " "local variables", decl); *no_add_attrs = true; } /* The decl may have already been given a section attribute from a previous declaration. Ensure they match. */ else if (DECL_SECTION_NAME (decl) != NULL_TREE && strcmp (TREE_STRING_POINTER (DECL_SECTION_NAME (decl)), TREE_STRING_POINTER (TREE_VALUE (args))) != 0) { error ("%Jsection of '%D' conflicts with previous declaration", *node, *node); *no_add_attrs = true; } else DECL_SECTION_NAME (decl) = TREE_VALUE (args); } else { error ("%Jsection attribute not allowed for '%D'", *node, *node); *no_add_attrs = true; } } else { error ("%Jsection attributes are not supported for this target", *node); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "aligned" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_aligned_attribute (tree *node, tree name ATTRIBUTE_UNUSED, tree args, int flags, bool *no_add_attrs) { tree decl = NULL_TREE; tree *type = NULL; int is_type = 0; tree align_expr = (args ? TREE_VALUE (args) : size_int (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); int i; if (DECL_P (*node)) { decl = *node; type = &TREE_TYPE (decl); is_type = TREE_CODE (*node) == TYPE_DECL; } else if (TYPE_P (*node)) type = node, is_type = 1; /* Strip any NOPs of any kind. */ while (TREE_CODE (align_expr) == NOP_EXPR || TREE_CODE (align_expr) == CONVERT_EXPR || TREE_CODE (align_expr) == NON_LVALUE_EXPR) align_expr = TREE_OPERAND (align_expr, 0); if (TREE_CODE (align_expr) != INTEGER_CST) { error ("requested alignment is not a constant"); *no_add_attrs = true; } else if ((i = tree_log2 (align_expr)) == -1) { error ("requested alignment is not a power of 2"); *no_add_attrs = true; } else if (i > HOST_BITS_PER_INT - 2) { error ("requested alignment is too large"); *no_add_attrs = true; } else if (is_type) { /* If we have a TYPE_DECL, then copy the type, so that we don't accidentally modify a builtin type. See pushdecl. */ if (decl && TREE_TYPE (decl) != error_mark_node && DECL_ORIGINAL_TYPE (decl) == NULL_TREE) { tree tt = TREE_TYPE (decl); *type = build_type_copy (*type); DECL_ORIGINAL_TYPE (decl) = tt; TYPE_NAME (*type) = decl; TREE_USED (*type) = TREE_USED (decl); TREE_TYPE (decl) = *type; } else if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE)) *type = build_type_copy (*type); TYPE_ALIGN (*type) = (1 << i) * BITS_PER_UNIT; TYPE_USER_ALIGN (*type) = 1; } else if (TREE_CODE (decl) != VAR_DECL && TREE_CODE (decl) != FIELD_DECL) { error ("%Jalignment may not be specified for '%D'", decl, decl); *no_add_attrs = true; } else { DECL_ALIGN (decl) = (1 << i) * BITS_PER_UNIT; DECL_USER_ALIGN (decl) = 1; } return NULL_TREE; } /* Handle a "weak" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_weak_attribute (tree *node, tree name ATTRIBUTE_UNUSED, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs ATTRIBUTE_UNUSED) { declare_weak (*node); return NULL_TREE; } /* Handle an "alias" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_alias_attribute (tree *node, tree name, tree args, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { tree decl = *node; if ((TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl)) || (TREE_CODE (decl) != FUNCTION_DECL && ! DECL_EXTERNAL (decl))) { error ("%J'%D' defined both normally and as an alias", decl, decl); *no_add_attrs = true; } else if (decl_function_context (decl) == 0) { tree id; id = TREE_VALUE (args); if (TREE_CODE (id) != STRING_CST) { error ("alias arg not a string"); *no_add_attrs = true; return NULL_TREE; } id = get_identifier (TREE_STRING_POINTER (id)); /* This counts as a use of the object pointed to. */ TREE_USED (id) = 1; if (TREE_CODE (decl) == FUNCTION_DECL) DECL_INITIAL (decl) = error_mark_node; else DECL_EXTERNAL (decl) = 0; } else { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE; } /* Handle an "visibility" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_visibility_attribute (tree *node, tree name, tree args, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { tree decl = *node; tree id = TREE_VALUE (args); *no_add_attrs = true; if (decl_function_context (decl) != 0 || ! TREE_PUBLIC (decl)) { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); return NULL_TREE; } if (TREE_CODE (id) != STRING_CST) { error ("visibility arg not a string"); return NULL_TREE; } if (strcmp (TREE_STRING_POINTER (id), "default") == 0) DECL_VISIBILITY (decl) = VISIBILITY_DEFAULT; else if (strcmp (TREE_STRING_POINTER (id), "internal") == 0) DECL_VISIBILITY (decl) = VISIBILITY_INTERNAL; else if (strcmp (TREE_STRING_POINTER (id), "hidden") == 0) DECL_VISIBILITY (decl) = VISIBILITY_HIDDEN; else if (strcmp (TREE_STRING_POINTER (id), "protected") == 0) DECL_VISIBILITY (decl) = VISIBILITY_PROTECTED; else error ("visibility arg must be one of \"default\", \"hidden\", \"protected\" or \"internal\""); return NULL_TREE; } /* Handle an "tls_model" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_tls_model_attribute (tree *node, tree name, tree args, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { tree decl = *node; if (! DECL_THREAD_LOCAL (decl)) { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } else { tree id; id = TREE_VALUE (args); if (TREE_CODE (id) != STRING_CST) { error ("tls_model arg not a string"); *no_add_attrs = true; return NULL_TREE; } if (strcmp (TREE_STRING_POINTER (id), "local-exec") && strcmp (TREE_STRING_POINTER (id), "initial-exec") && strcmp (TREE_STRING_POINTER (id), "local-dynamic") && strcmp (TREE_STRING_POINTER (id), "global-dynamic")) { error ("tls_model arg must be one of \"local-exec\", \"initial-exec\", \"local-dynamic\" or \"global-dynamic\""); *no_add_attrs = true; return NULL_TREE; } } return NULL_TREE; } /* Handle a "no_instrument_function" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_no_instrument_function_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { tree decl = *node; if (TREE_CODE (decl) != FUNCTION_DECL) { error ("%J'%E' attribute applies only to functions", decl, name); *no_add_attrs = true; } else if (DECL_INITIAL (decl)) { error ("%Jcan't set '%E' attribute after definition", decl, name); *no_add_attrs = true; } else DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl) = 1; return NULL_TREE; } /* Handle a "malloc" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_malloc_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { if (TREE_CODE (*node) == FUNCTION_DECL) DECL_IS_MALLOC (*node) = 1; /* ??? TODO: Support types. */ else { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "no_limit_stack" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_no_limit_stack_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { tree decl = *node; if (TREE_CODE (decl) != FUNCTION_DECL) { error ("%J'%E' attribute applies only to functions", decl, name); *no_add_attrs = true; } else if (DECL_INITIAL (decl)) { error ("%Jcan't set '%E' attribute after definition", decl, name); *no_add_attrs = true; } else DECL_NO_LIMIT_STACK (decl) = 1; return NULL_TREE; } /* Handle a "pure" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_pure_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { if (TREE_CODE (*node) == FUNCTION_DECL) DECL_IS_PURE (*node) = 1; /* ??? TODO: Support types. */ else { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "deprecated" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_deprecated_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags, bool *no_add_attrs) { tree type = NULL_TREE; int warn = 0; const char *what = NULL; if (DECL_P (*node)) { tree decl = *node; type = TREE_TYPE (decl); if (TREE_CODE (decl) == TYPE_DECL || TREE_CODE (decl) == PARM_DECL || TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == FUNCTION_DECL || TREE_CODE (decl) == FIELD_DECL) TREE_DEPRECATED (decl) = 1; else warn = 1; } else if (TYPE_P (*node)) { if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE)) *node = build_type_copy (*node); TREE_DEPRECATED (*node) = 1; type = *node; } else warn = 1; if (warn) { *no_add_attrs = true; if (type && TYPE_NAME (type)) { if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) what = IDENTIFIER_POINTER (TYPE_NAME (*node)); else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL && DECL_NAME (TYPE_NAME (type))) what = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); } if (what) warning ("`%s' attribute ignored for `%s'", IDENTIFIER_POINTER (name), what); else warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); } return NULL_TREE; } /* Keep a list of vector type nodes we created in handle_vector_size_attribute, to prevent us from duplicating type nodes unnecessarily. The normal mechanism to prevent duplicates is to use type_hash_canon, but since we want to distinguish types that are essentially identical (except for their debug representation), we use a local list here. */ static GTY(()) tree vector_type_node_list = 0; /* Handle a "vector_size" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_vector_size_attribute (tree *node, tree name, tree args, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { unsigned HOST_WIDE_INT vecsize, nunits; enum machine_mode mode, orig_mode, new_mode; tree type = *node, new_type = NULL_TREE; tree type_list_node; *no_add_attrs = true; if (! host_integerp (TREE_VALUE (args), 1)) { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); return NULL_TREE; } /* Get the vector size (in bytes). */ vecsize = tree_low_cst (TREE_VALUE (args), 1); /* We need to provide for vector pointers, vector arrays, and functions returning vectors. For example: __attribute__((vector_size(16))) short *foo; In this case, the mode is SI, but the type being modified is HI, so we need to look further. */ while (POINTER_TYPE_P (type) || TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE || TREE_CODE (type) == ARRAY_TYPE) type = TREE_TYPE (type); /* Get the mode of the type being modified. */ orig_mode = TYPE_MODE (type); if (TREE_CODE (type) == RECORD_TYPE || (GET_MODE_CLASS (orig_mode) != MODE_FLOAT && GET_MODE_CLASS (orig_mode) != MODE_INT) || ! host_integerp (TYPE_SIZE_UNIT (type), 1)) { error ("invalid vector type for attribute `%s'", IDENTIFIER_POINTER (name)); return NULL_TREE; } /* Calculate how many units fit in the vector. */ nunits = vecsize / tree_low_cst (TYPE_SIZE_UNIT (type), 1); /* Find a suitably sized vector. */ new_mode = VOIDmode; for (mode = GET_CLASS_NARROWEST_MODE (GET_MODE_CLASS (orig_mode) == MODE_INT ? MODE_VECTOR_INT : MODE_VECTOR_FLOAT); mode != VOIDmode; mode = GET_MODE_WIDER_MODE (mode)) if (vecsize == GET_MODE_SIZE (mode) && nunits == (unsigned HOST_WIDE_INT) GET_MODE_NUNITS (mode)) { new_mode = mode; break; } if (new_mode == VOIDmode) { error ("no vector mode with the size and type specified could be found"); return NULL_TREE; } for (type_list_node = vector_type_node_list; type_list_node; type_list_node = TREE_CHAIN (type_list_node)) { tree other_type = TREE_VALUE (type_list_node); tree record = TYPE_DEBUG_REPRESENTATION_TYPE (other_type); tree fields = TYPE_FIELDS (record); tree field_type = TREE_TYPE (fields); tree array_type = TREE_TYPE (field_type); if (TREE_CODE (fields) != FIELD_DECL || TREE_CODE (field_type) != ARRAY_TYPE) abort (); if (TYPE_MODE (other_type) == mode && type == array_type) { new_type = other_type; break; } } if (new_type == NULL_TREE) { tree index, array, rt, list_node; new_type = (*lang_hooks.types.type_for_mode) (new_mode, TREE_UNSIGNED (type)); if (!new_type) { error ("no vector mode with the size and type specified could be found"); return NULL_TREE; } new_type = build_type_copy (new_type); /* If this is a vector, make sure we either have hardware support, or we can emulate it. */ if ((GET_MODE_CLASS (mode) == MODE_VECTOR_INT || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT) && !vector_mode_valid_p (mode)) { error ("unable to emulate '%s'", GET_MODE_NAME (mode)); return NULL_TREE; } /* Set the debug information here, because this is the only place where we know the underlying type for a vector made with vector_size. For debugging purposes we pretend a vector is an array within a structure. */ index = build_int_2 (TYPE_VECTOR_SUBPARTS (new_type) - 1, 0); array = build_array_type (type, build_index_type (index)); rt = make_node (RECORD_TYPE); TYPE_FIELDS (rt) = build_decl (FIELD_DECL, get_identifier ("f"), array); DECL_CONTEXT (TYPE_FIELDS (rt)) = rt; layout_type (rt); TYPE_DEBUG_REPRESENTATION_TYPE (new_type) = rt; list_node = build_tree_list (NULL, new_type); TREE_CHAIN (list_node) = vector_type_node_list; vector_type_node_list = list_node; } /* Build back pointers if needed. */ *node = reconstruct_complex_type (*node, new_type); return NULL_TREE; } /* Handle the "nonnull" attribute. */ static tree handle_nonnull_attribute (tree *node, tree name ATTRIBUTE_UNUSED, tree args, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { tree type = *node; unsigned HOST_WIDE_INT attr_arg_num; /* If no arguments are specified, all pointer arguments should be non-null. Verify a full prototype is given so that the arguments will have the correct types when we actually check them later. */ if (! args) { if (! TYPE_ARG_TYPES (type)) { error ("nonnull attribute without arguments on a non-prototype"); *no_add_attrs = true; } return NULL_TREE; } /* Argument list specified. Verify that each argument number references a pointer argument. */ for (attr_arg_num = 1; args; args = TREE_CHAIN (args)) { tree argument; unsigned HOST_WIDE_INT arg_num, ck_num; if (! get_nonnull_operand (TREE_VALUE (args), &arg_num)) { error ("nonnull argument has invalid operand number (arg %lu)", (unsigned long) attr_arg_num); *no_add_attrs = true; return NULL_TREE; } argument = TYPE_ARG_TYPES (type); if (argument) { for (ck_num = 1; ; ck_num++) { if (! argument || ck_num == arg_num) break; argument = TREE_CHAIN (argument); } if (! argument || TREE_CODE (TREE_VALUE (argument)) == VOID_TYPE) { error ("nonnull argument with out-of-range operand number (arg %lu, operand %lu)", (unsigned long) attr_arg_num, (unsigned long) arg_num); *no_add_attrs = true; return NULL_TREE; } if (TREE_CODE (TREE_VALUE (argument)) != POINTER_TYPE) { error ("nonnull argument references non-pointer operand (arg %lu, operand %lu)", (unsigned long) attr_arg_num, (unsigned long) arg_num); *no_add_attrs = true; return NULL_TREE; } } } return NULL_TREE; } /* Check the argument list of a function call for null in argument slots that are marked as requiring a non-null pointer argument. */ static void check_function_nonnull (tree attrs, tree params) { tree a, args, param; int param_num; for (a = attrs; a; a = TREE_CHAIN (a)) { if (is_attribute_p ("nonnull", TREE_PURPOSE (a))) { args = TREE_VALUE (a); /* Walk the argument list. If we encounter an argument number we should check for non-null, do it. If the attribute has no args, then every pointer argument is checked (in which case the check for pointer type is done in check_nonnull_arg). */ for (param = params, param_num = 1; ; param_num++, param = TREE_CHAIN (param)) { if (! param) break; if (! args || nonnull_check_p (args, param_num)) check_function_arguments_recurse (check_nonnull_arg, NULL, TREE_VALUE (param), param_num); } } } } /* Helper for check_function_nonnull; given a list of operands which must be non-null in ARGS, determine if operand PARAM_NUM should be checked. */ static bool nonnull_check_p (tree args, unsigned HOST_WIDE_INT param_num) { unsigned HOST_WIDE_INT arg_num; for (; args; args = TREE_CHAIN (args)) { if (! get_nonnull_operand (TREE_VALUE (args), &arg_num)) abort (); if (arg_num == param_num) return true; } return false; } /* Check that the function argument PARAM (which is operand number PARAM_NUM) is non-null. This is called by check_function_nonnull via check_function_arguments_recurse. */ static void check_nonnull_arg (void *ctx ATTRIBUTE_UNUSED, tree param, unsigned HOST_WIDE_INT param_num) { /* Just skip checking the argument if it's not a pointer. This can happen if the "nonnull" attribute was given without an operand list (which means to check every pointer argument). */ if (TREE_CODE (TREE_TYPE (param)) != POINTER_TYPE) return; if (integer_zerop (param)) warning ("null argument where non-null required (arg %lu)", (unsigned long) param_num); } /* Helper for nonnull attribute handling; fetch the operand number from the attribute argument list. */ static bool get_nonnull_operand (tree arg_num_expr, unsigned HOST_WIDE_INT *valp) { /* Strip any conversions from the arg number and verify they are constants. */ while (TREE_CODE (arg_num_expr) == NOP_EXPR || TREE_CODE (arg_num_expr) == CONVERT_EXPR || TREE_CODE (arg_num_expr) == NON_LVALUE_EXPR) arg_num_expr = TREE_OPERAND (arg_num_expr, 0); if (TREE_CODE (arg_num_expr) != INTEGER_CST || TREE_INT_CST_HIGH (arg_num_expr) != 0) return false; *valp = TREE_INT_CST_LOW (arg_num_expr); return true; } /* Handle a "nothrow" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_nothrow_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { if (TREE_CODE (*node) == FUNCTION_DECL) TREE_NOTHROW (*node) = 1; /* ??? TODO: Support types. */ else { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "cleanup" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_cleanup_attribute (tree *node, tree name, tree args, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { tree decl = *node; tree cleanup_id, cleanup_decl; /* ??? Could perhaps support cleanups on TREE_STATIC, much like we do for global destructors in C++. This requires infrastructure that we don't have generically at the moment. It's also not a feature we'd be missing too much, since we do have attribute constructor. */ if (TREE_CODE (decl) != VAR_DECL || TREE_STATIC (decl)) { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; return NULL_TREE; } /* Verify that the argument is a function in scope. */ /* ??? We could support pointers to functions here as well, if that was considered desirable. */ cleanup_id = TREE_VALUE (args); if (TREE_CODE (cleanup_id) != IDENTIFIER_NODE) { error ("cleanup arg not an identifier"); *no_add_attrs = true; return NULL_TREE; } cleanup_decl = lookup_name (cleanup_id); if (!cleanup_decl || TREE_CODE (cleanup_decl) != FUNCTION_DECL) { error ("cleanup arg not a function"); *no_add_attrs = true; return NULL_TREE; } /* That the function has proper type is checked with the eventual call to build_function_call. */ return NULL_TREE; } /* Handle a "warn_unused_result" attribute. No special handling. */ static tree handle_warn_unused_result_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { /* Ignore the attribute for functions not returning any value. */ if (VOID_TYPE_P (TREE_TYPE (*node))) { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE; } /* Check for valid arguments being passed to a function. */ void check_function_arguments (tree attrs, tree params) { /* Check for null being passed in a pointer argument that must be non-null. We also need to do this if format checking is enabled. */ if (warn_nonnull) check_function_nonnull (attrs, params); /* Check for errors in format strings. */ if (warn_format) check_function_format (NULL, attrs, params); } /* Generic argument checking recursion routine. PARAM is the argument to be checked. PARAM_NUM is the number of the argument. CALLBACK is invoked once the argument is resolved. CTX is context for the callback. */ void check_function_arguments_recurse (void (*callback) (void *, tree, unsigned HOST_WIDE_INT), void *ctx, tree param, unsigned HOST_WIDE_INT param_num) { if (TREE_CODE (param) == NOP_EXPR) { /* Strip coercion. */ check_function_arguments_recurse (callback, ctx, TREE_OPERAND (param, 0), param_num); return; } if (TREE_CODE (param) == CALL_EXPR) { tree type = TREE_TYPE (TREE_TYPE (TREE_OPERAND (param, 0))); tree attrs; bool found_format_arg = false; /* See if this is a call to a known internationalization function that modifies a format arg. Such a function may have multiple format_arg attributes (for example, ngettext). */ for (attrs = TYPE_ATTRIBUTES (type); attrs; attrs = TREE_CHAIN (attrs)) if (is_attribute_p ("format_arg", TREE_PURPOSE (attrs))) { tree inner_args; tree format_num_expr; int format_num; int i; /* Extract the argument number, which was previously checked to be valid. */ format_num_expr = TREE_VALUE (TREE_VALUE (attrs)); while (TREE_CODE (format_num_expr) == NOP_EXPR || TREE_CODE (format_num_expr) == CONVERT_EXPR || TREE_CODE (format_num_expr) == NON_LVALUE_EXPR) format_num_expr = TREE_OPERAND (format_num_expr, 0); if (TREE_CODE (format_num_expr) != INTEGER_CST || TREE_INT_CST_HIGH (format_num_expr) != 0) abort (); format_num = TREE_INT_CST_LOW (format_num_expr); for (inner_args = TREE_OPERAND (param, 1), i = 1; inner_args != 0; inner_args = TREE_CHAIN (inner_args), i++) if (i == format_num) { check_function_arguments_recurse (callback, ctx, TREE_VALUE (inner_args), param_num); found_format_arg = true; break; } } /* If we found a format_arg attribute and did a recursive check, we are done with checking this argument. Otherwise, we continue and this will be considered a non-literal. */ if (found_format_arg) return; } if (TREE_CODE (param) == COND_EXPR) { /* Check both halves of the conditional expression. */ check_function_arguments_recurse (callback, ctx, TREE_OPERAND (param, 1), param_num); check_function_arguments_recurse (callback, ctx, TREE_OPERAND (param, 2), param_num); return; } (*callback) (ctx, param, param_num); } /* Function to help qsort sort FIELD_DECLs by name order. */ int field_decl_cmp (const void *x_p, const void *y_p) { const tree *const x = x_p; const tree *const y = y_p; if (DECL_NAME (*x) == DECL_NAME (*y)) /* A nontype is "greater" than a type. */ return (TREE_CODE (*y) == TYPE_DECL) - (TREE_CODE (*x) == TYPE_DECL); if (DECL_NAME (*x) == NULL_TREE) return -1; if (DECL_NAME (*y) == NULL_TREE) return 1; if (DECL_NAME (*x) < DECL_NAME (*y)) return -1; return 1; } static struct { gt_pointer_operator new_value; void *cookie; } resort_data; /* This routine compares two fields like field_decl_cmp but using the pointer operator in resort_data. */ static int resort_field_decl_cmp (const void *x_p, const void *y_p) { const tree *const x = x_p; const tree *const y = y_p; if (DECL_NAME (*x) == DECL_NAME (*y)) /* A nontype is "greater" than a type. */ return (TREE_CODE (*y) == TYPE_DECL) - (TREE_CODE (*x) == TYPE_DECL); if (DECL_NAME (*x) == NULL_TREE) return -1; if (DECL_NAME (*y) == NULL_TREE) return 1; { tree d1 = DECL_NAME (*x); tree d2 = DECL_NAME (*y); resort_data.new_value (&d1, resort_data.cookie); resort_data.new_value (&d2, resort_data.cookie); if (d1 < d2) return -1; } return 1; } /* Resort DECL_SORTED_FIELDS because pointers have been reordered. */ void resort_sorted_fields (void *obj, void *orig_obj ATTRIBUTE_UNUSED , gt_pointer_operator new_value, void *cookie) { struct sorted_fields_type *sf = obj; resort_data.new_value = new_value; resort_data.cookie = cookie; qsort (&sf->elts[0], sf->len, sizeof (tree), resort_field_decl_cmp); } /* Used by estimate_num_insns. Estimate number of instructions seen by given statement. */ static tree c_estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data) { int *count = data; tree x = *tp; if (TYPE_P (x) || DECL_P (x)) { *walk_subtrees = 0; return NULL; } /* Assume that constants and references counts nothing. These should be majorized by amount of operations among them we count later and are common target of CSE and similar optimizations. */ if (TREE_CODE_CLASS (TREE_CODE (x)) == 'c' || TREE_CODE_CLASS (TREE_CODE (x)) == 'r') return NULL; switch (TREE_CODE (x)) { /* Recognize assignments of large structures and constructors of big arrays. */ case MODIFY_EXPR: case CONSTRUCTOR: { HOST_WIDE_INT size; size = int_size_in_bytes (TREE_TYPE (x)); if (size < 0 || size > MOVE_MAX_PIECES * MOVE_RATIO) *count += 10; else *count += ((size + MOVE_MAX_PIECES - 1) / MOVE_MAX_PIECES); } break; case CALL_EXPR: { tree decl = get_callee_fndecl (x); if (decl && DECL_BUILT_IN (decl)) switch (DECL_FUNCTION_CODE (decl)) { case BUILT_IN_CONSTANT_P: *walk_subtrees = 0; return NULL_TREE; case BUILT_IN_EXPECT: return NULL_TREE; default: break; } *count += 10; break; } /* Few special cases of expensive operations. This is usefull to avoid inlining on functions having too many of these. */ case TRUNC_DIV_EXPR: case CEIL_DIV_EXPR: case FLOOR_DIV_EXPR: case ROUND_DIV_EXPR: case TRUNC_MOD_EXPR: case CEIL_MOD_EXPR: case FLOOR_MOD_EXPR: case ROUND_MOD_EXPR: case RDIV_EXPR: *count += 10; break; /* Various containers that will produce no code themselves. */ case INIT_EXPR: case TARGET_EXPR: case BIND_EXPR: case BLOCK: case TREE_LIST: case TREE_VEC: case IDENTIFIER_NODE: case PLACEHOLDER_EXPR: case WITH_CLEANUP_EXPR: case CLEANUP_POINT_EXPR: case NOP_EXPR: case VIEW_CONVERT_EXPR: case SAVE_EXPR: case UNSAVE_EXPR: case COMPLEX_EXPR: case REALPART_EXPR: case IMAGPART_EXPR: case TRY_CATCH_EXPR: case TRY_FINALLY_EXPR: case LABEL_EXPR: case EXIT_EXPR: case LABELED_BLOCK_EXPR: case EXIT_BLOCK_EXPR: case EXPR_WITH_FILE_LOCATION: case EXPR_STMT: case COMPOUND_STMT: case RETURN_STMT: case LABEL_STMT: case SCOPE_STMT: case FILE_STMT: case CASE_LABEL: case STMT_EXPR: case CLEANUP_STMT: case SIZEOF_EXPR: case ARROW_EXPR: case ALIGNOF_EXPR: break; case DECL_STMT: /* Do not account static initializers. */ if (TREE_STATIC (TREE_OPERAND (x, 0))) *walk_subtrees = 0; break; default: (*count)++; } return NULL; } /* Estimate number of instructions that will be created by expanding the body. */ int c_estimate_num_insns (tree decl) { int num = 0; walk_tree_without_duplicates (&DECL_SAVED_TREE (decl), c_estimate_num_insns_1, &num); return num; } /* Used by c_decl_uninit to find where expressions like x = x + 1; */ static tree c_decl_uninit_1 (tree *t, int *walk_sub_trees, void *x) { /* If x = EXP(&x)EXP, then do not warn about the use of x. */ if (TREE_CODE (*t) == ADDR_EXPR && TREE_OPERAND (*t, 0) == x) { *walk_sub_trees = 0; return NULL_TREE; } if (*t == x) return *t; return NULL_TREE; } /* Find out if a variable is uninitialized based on DECL_INITIAL. */ bool c_decl_uninit (tree t) { /* int x = x; is GCC extension to turn off this warning, only if warn_init_self is zero. */ if (DECL_INITIAL (t) == t) return warn_init_self ? true : false; /* Walk the trees looking for the variable itself. */ if (walk_tree_without_duplicates (&DECL_INITIAL (t), c_decl_uninit_1, t)) return true; return false; } /* Issue the error given by MSGID, indicating that it occurred before TOKEN, which had the associated VALUE. */ void c_parse_error (const char *msgid, enum cpp_ttype token, tree value) { const char *string = _(msgid); if (token == CPP_EOF) error ("%s at end of input", string); else if (token == CPP_CHAR || token == CPP_WCHAR) { unsigned int val = TREE_INT_CST_LOW (value); const char *const ell = (token == CPP_CHAR) ? "" : "L"; if (val <= UCHAR_MAX && ISGRAPH (val)) error ("%s before %s'%c'", string, ell, val); else error ("%s before %s'\\x%x'", string, ell, val); } else if (token == CPP_STRING || token == CPP_WSTRING) error ("%s before string constant", string); else if (token == CPP_NUMBER) error ("%s before numeric constant", string); else if (token == CPP_NAME) error ("%s before \"%s\"", string, IDENTIFIER_POINTER (value)); else if (token < N_TTYPES) error ("%s before '%s' token", string, cpp_type2name (token)); else error ("%s", string); } #include "gt-c-common.h" diff --git a/contrib/gcc/c-common.h b/contrib/gcc/c-common.h index bd8be8bcfb27..3466bff6a424 100644 --- a/contrib/gcc/c-common.h +++ b/contrib/gcc/c-common.h @@ -1,1333 +1,1335 @@ /* Definitions for c-common.c. Copyright (C) 1987, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef GCC_C_COMMON_H #define GCC_C_COMMON_H #include "splay-tree.h" #include "cpplib.h" #include "ggc.h" /* Usage of TREE_LANG_FLAG_?: 0: COMPOUND_STMT_NO_SCOPE (in COMPOUND_STMT). TREE_NEGATED_INT (in INTEGER_CST). IDENTIFIER_MARKED (used by search routines). SCOPE_BEGIN_P (in SCOPE_STMT) DECL_PRETTY_FUNCTION_P (in VAR_DECL) NEW_FOR_SCOPE_P (in FOR_STMT) ASM_INPUT_P (in ASM_STMT) STMT_EXPR_NO_SCOPE (in STMT_EXPR) 1: C_DECLARED_LABEL_FLAG (in LABEL_DECL) STMT_IS_FULL_EXPR_P (in _STMT) 2: STMT_LINENO_FOR_FN_P (in _STMT) 3: SCOPE_NO_CLEANUPS_P (in SCOPE_STMT) COMPOUND_STMT_BODY_BLOCK (in COMPOUND_STMT) STMT_EXPR_WARN_UNUSED_RESULT (in STMT_EXPR) 4: SCOPE_PARTIAL_P (in SCOPE_STMT) */ /* Reserved identifiers. This is the union of all the keywords for C, C++, and Objective-C. All the type modifiers have to be in one block at the beginning, because they are used as mask bits. There are 27 type modifiers; if we add many more we will have to redesign the mask mechanism. */ enum rid { /* Modifiers: */ /* C, in empirical order of frequency. */ RID_STATIC = 0, RID_UNSIGNED, RID_LONG, RID_CONST, RID_EXTERN, RID_REGISTER, RID_TYPEDEF, RID_SHORT, RID_INLINE, RID_VOLATILE, RID_SIGNED, RID_AUTO, RID_RESTRICT, /* C extensions */ RID_COMPLEX, RID_THREAD, /* C++ */ RID_FRIEND, RID_VIRTUAL, RID_EXPLICIT, RID_EXPORT, RID_MUTABLE, /* ObjC */ RID_IN, RID_OUT, RID_INOUT, RID_BYCOPY, RID_BYREF, RID_ONEWAY, /* C */ RID_INT, RID_CHAR, RID_FLOAT, RID_DOUBLE, RID_VOID, RID_ENUM, RID_STRUCT, RID_UNION, RID_IF, RID_ELSE, RID_WHILE, RID_DO, RID_FOR, RID_SWITCH, RID_CASE, RID_DEFAULT, RID_BREAK, RID_CONTINUE, RID_RETURN, RID_GOTO, RID_SIZEOF, /* C extensions */ RID_ASM, RID_TYPEOF, RID_ALIGNOF, RID_ATTRIBUTE, RID_VA_ARG, RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL, RID_PTRBASE, RID_PTREXTENT, RID_PTRVALUE, RID_CHOOSE_EXPR, RID_TYPES_COMPATIBLE_P, /* Too many ways of getting the name of a function as a string */ RID_FUNCTION_NAME, RID_PRETTY_FUNCTION_NAME, RID_C99_FUNCTION_NAME, /* C++ */ RID_BOOL, RID_WCHAR, RID_CLASS, RID_PUBLIC, RID_PRIVATE, RID_PROTECTED, RID_TEMPLATE, RID_NULL, RID_CATCH, RID_DELETE, RID_FALSE, RID_NAMESPACE, RID_NEW, RID_OFFSETOF, RID_OPERATOR, RID_THIS, RID_THROW, RID_TRUE, RID_TRY, RID_TYPENAME, RID_TYPEID, RID_USING, /* casts */ RID_CONSTCAST, RID_DYNCAST, RID_REINTCAST, RID_STATCAST, /* Objective-C */ RID_ID, RID_AT_ENCODE, RID_AT_END, RID_AT_CLASS, RID_AT_ALIAS, RID_AT_DEFS, RID_AT_PRIVATE, RID_AT_PROTECTED, RID_AT_PUBLIC, RID_AT_PROTOCOL, RID_AT_SELECTOR, RID_AT_THROW, RID_AT_TRY, RID_AT_CATCH, RID_AT_FINALLY, RID_AT_SYNCHRONIZED, RID_AT_INTERFACE, RID_AT_IMPLEMENTATION, RID_MAX, RID_FIRST_MODIFIER = RID_STATIC, RID_LAST_MODIFIER = RID_ONEWAY, RID_FIRST_AT = RID_AT_ENCODE, RID_LAST_AT = RID_AT_IMPLEMENTATION, RID_FIRST_PQ = RID_IN, RID_LAST_PQ = RID_ONEWAY }; #define OBJC_IS_AT_KEYWORD(rid) \ ((unsigned int)(rid) >= (unsigned int)RID_FIRST_AT && \ (unsigned int)(rid) <= (unsigned int)RID_LAST_AT) #define OBJC_IS_PQ_KEYWORD(rid) \ ((unsigned int)(rid) >= (unsigned int)RID_FIRST_PQ && \ (unsigned int)(rid) <= (unsigned int)RID_LAST_PQ) /* The elements of `ridpointers' are identifier nodes for the reserved type names and storage classes. It is indexed by a RID_... value. */ extern GTY ((length ("(int)RID_MAX"))) tree *ridpointers; /* Standard named or nameless data types of the C compiler. */ enum c_tree_index { CTI_WCHAR_TYPE, CTI_SIGNED_WCHAR_TYPE, CTI_UNSIGNED_WCHAR_TYPE, CTI_WINT_TYPE, CTI_SIGNED_SIZE_TYPE, /* For format checking only. */ CTI_UNSIGNED_PTRDIFF_TYPE, /* For format checking only. */ CTI_INTMAX_TYPE, CTI_UINTMAX_TYPE, CTI_WIDEST_INT_LIT_TYPE, CTI_WIDEST_UINT_LIT_TYPE, CTI_CHAR_ARRAY_TYPE, CTI_WCHAR_ARRAY_TYPE, CTI_INT_ARRAY_TYPE, CTI_STRING_TYPE, CTI_CONST_STRING_TYPE, /* Type for boolean expressions (bool in C++, int in C). */ CTI_TRUTHVALUE_TYPE, CTI_TRUTHVALUE_TRUE, CTI_TRUTHVALUE_FALSE, CTI_DEFAULT_FUNCTION_TYPE, CTI_G77_INTEGER_TYPE, CTI_G77_UINTEGER_TYPE, CTI_G77_LONGINT_TYPE, CTI_G77_ULONGINT_TYPE, /* These are not types, but we have to look them up all the time. */ CTI_FUNCTION_NAME_DECL, CTI_PRETTY_FUNCTION_NAME_DECL, CTI_C99_FUNCTION_NAME_DECL, CTI_SAVED_FUNCTION_NAME_DECLS, CTI_VOID_ZERO, CTI_MAX }; #define C_RID_CODE(id) (((struct c_common_identifier *) (id))->node.rid_code) /* Identifier part common to the C front ends. Inherits from tree_identifier, despite appearances. */ struct c_common_identifier GTY(()) { struct tree_common common; struct cpp_hashnode node; }; #define wchar_type_node c_global_trees[CTI_WCHAR_TYPE] #define signed_wchar_type_node c_global_trees[CTI_SIGNED_WCHAR_TYPE] #define unsigned_wchar_type_node c_global_trees[CTI_UNSIGNED_WCHAR_TYPE] #define wint_type_node c_global_trees[CTI_WINT_TYPE] #define signed_size_type_node c_global_trees[CTI_SIGNED_SIZE_TYPE] #define unsigned_ptrdiff_type_node c_global_trees[CTI_UNSIGNED_PTRDIFF_TYPE] #define intmax_type_node c_global_trees[CTI_INTMAX_TYPE] #define uintmax_type_node c_global_trees[CTI_UINTMAX_TYPE] #define widest_integer_literal_type_node c_global_trees[CTI_WIDEST_INT_LIT_TYPE] #define widest_unsigned_literal_type_node c_global_trees[CTI_WIDEST_UINT_LIT_TYPE] #define truthvalue_type_node c_global_trees[CTI_TRUTHVALUE_TYPE] #define truthvalue_true_node c_global_trees[CTI_TRUTHVALUE_TRUE] #define truthvalue_false_node c_global_trees[CTI_TRUTHVALUE_FALSE] #define char_array_type_node c_global_trees[CTI_CHAR_ARRAY_TYPE] #define wchar_array_type_node c_global_trees[CTI_WCHAR_ARRAY_TYPE] #define int_array_type_node c_global_trees[CTI_INT_ARRAY_TYPE] #define string_type_node c_global_trees[CTI_STRING_TYPE] #define const_string_type_node c_global_trees[CTI_CONST_STRING_TYPE] #define default_function_type c_global_trees[CTI_DEFAULT_FUNCTION_TYPE] /* g77 integer types, which which must be kept in sync with f/com.h */ #define g77_integer_type_node c_global_trees[CTI_G77_INTEGER_TYPE] #define g77_uinteger_type_node c_global_trees[CTI_G77_UINTEGER_TYPE] #define g77_longint_type_node c_global_trees[CTI_G77_LONGINT_TYPE] #define g77_ulongint_type_node c_global_trees[CTI_G77_ULONGINT_TYPE] #define function_name_decl_node c_global_trees[CTI_FUNCTION_NAME_DECL] #define pretty_function_name_decl_node c_global_trees[CTI_PRETTY_FUNCTION_NAME_DECL] #define c99_function_name_decl_node c_global_trees[CTI_C99_FUNCTION_NAME_DECL] #define saved_function_name_decls c_global_trees[CTI_SAVED_FUNCTION_NAME_DECLS] /* A node for `((void) 0)'. */ #define void_zero_node c_global_trees[CTI_VOID_ZERO] extern GTY(()) tree c_global_trees[CTI_MAX]; /* In a RECORD_TYPE, a sorted array of the fields of the type, not a tree for size reasons. */ struct sorted_fields_type GTY(()) { int len; tree GTY((length ("%h.len"))) elts[1]; }; /* Mark which labels are explicitly declared. These may be shadowed, and may be referenced from nested functions. */ #define C_DECLARED_LABEL_FLAG(label) TREE_LANG_FLAG_1 (label) /* Flag strings given by __FUNCTION__ and __PRETTY_FUNCTION__ for a warning if they undergo concatenation. */ #define C_ARTIFICIAL_STRING_P(NODE) TREE_LANG_FLAG_0 (NODE) typedef enum c_language_kind { clk_c = 0, /* C90, C94 or C99 */ clk_objc = 1, /* clk_c with ObjC features. */ clk_cxx = 2, /* ANSI/ISO C++ */ clk_objcxx = 3 /* clk_cxx with ObjC features. */ } c_language_kind; /* To test for a specific language use c_language, defined by each front end. For "ObjC features" or "not C++" use the macros. */ extern c_language_kind c_language; #define c_dialect_cxx() (c_language & clk_cxx) #define c_dialect_objc() (c_language & clk_objc) /* Information about a statement tree. */ struct stmt_tree_s GTY(()) { /* The last statement added to the tree. */ tree x_last_stmt; /* The type of the last expression statement. (This information is needed to implement the statement-expression extension.) */ tree x_last_expr_type; /* The last filename we recorded. */ const char *x_last_expr_filename; /* In C++, Nonzero if we should treat statements as full expressions. In particular, this variable is no-zero if at the end of a statement we should destroy any temporaries created during that statement. Similarly, if, at the end of a block, we should destroy any local variables in this block. Normally, this variable is nonzero, since those are the normal semantics of C++. However, in order to represent aggregate initialization code as tree structure, we use statement-expressions. The statements within the statement expression should not result in cleanups being run until the entire enclosing statement is complete. This flag has no effect in C. */ int stmts_are_full_exprs_p; }; typedef struct stmt_tree_s *stmt_tree; /* Global state pertinent to the current function. Some C dialects extend this structure with additional fields. */ struct c_language_function GTY(()) { /* While we are parsing the function, this contains information about the statement-tree that we are building. */ struct stmt_tree_s x_stmt_tree; /* The stack of SCOPE_STMTs for the current function. */ tree x_scope_stmt_stack; }; /* When building a statement-tree, this is the last statement added to the tree. */ #define last_tree (current_stmt_tree ()->x_last_stmt) /* The type of the last expression-statement we have seen. */ #define last_expr_type (current_stmt_tree ()->x_last_expr_type) /* The name of the last file we have seen. */ #define last_expr_filename (current_stmt_tree ()->x_last_expr_filename) /* LAST_TREE contains the last statement parsed. These are chained together through the TREE_CHAIN field, but often need to be re-organized since the parse is performed bottom-up. This macro makes LAST_TREE the indicated SUBSTMT of STMT. */ #define RECHAIN_STMTS(stmt, substmt) \ do { \ substmt = TREE_CHAIN (stmt); \ TREE_CHAIN (stmt) = NULL_TREE; \ last_tree = stmt; \ } while (0) /* Language-specific hooks. */ extern void (*lang_expand_stmt) (tree); extern void (*lang_expand_decl_stmt) (tree); extern void (*lang_expand_function_end) (void); /* Callback that determines if it's ok for a function to have no noreturn attribute. */ extern int (*lang_missing_noreturn_ok_p) (tree); extern int yyparse (void); extern stmt_tree current_stmt_tree (void); extern tree *current_scope_stmt_stack (void); extern void begin_stmt_tree (tree *); extern tree add_stmt (tree); extern void add_decl_stmt (tree); extern tree add_scope_stmt (int, int); extern void finish_stmt_tree (tree *); extern tree walk_stmt_tree (tree *, walk_tree_fn, void *); extern void prep_stmt (tree); extern void expand_stmt (tree); extern tree c_begin_if_stmt (void); extern tree c_begin_while_stmt (void); extern void c_finish_while_stmt_cond (tree, tree); extern int field_decl_cmp (const void *, const void *); extern void resort_sorted_fields (void *, void *, gt_pointer_operator, void *); /* Switches common to the C front ends. */ /* Nonzero if prepreprocessing only. */ extern int flag_preprocess_only; /* Zero means that faster, ...NonNil variants of objc_msgSend... calls will be used in ObjC; passing nil receivers to such calls will most likely result in crashes. */ extern int flag_nil_receivers; /* Nonzero means that we will allow new ObjC exception syntax (@throw, @try, etc.) in source code. */ extern int flag_objc_exceptions; /* Nonzero means that code generation will be altered to support "zero-link" execution. This currently affects ObjC only, but may affect other languages in the future. */ extern int flag_zero_link; /* Nonzero means emit an '__OBJC, __image_info' for the current translation unit. It will inform the ObjC runtime that class definition(s) herein contained are to replace one(s) previously loaded. */ extern int flag_replace_objc_classes; /* Nonzero means don't output line number information. */ extern char flag_no_line_commands; /* Nonzero causes -E output not to be done, but directives such as #define that have side effects are still obeyed. */ extern char flag_no_output; /* Nonzero means dump macros in some fashion; contains the 'D', 'M' or 'N' of the command line switch. */ extern char flag_dump_macros; /* Nonzero means pass #include lines through to the output. */ extern char flag_dump_includes; /* The file name to which we should write a precompiled header, or NULL if no header will be written in this compile. */ extern const char *pch_file; /* Nonzero if an ISO standard was selected. It rejects macros in the user's namespace. */ extern int flag_iso; /* Nonzero if -undef was given. It suppresses target built-in macros and assertions. */ extern int flag_undef; /* Nonzero means don't recognize the non-ANSI builtin functions. */ extern int flag_no_builtin; /* Nonzero means don't recognize the non-ANSI builtin functions. -ansi sets this. */ extern int flag_no_nonansi_builtin; /* Nonzero means give `double' the same size as `float'. */ extern int flag_short_double; /* Nonzero means give `wchar_t' the same size as `short'. */ extern int flag_short_wchar; /* Nonzero means allow Microsoft extensions without warnings or errors. */ extern int flag_ms_extensions; /* Nonzero means don't recognize the keyword `asm'. */ extern int flag_no_asm; /* Nonzero means give string constants the type `const char *', as mandated by the standard. */ extern int flag_const_strings; /* Nonzero means to treat bitfields as signed unless they say `unsigned'. */ extern int flag_signed_bitfields; extern int explicit_flag_signed_bitfields; /* Nonzero means warn about pointer casts that can drop a type qualifier from the pointer target type. */ extern int warn_cast_qual; /* Warn about functions which might be candidates for format attributes. */ extern int warn_missing_format_attribute; /* Nonzero means do not warn that K&R style main() is not a function prototype. */ extern int flag_bsd_no_warn_kr_main; /* Nonzero means warn about sizeof(function) or addition/subtraction of function pointers. */ extern int warn_pointer_arith; /* Nonzero means warn for any global function def without separate previous prototype decl. */ extern int warn_missing_prototypes; /* Warn if adding () is suggested. */ extern int warn_parentheses; /* Warn if initializer is not completely bracketed. */ extern int warn_missing_braces; /* Warn about comparison of signed and unsigned values. If -1, neither -Wsign-compare nor -Wno-sign-compare has been specified. */ extern int warn_sign_compare; /* Nonzero means warn about usage of long long when `-pedantic'. */ extern int warn_long_long; /* Nonzero means warn about deprecated conversion from string constant to `char *'. */ extern int warn_write_strings; /* Nonzero means warn about multiple (redundant) decls for the same single variable or function. */ extern int warn_redundant_decls; /* Warn about testing equality of floating point numbers. */ extern int warn_float_equal; /* Warn about a subscript that has type char. */ extern int warn_char_subscripts; /* Warn if a type conversion is done that might have confusing results. */ extern int warn_conversion; /* Warn about #pragma directives that are not recognized. */ extern int warn_unknown_pragmas; /* Tri state variable. */ /* Warn about format/argument anomalies in calls to formatted I/O functions (*printf, *scanf, strftime, strfmon, etc.). */ extern int warn_format; /* Warn about Y2K problems with strftime formats. */ extern int warn_format_y2k; /* Warn about excess arguments to formats. */ extern int warn_format_extra_args; /* Warn about zero-length formats. */ extern int warn_format_zero_length; /* Warn about non-literal format arguments. */ extern int warn_format_nonliteral; /* Warn about possible security problems with calls to format functions. */ extern int warn_format_security; /* C/ObjC language option variables. */ /* Nonzero means message about use of implicit function declarations; 1 means warning; 2 means error. */ extern int mesg_implicit_function_declaration; /* Nonzero means allow type mismatches in conditional expressions; just make their values `void'. */ extern int flag_cond_mismatch; /* Nonzero means enable C89 Amendment 1 features. */ extern int flag_isoc94; /* Nonzero means use the ISO C99 dialect of C. */ extern int flag_isoc99; /* Nonzero means allow the BSD kernel printf enhancments. */ extern int flag_bsd_format; /* Nonzero means that we have builtin functions, and main is an int. */ extern int flag_hosted; /* Nonzero means warn when casting a function call to a type that does not match the return type (e.g. (float)sqrt() or (anything*)malloc() when there is no previous declaration of sqrt or malloc. */ extern int warn_bad_function_cast; /* Warn about traditional constructs whose meanings changed in ANSI C. */ extern int warn_traditional; /* Nonzero means warn for a declaration found after a statement. */ extern int warn_declaration_after_statement; /* Nonzero means warn for non-prototype function decls or non-prototyped defs without previous prototype. */ extern int warn_strict_prototypes; /* Nonzero means warn for any global function def without separate previous decl. */ extern int warn_missing_declarations; /* Nonzero means warn about extern declarations of objects not at file-scope level and about *all* declarations of functions (whether extern or static) not at file-scope level. Note that we exclude implicit function declarations. To get warnings about those, use -Wimplicit. */ extern int warn_nested_externs; /* Warn if main is suspicious. */ extern int warn_main; /* Nonzero means warn about possible violations of sequence point rules. */ extern int warn_sequence_point; /* Nonzero means warn about uninitialized variable when it is initialized with itself. For example: int i = i;, GCC will not warn about this when warn_init_self is nonzero. */ extern int warn_init_self; /* Nonzero means to warn about compile-time division by zero. */ extern int warn_div_by_zero; /* Nonzero means warn about use of implicit int. */ extern int warn_implicit_int; /* Warn about NULL being passed to argument slots marked as requiring non-NULL. */ extern int warn_nonnull; /* Warn about old-style parameter declaration. */ extern int warn_old_style_definition; /* ObjC language option variables. */ /* Open and close the file for outputting class declarations, if requested (ObjC). */ extern int flag_gen_declaration; /* Generate code for GNU or NeXT runtime environment. */ extern int flag_next_runtime; /* Tells the compiler that this is a special run. Do not perform any compiling, instead we are to test some platform dependent features and output a C header file with appropriate definitions. */ extern int print_struct_values; /* ???. Undocumented. */ extern const char *constant_string_class_name; /* Warn if multiple methods are seen for the same selector, but with different argument types. Performs the check on the whole selector table at the end of compilation. */ extern int warn_selector; /* Warn if a @selector() is found, and no method with that selector has been previously declared. The check is done on each @selector() as soon as it is found - so it warns about forward declarations. */ extern int warn_undeclared_selector; /* Warn if methods required by a protocol are not implemented in the class adopting it. When turned off, methods inherited to that class are also considered implemented. */ extern int warn_protocol; /* C++ language option variables. */ /* Nonzero means don't recognize any extension keywords. */ extern int flag_no_gnu_keywords; /* Nonzero means do emit exported implementations of functions even if they can be inlined. */ extern int flag_implement_inlines; /* Nonzero means that implicit instantiations will be emitted if needed. */ extern int flag_implicit_templates; /* Nonzero means that implicit instantiations of inline templates will be emitted if needed, even if instantiations of non-inline templates aren't. */ extern int flag_implicit_inline_templates; /* Nonzero means generate separate instantiation control files and juggle them at link time. */ extern int flag_use_repository; /* Nonzero if we want to issue diagnostics that the standard says are not required. */ extern int flag_optional_diags; /* Nonzero means we should attempt to elide constructors when possible. */ extern int flag_elide_constructors; /* Nonzero means that member functions defined in class scope are inline by default. */ extern int flag_default_inline; /* Controls whether compiler generates 'type descriptor' that give run-time type information. */ extern int flag_rtti; /* Nonzero if we want to conserve space in the .o files. We do this by putting uninitialized data and runtime initialized data into .common instead of .data at the expense of not flagging multiple definitions. */ extern int flag_conserve_space; /* Nonzero if we want to obey access control semantics. */ extern int flag_access_control; /* Nonzero if we want to check the return value of new and avoid calling constructors if it is a null pointer. */ extern int flag_check_new; /* Nonzero if we want the new ISO rules for pushing a new scope for `for' initialization variables. 0: Old rules, set by -fno-for-scope. 2: New ISO rules, set by -ffor-scope. 1: Try to implement new ISO rules, but with backup compatibility (and warnings). This is the default, for now. */ extern int flag_new_for_scope; /* Nonzero if we want to emit defined symbols with common-like linkage as weak symbols where possible, in order to conform to C++ semantics. Otherwise, emit them as local symbols. */ extern int flag_weak; /* 0 means we want the preprocessor to not emit line directives for the current working directory. 1 means we want it to do it. -1 means we should decide depending on whether debugging information is being emitted or not. */ extern int flag_working_directory; /* Nonzero to use __cxa_atexit, rather than atexit, to register destructors for local statics and global objects. */ extern int flag_use_cxa_atexit; /* Nonzero means make the default pedwarns warnings instead of errors. The value of this flag is ignored if -pedantic is specified. */ extern int flag_permissive; /* Nonzero means to implement standard semantics for exception specifications, calling unexpected if an exception is thrown that doesn't match the specification. Zero means to treat them as assertions and optimize accordingly, but not check them. */ extern int flag_enforce_eh_specs; /* Nonzero means warn about things that will change when compiling with an ABI-compliant compiler. */ extern int warn_abi; /* Nonzero means warn about invalid uses of offsetof. */ extern int warn_invalid_offsetof; /* Nonzero means warn about implicit declarations. */ extern int warn_implicit; /* Nonzero means warn when all ctors or dtors are private, and the class has no friends. */ extern int warn_ctor_dtor_privacy; /* Nonzero means warn in function declared in derived class has the same name as a virtual in the base class, but fails to match the type signature of any virtual function in the base class. */ extern int warn_overloaded_virtual; /* Nonzero means warn when declaring a class that has a non virtual destructor, when it really ought to have a virtual one. */ extern int warn_nonvdtor; /* Nonzero means warn when the compiler will reorder code. */ extern int warn_reorder; /* Nonzero means warn when synthesis behavior differs from Cfront's. */ extern int warn_synth; /* Nonzero means warn when we convert a pointer to member function into a pointer to (void or function). */ extern int warn_pmf2ptr; /* Nonzero means warn about violation of some Effective C++ style rules. */ extern int warn_ecpp; /* Nonzero means warn where overload resolution chooses a promotion from unsigned to signed over a conversion to an unsigned of the same size. */ extern int warn_sign_promo; /* Nonzero means warn when an old-style cast is used. */ extern int warn_old_style_cast; /* Nonzero means warn when non-templatized friend functions are declared within a template */ extern int warn_nontemplate_friend; /* Nonzero means complain about deprecated features. */ extern int warn_deprecated; /* Maximum template instantiation depth. This limit is rather arbitrary, but it exists to limit the time it takes to notice infinite template instantiations. */ extern int max_tinst_depth; /* Nonzero means the expression being parsed will never be evaluated. This is a count, since unevaluated expressions can nest. */ extern int skip_evaluation; /* C types are partitioned into three subsets: object, function, and incomplete types. */ #define C_TYPE_OBJECT_P(type) \ (TREE_CODE (type) != FUNCTION_TYPE && TYPE_SIZE (type)) #define C_TYPE_INCOMPLETE_P(type) \ (TREE_CODE (type) != FUNCTION_TYPE && TYPE_SIZE (type) == 0) #define C_TYPE_FUNCTION_P(type) \ (TREE_CODE (type) == FUNCTION_TYPE) /* For convenience we define a single macro to identify the class of object or incomplete types. */ #define C_TYPE_OBJECT_OR_INCOMPLETE_P(type) \ (!C_TYPE_FUNCTION_P (type)) /* Record in each node resulting from a binary operator what operator was specified for it. */ #define C_EXP_ORIGINAL_CODE(exp) ((enum tree_code) TREE_COMPLEXITY (exp)) /* Attribute table common to the C front ends. */ extern const struct attribute_spec c_common_attribute_table[]; extern const struct attribute_spec c_common_format_attribute_table[]; /* Pointer to function to lazily generate the VAR_DECL for __FUNCTION__ etc. ID is the identifier to use, NAME is the string. TYPE_DEP indicates whether it depends on type of the function or not (i.e. __PRETTY_FUNCTION__). */ extern tree (*make_fname_decl) (tree, int); extern tree identifier_global_value (tree); extern void record_builtin_type (enum rid, const char *, tree); extern tree build_void_list_node (void); extern void start_fname_decls (void); extern void finish_fname_decls (void); extern const char *fname_as_string (int); extern tree fname_decl (unsigned, tree); extern void check_function_arguments (tree, tree); extern void check_function_arguments_recurse (void (*) (void *, tree, unsigned HOST_WIDE_INT), void *, tree, unsigned HOST_WIDE_INT); extern void check_function_format (int *, tree, tree); extern void set_Wformat (int); extern tree handle_format_attribute (tree *, tree, tree, int, bool *); extern tree handle_format_arg_attribute (tree *, tree, tree, int, bool *); extern int c_common_handle_option (size_t code, const char *arg, int value); extern bool c_common_missing_argument (const char *opt, size_t code); extern tree c_common_type_for_mode (enum machine_mode, int); extern tree c_common_type_for_size (unsigned int, int); extern tree c_common_unsigned_type (tree); extern tree c_common_signed_type (tree); extern tree c_common_signed_or_unsigned_type (int, tree); extern tree c_common_truthvalue_conversion (tree); extern void c_apply_type_quals_to_decl (int, tree); extern tree c_sizeof_or_alignof_type (tree, enum tree_code, int); extern tree c_alignof_expr (tree); /* Print an error message for invalid operands to arith operation CODE. NOP_EXPR is used as a special case (see truthvalue_conversion). */ extern void binary_op_error (enum tree_code); #define my_friendly_assert(EXP, N) (void) \ (((EXP) == 0) ? (fancy_abort (__FILE__, __LINE__, __FUNCTION__), 0) : 0) extern tree c_expand_expr_stmt (tree); extern void c_expand_start_cond (tree, int, tree); extern void c_finish_then (void); extern void c_expand_start_else (void); extern void c_finish_else (void); extern void c_expand_end_cond (void); /* Validate the expression after `case' and apply default promotions. */ extern tree check_case_value (tree); extern tree fix_string_type (tree); struct varray_head_tag; extern void constant_expression_warning (tree); extern tree convert_and_check (tree, tree); extern void overflow_warning (tree); extern void unsigned_conversion_warning (tree, tree); /* Read the rest of the current #-directive line. */ extern char *get_directive_line (void); #define GET_DIRECTIVE_LINE() get_directive_line () #define c_sizeof(T) c_sizeof_or_alignof_type (T, SIZEOF_EXPR, 1) #define c_alignof(T) c_sizeof_or_alignof_type (T, ALIGNOF_EXPR, 1) /* Subroutine of build_binary_op, used for comparison operations. See if the operands have both been converted from subword integer types and, if so, perhaps change them both back to their original type. */ extern tree shorten_compare (tree *, tree *, tree *, enum tree_code *); extern tree pointer_int_sum (enum tree_code, tree, tree); extern unsigned int min_precision (tree, int); /* Add qualifiers to a type, in the fashion for C. */ extern tree c_build_qualified_type (tree, int); /* Build tree nodes and builtin functions common to both C and C++ language frontends. */ extern void c_common_nodes_and_builtins (void); extern void disable_builtin_function (const char *); extern tree build_va_arg (tree, tree); extern unsigned int c_common_init_options (unsigned int, const char **); extern bool c_common_post_options (const char **); extern bool c_common_init (void); extern void c_common_finish (void); extern void c_common_parse_file (int); extern HOST_WIDE_INT c_common_get_alias_set (tree); extern void c_register_builtin_type (tree, const char*); extern bool c_promoting_integer_type_p (tree); extern int self_promoting_args_p (tree); extern tree strip_array_types (tree); extern tree strip_pointer_operator (tree); /* This function resets the parsers' state in preparation for parsing a new file. */ extern void c_reset_state (void); /* This is the basic parsing function. */ extern void c_parse_file (void); /* This is misnamed, it actually performs end-of-compilation processing. */ extern void finish_file (void); /* These macros provide convenient access to the various _STMT nodes. */ /* Nonzero if this statement should be considered a full-expression, i.e., if temporaries created during this statement should have their destructors run at the end of this statement. (In C, this will always be false, since there are no destructors.) */ #define STMT_IS_FULL_EXPR_P(NODE) TREE_LANG_FLAG_1 ((NODE)) /* IF_STMT accessors. These give access to the condition of the if statement, the then block of the if statement, and the else block of the if statement if it exists. */ #define IF_COND(NODE) TREE_OPERAND (IF_STMT_CHECK (NODE), 0) #define THEN_CLAUSE(NODE) TREE_OPERAND (IF_STMT_CHECK (NODE), 1) #define ELSE_CLAUSE(NODE) TREE_OPERAND (IF_STMT_CHECK (NODE), 2) /* WHILE_STMT accessors. These give access to the condition of the while statement and the body of the while statement, respectively. */ #define WHILE_COND(NODE) TREE_OPERAND (WHILE_STMT_CHECK (NODE), 0) #define WHILE_BODY(NODE) TREE_OPERAND (WHILE_STMT_CHECK (NODE), 1) /* DO_STMT accessors. These give access to the condition of the do statement and the body of the do statement, respectively. */ #define DO_COND(NODE) TREE_OPERAND (DO_STMT_CHECK (NODE), 0) #define DO_BODY(NODE) TREE_OPERAND (DO_STMT_CHECK (NODE), 1) /* RETURN_STMT accessors. These give the expression associated with a return statement, and whether it should be ignored when expanding (as opposed to inlining). */ #define RETURN_STMT_EXPR(NODE) TREE_OPERAND (RETURN_STMT_CHECK (NODE), 0) /* EXPR_STMT accessor. This gives the expression associated with an expression statement. */ #define EXPR_STMT_EXPR(NODE) TREE_OPERAND (EXPR_STMT_CHECK (NODE), 0) /* FOR_STMT accessors. These give access to the init statement, condition, update expression, and body of the for statement, respectively. */ #define FOR_INIT_STMT(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 0) #define FOR_COND(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 1) #define FOR_EXPR(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 2) #define FOR_BODY(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 3) /* SWITCH_STMT accessors. These give access to the condition, body and original condition type (before any compiler conversions) of the switch statement, respectively. */ #define SWITCH_COND(NODE) TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 0) #define SWITCH_BODY(NODE) TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 1) #define SWITCH_TYPE(NODE) TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 2) /* CASE_LABEL accessors. These give access to the high and low values of a case label, respectively. */ #define CASE_LOW(NODE) TREE_OPERAND (CASE_LABEL_CHECK (NODE), 0) #define CASE_HIGH(NODE) TREE_OPERAND (CASE_LABEL_CHECK (NODE), 1) #define CASE_LABEL_DECL(NODE) TREE_OPERAND (CASE_LABEL_CHECK (NODE), 2) /* GOTO_STMT accessor. This gives access to the label associated with a goto statement. */ #define GOTO_DESTINATION(NODE) TREE_OPERAND (GOTO_STMT_CHECK (NODE), 0) /* True for goto created artificially by the compiler. */ #define GOTO_FAKE_P(NODE) (TREE_LANG_FLAG_0 (GOTO_STMT_CHECK (NODE))) /* COMPOUND_STMT accessor. This gives access to the TREE_LIST of statements associated with a compound statement. The result is the first statement in the list. Succeeding nodes can be accessed by calling TREE_CHAIN on a node in the list. */ #define COMPOUND_BODY(NODE) TREE_OPERAND (COMPOUND_STMT_CHECK (NODE), 0) /* ASM_STMT accessors. ASM_STRING returns a STRING_CST for the instruction (e.g., "mov x, y"). ASM_OUTPUTS, ASM_INPUTS, and ASM_CLOBBERS represent the outputs, inputs, and clobbers for the statement. */ #define ASM_CV_QUAL(NODE) TREE_OPERAND (ASM_STMT_CHECK (NODE), 0) #define ASM_STRING(NODE) TREE_OPERAND (ASM_STMT_CHECK (NODE), 1) #define ASM_OUTPUTS(NODE) TREE_OPERAND (ASM_STMT_CHECK (NODE), 2) #define ASM_INPUTS(NODE) TREE_OPERAND (ASM_STMT_CHECK (NODE), 3) #define ASM_CLOBBERS(NODE) TREE_OPERAND (ASM_STMT_CHECK (NODE), 4) /* DECL_STMT accessor. This gives access to the DECL associated with the given declaration statement. */ #define DECL_STMT_DECL(NODE) TREE_OPERAND (DECL_STMT_CHECK (NODE), 0) /* STMT_EXPR accessor. */ #define STMT_EXPR_STMT(NODE) TREE_OPERAND (STMT_EXPR_CHECK (NODE), 0) /* Nonzero if this statement-expression does not have an associated scope. */ #define STMT_EXPR_NO_SCOPE(NODE) \ TREE_LANG_FLAG_0 (STMT_EXPR_CHECK (NODE)) /* Nonzero if this statement-expression should cause warning if its result is not used. */ #define STMT_EXPR_WARN_UNUSED_RESULT(NODE) \ TREE_LANG_FLAG_3 (STMT_EXPR_CHECK (NODE)) /* LABEL_STMT accessor. This gives access to the label associated with the given label statement. */ #define LABEL_STMT_LABEL(NODE) TREE_OPERAND (LABEL_STMT_CHECK (NODE), 0) /* COMPOUND_LITERAL_EXPR accessors. */ #define COMPOUND_LITERAL_EXPR_DECL_STMT(NODE) \ TREE_OPERAND (COMPOUND_LITERAL_EXPR_CHECK (NODE), 0) #define COMPOUND_LITERAL_EXPR_DECL(NODE) \ DECL_STMT_DECL (COMPOUND_LITERAL_EXPR_DECL_STMT (NODE)) /* Nonzero if this SCOPE_STMT is for the beginning of a scope. */ #define SCOPE_BEGIN_P(NODE) \ (TREE_LANG_FLAG_0 (SCOPE_STMT_CHECK (NODE))) /* Nonzero if this SCOPE_STMT is for the end of a scope. */ #define SCOPE_END_P(NODE) \ (!SCOPE_BEGIN_P (SCOPE_STMT_CHECK (NODE))) /* The BLOCK containing the declarations contained in this scope. */ #define SCOPE_STMT_BLOCK(NODE) \ (TREE_OPERAND (SCOPE_STMT_CHECK (NODE), 0)) /* Nonzero for a SCOPE_STMT if there were no variables in this scope. */ #define SCOPE_NULLIFIED_P(NODE) \ (SCOPE_STMT_BLOCK ((NODE)) == NULL_TREE) /* Nonzero for a SCOPE_STMT which represents a lexical scope, but which should be treated as non-existent from the point of view of running cleanup actions. */ #define SCOPE_NO_CLEANUPS_P(NODE) \ (TREE_LANG_FLAG_3 (SCOPE_STMT_CHECK (NODE))) /* Nonzero for a SCOPE_STMT if this statement is for a partial scope. For example, in: S s; l: S s2; goto l; there is (implicitly) a new scope after `l', even though there are no curly braces. In particular, when we hit the goto, we must destroy s2 and then re-construct it. For the implicit scope, SCOPE_PARTIAL_P will be set. */ #define SCOPE_PARTIAL_P(NODE) \ (TREE_LANG_FLAG_4 (SCOPE_STMT_CHECK (NODE))) /* Nonzero for an ASM_STMT if the assembly statement is volatile. */ #define ASM_VOLATILE_P(NODE) \ (ASM_CV_QUAL (ASM_STMT_CHECK (NODE)) != NULL_TREE) /* The VAR_DECL to clean up in a CLEANUP_STMT. */ #define CLEANUP_DECL(NODE) \ TREE_OPERAND (CLEANUP_STMT_CHECK (NODE), 0) /* The cleanup to run in a CLEANUP_STMT. */ #define CLEANUP_EXPR(NODE) \ TREE_OPERAND (CLEANUP_STMT_CHECK (NODE), 1) /* The filename we are changing to as of this FILE_STMT. */ #define FILE_STMT_FILENAME_NODE(NODE) \ (TREE_OPERAND (FILE_STMT_CHECK (NODE), 0)) #define FILE_STMT_FILENAME(NODE) \ (IDENTIFIER_POINTER (FILE_STMT_FILENAME_NODE (NODE))) /* The line-number at which a statement began. But if STMT_LINENO_FOR_FN_P does holds, then this macro gives the line number for the end of the current function instead. */ #define STMT_LINENO(NODE) \ (TREE_COMPLEXITY ((NODE))) /* If nonzero, the STMT_LINENO for NODE is the line at which the function ended. */ #define STMT_LINENO_FOR_FN_P(NODE) \ (TREE_LANG_FLAG_2 ((NODE))) /* Nonzero if we want the new ISO rules for pushing a new scope for `for' initialization variables. */ #define NEW_FOR_SCOPE_P(NODE) (TREE_LANG_FLAG_0 (NODE)) /* Nonzero if we want to create an ASM_INPUT instead of an ASM_OPERAND with no operands. */ #define ASM_INPUT_P(NODE) (TREE_LANG_FLAG_0 (NODE)) #define DEFTREECODE(SYM, NAME, TYPE, LENGTH) SYM, enum c_tree_code { C_DUMMY_TREE_CODE = LAST_AND_UNUSED_TREE_CODE, #include "c-common.def" LAST_C_TREE_CODE }; #undef DEFTREECODE #define c_common_stmt_codes \ CLEANUP_STMT, EXPR_STMT, COMPOUND_STMT, \ DECL_STMT, IF_STMT, FOR_STMT, \ WHILE_STMT, DO_STMT, RETURN_STMT, \ BREAK_STMT, CONTINUE_STMT, SCOPE_STMT, \ SWITCH_STMT, GOTO_STMT, LABEL_STMT, \ ASM_STMT, FILE_STMT, CASE_LABEL /* TRUE if a code represents a statement. The front end init langhook should take care of initialization of this array. */ extern bool statement_code_p[MAX_TREE_CODES]; #define STATEMENT_CODE_P(CODE) statement_code_p[(int) (CODE)] #define INIT_STATEMENT_CODES(STMT_CODES) \ do { \ unsigned int i; \ memset (&statement_code_p, 0, sizeof (statement_code_p)); \ for (i = 0; i < ARRAY_SIZE (STMT_CODES); i++) \ statement_code_p[STMT_CODES[i]] = true; \ } while (0) extern void genrtl_do_pushlevel (void); extern void genrtl_goto_stmt (tree); extern void genrtl_expr_stmt (tree); extern void genrtl_expr_stmt_value (tree, int, int); extern void genrtl_decl_stmt (tree); extern void genrtl_if_stmt (tree); extern void genrtl_while_stmt (tree); extern void genrtl_do_stmt (tree); extern void genrtl_return_stmt (tree); extern void genrtl_for_stmt (tree); extern void genrtl_break_stmt (void); extern void genrtl_continue_stmt (void); extern void genrtl_scope_stmt (tree); extern void genrtl_switch_stmt (tree); extern void genrtl_case_label (tree); extern void genrtl_compound_stmt (tree); extern void genrtl_asm_stmt (tree, tree, tree, tree, tree, int); extern void genrtl_cleanup_stmt (tree); extern int stmts_are_full_exprs_p (void); extern int anon_aggr_type_p (tree); /* For a VAR_DECL that is an anonymous union, these are the various sub-variables that make up the anonymous union. */ #define DECL_ANON_UNION_ELEMS(NODE) DECL_ARGUMENTS ((NODE)) /* In a FIELD_DECL, nonzero if the decl was originally a bitfield. */ #define DECL_C_BIT_FIELD(NODE) \ (DECL_LANG_FLAG_4 (FIELD_DECL_CHECK (NODE)) == 1) #define SET_DECL_C_BIT_FIELD(NODE) \ (DECL_LANG_FLAG_4 (FIELD_DECL_CHECK (NODE)) = 1) #define CLEAR_DECL_C_BIT_FIELD(NODE) \ (DECL_LANG_FLAG_4 (FIELD_DECL_CHECK (NODE)) = 0) /* In a VAR_DECL, nonzero if the decl is a register variable with an explicit asm specification. */ #define DECL_C_HARD_REGISTER(DECL) DECL_LANG_FLAG_4 (VAR_DECL_CHECK (DECL)) extern void emit_local_var (tree); extern void make_rtl_for_local_static (tree); extern tree expand_cond (tree); extern tree c_expand_return (tree); extern tree do_case (tree, tree); extern tree build_stmt (enum tree_code, ...); extern tree build_case_label (tree, tree, tree); extern tree build_continue_stmt (void); extern tree build_break_stmt (void); extern tree build_return_stmt (tree); #define COMPOUND_STMT_NO_SCOPE(NODE) TREE_LANG_FLAG_0 (NODE) /* Used by the C++ frontend to mark the block around the member initializers and cleanups. */ #define COMPOUND_STMT_BODY_BLOCK(NODE) TREE_LANG_FLAG_3 (NODE) extern void c_expand_asm_operands (tree, tree, tree, tree, int, location_t); /* These functions must be defined by each front-end which implements a variant of the C language. They are used in c-common.c. */ extern tree build_unary_op (enum tree_code, tree, int); extern tree build_binary_op (enum tree_code, tree, tree, int); extern int lvalue_p (tree); extern tree default_conversion (tree); /* Given two integer or real types, return the type for their sum. Given two compatible ANSI C types, returns the merged type. */ extern tree common_type (tree, tree); extern tree expand_tree_builtin (tree, tree, tree); extern tree decl_constant_value (tree); /* Handle increment and decrement of boolean types. */ extern tree boolean_increment (enum tree_code, tree); /* Hook currently used only by the C++ front end to reset internal state after entering or leaving a header file. */ extern void extract_interface_info (void); extern int case_compare (splay_tree_key, splay_tree_key); extern tree c_add_case_label (splay_tree, tree, tree, tree); extern tree build_function_call (tree, tree); extern tree finish_label_address_expr (tree); /* Same function prototype, but the C and C++ front ends have different implementations. Used in c-common.c. */ extern tree lookup_label (tree); +extern int vector_types_convertible_p (tree t1, tree t2); + extern rtx c_expand_expr (tree, rtx, enum machine_mode, int, rtx *); extern int c_safe_from_p (rtx, tree); extern int c_staticp (tree); extern int c_common_unsafe_for_reeval (tree); extern void init_c_lex (void); extern void c_cpp_builtins (cpp_reader *); /* Positive if an implicit `extern "C"' scope has just been entered; negative if such a scope has just been exited. */ extern int pending_lang_change; /* Information recorded about each file examined during compilation. */ struct c_fileinfo { int time; /* Time spent in the file. */ short interface_only; /* Flags - used only by C++ */ short interface_unknown; }; struct c_fileinfo *get_fileinfo (const char *); extern void dump_time_statistics (void); extern bool c_dump_tree (void *, tree); extern void pch_init (void); extern int c_common_valid_pch (cpp_reader *pfile, const char *name, int fd); extern void c_common_read_pch (cpp_reader *pfile, const char *name, int fd, const char *orig); extern void c_common_write_pch (void); extern void c_common_no_more_pch (void); extern void builtin_define_with_value (const char *, const char *, int); extern void c_stddef_cpp_builtins (void); extern void fe_file_change (const struct line_map *); extern int c_estimate_num_insns (tree decl); extern bool c_decl_uninit (tree t); extern void c_parse_error (const char *, enum cpp_ttype, tree); /* The following have been moved here from c-tree.h, since they're needed in the ObjC++ world, too. What is more, stub-objc.c could use a few prototypes. */ extern tree lookup_interface (tree); extern tree is_class_name (tree); extern tree objc_is_object_ptr (tree); extern void objc_check_decl (tree); extern int objc_comptypes (tree, tree, int); extern tree objc_message_selector (void); extern tree lookup_objc_ivar (tree); extern void *get_current_scope (void); extern void objc_mark_locals_volatile (void *); /* In c-ppoutput.c */ extern void init_pp_output (FILE *); extern void preprocess_file (cpp_reader *); extern void pp_file_change (const struct line_map *); extern void pp_dir_change (cpp_reader *, const char *); #endif /* ! GCC_C_COMMON_H */ diff --git a/contrib/gcc/c-decl.c b/contrib/gcc/c-decl.c index 26431d0e4836..af6d6689058c 100644 --- a/contrib/gcc/c-decl.c +++ b/contrib/gcc/c-decl.c @@ -1,6669 +1,6682 @@ /* Process declarations and variables for C compiler. Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* $FreeBSD$ */ /* Process declarations and symbol lookup for C front end. Also constructs types; the standard scalar types at initialization, and structure, union, array and enum types when they are declared. */ /* ??? not all decl nodes are given the most useful possible line numbers. For example, the CONST_DECLs for enum values. */ #include "config.h" #include "system.h" #include "coretypes.h" #include "tm.h" #include "intl.h" #include "tree.h" #include "tree-inline.h" #include "rtl.h" #include "flags.h" #include "function.h" #include "output.h" #include "expr.h" #include "c-tree.h" #include "toplev.h" #include "ggc.h" #include "tm_p.h" #include "cpplib.h" #include "target.h" #include "debug.h" #include "opts.h" #include "timevar.h" #include "c-common.h" #include "c-pragma.h" #include "cgraph.h" #include "hashtab.h" #include "libfuncs.h" #include "except.h" #include "langhooks-def.h" /* In grokdeclarator, distinguish syntactic contexts of declarators. */ enum decl_context { NORMAL, /* Ordinary declaration */ FUNCDEF, /* Function definition */ PARM, /* Declaration of parm before function body */ FIELD, /* Declaration inside struct or union */ TYPENAME}; /* Typename (inside cast or sizeof) */ /* Nonzero if we have seen an invalid cross reference to a struct, union, or enum, but not yet printed the message. */ tree pending_invalid_xref; /* File and line to appear in the eventual error message. */ location_t pending_invalid_xref_location; /* While defining an enum type, this is 1 plus the last enumerator constant value. Note that will do not have to save this or `enum_overflow' around nested function definition since such a definition could only occur in an enum value expression and we don't use these variables in that case. */ static tree enum_next_value; /* Nonzero means that there was overflow computing enum_next_value. */ static int enum_overflow; /* Parsing a function declarator leaves a list of parameter names or a chain of parameter decls here. */ static tree last_function_parms; /* ... and a chain of structure and enum types declared in the parmlist here. */ static tree last_function_parm_tags; /* ... and a chain of all non-parameter declarations (such as CONST_DECLs from enumerations) here. */ static tree last_function_parm_others; /* After parsing the declarator that starts a function definition, `start_function' puts the list of parameter names or chain of decls here for `store_parm_decls' to find. */ static tree current_function_parms; /* Similar, for last_function_parm_tags. */ static tree current_function_parm_tags; /* And for last_function_parm_others. */ static tree current_function_parm_others; /* Similar, for the file and line that the prototype came from if this is an old-style definition. */ static location_t current_function_prototype_locus; /* The current statement tree. */ static GTY(()) struct stmt_tree_s c_stmt_tree; /* The current scope statement stack. */ static GTY(()) tree c_scope_stmt_stack; /* State saving variables. */ int c_in_iteration_stmt; int c_in_case_stmt; /* A list of external DECLs that appeared at block scope when there was some other global meaning for that identifier. */ static GTY(()) tree truly_local_externals; /* All the builtins; this is a subset of the entries of global_scope. */ static GTY(()) tree first_builtin_decl; static GTY(()) tree last_builtin_decl; /* A DECL for the current file-scope context. */ static GTY(()) tree current_file_decl; /* Set to 0 at beginning of a function definition, set to 1 if a return statement that specifies a return value is seen. */ int current_function_returns_value; /* Set to 0 at beginning of a function definition, set to 1 if a return statement with no argument is seen. */ int current_function_returns_null; /* Set to 0 at beginning of a function definition, set to 1 if a call to a noreturn function is seen. */ int current_function_returns_abnormally; /* Set to nonzero by `grokdeclarator' for a function whose return type is defaulted, if warnings for this are desired. */ static int warn_about_return_type; /* Nonzero when starting a function declared `extern inline'. */ static int current_extern_inline; /* Each c_scope structure describes the complete contents of one scope. Three scopes are distinguished specially: the innermost or current scope, the innermost function scope, and the outermost or file scope. Most declarations are recorded in the current scope. All normal label declarations are recorded in the innermost function scope, as are bindings of undeclared identifiers to error_mark_node. (GCC permits nested functions as an extension, hence the 'innermost' qualifier.) Explicitly declared labels (using the __label__ extension) appear in the current scope. Being in the global scope (current_scope == global_scope) causes special behavior in several places below. Also, under some conditions the Objective-C front end records declarations in the global scope even though that isn't the current scope. The order of the names, parms, and blocks lists matters, and they are frequently appended to. To avoid having to walk all the way to the end of the list on each insertion, or reverse the lists later, we maintain a pointer to the last list entry for each of the lists. The order of the tags, shadowed, and shadowed_tags lists does not matter, so we just prepend to these lists. */ struct c_scope GTY(()) { /* The scope containing this one. */ struct c_scope *outer; /* The next outermost function scope. */ struct c_scope *outer_function; /* All variables, constants, functions, labels, and typedef names. */ tree names; tree names_last; /* All parameter declarations. Used only in the outermost scope of a function. */ tree parms; tree parms_last; /* All structure, union, and enum type tags. */ tree tags; /* For each scope, a list of shadowed outer-scope definitions to be restored when this scope is popped. Each link is a TREE_LIST whose TREE_PURPOSE is an identifier and whose TREE_VALUE is its old definition (a kind of ..._DECL node). */ tree shadowed; /* For each scope, a list of shadowed outer-scope tag definitions to be restored when this scope is popped. Each link is a TREE_LIST whose TREE_PURPOSE is an identifier and whose TREE_VALUE is its old definition (a kind of ..._TYPE node). */ tree shadowed_tags; /* For each scope (except the global one), a chain of BLOCK nodes for all the scopes that were entered and exited one level down. */ tree blocks; tree blocks_last; /* True if we are currently filling this scope with parameter declarations. */ BOOL_BITFIELD parm_flag : 1; /* True if we already complained about forward parameter decls in this scope. This prevents double warnings on foo (int a; int b; ...) */ BOOL_BITFIELD warned_forward_parm_decls : 1; /* True if this is the outermost block scope of a function body. This scope contains the parameters, the local variables declared in the outermost block, and all the labels (except those in nested functions, or declared at block scope with __label__). */ BOOL_BITFIELD function_body : 1; /* True means make a BLOCK for this scope no matter what. */ BOOL_BITFIELD keep : 1; }; /* The scope currently in effect. */ static GTY(()) struct c_scope *current_scope; /* A chain of c_scope structures awaiting reuse. */ static GTY((deletable (""))) struct c_scope *scope_freelist; /* The innermost function scope. Ordinary (not explicitly declared) labels, bindings to error_mark_node, and the lazily-created bindings of __func__ and its friends get this scope. */ static GTY(()) struct c_scope *current_function_scope; /* The outermost scope, corresponding to the C "file scope". This is created when the compiler is started and exists through the entire run. */ static GTY(()) struct c_scope *global_scope; /* Append VAR to LIST in scope SCOPE. */ #define SCOPE_LIST_APPEND(scope, list, decl) do { \ struct c_scope *s_ = (scope); \ tree d_ = (decl); \ if (s_->list##_last) \ TREE_CHAIN (s_->list##_last) = d_; \ else \ s_->list = d_; \ s_->list##_last = d_; \ } while (0) /* Concatenate FROM in scope FSCOPE onto TO in scope TSCOPE. */ #define SCOPE_LIST_CONCAT(tscope, to, fscope, from) do { \ struct c_scope *t_ = (tscope); \ struct c_scope *f_ = (fscope); \ if (t_->to##_last) \ TREE_CHAIN (t_->to##_last) = f_->from; \ else \ t_->to = f_->from; \ t_->to##_last = f_->from##_last; \ } while (0) /* True means unconditionally make a BLOCK for the next scope pushed. */ static bool keep_next_level_flag; /* True means the next call to pushlevel will be the outermost scope of a function body, so do not push a new scope, merely cease expecting parameter decls. */ static bool next_is_function_body; /* Functions called automatically at the beginning and end of execution. */ tree static_ctors, static_dtors; /* Forward declarations. */ static struct c_scope *make_scope (void); static void pop_scope (void); static tree make_label (tree, location_t); static void bind_label (tree, tree, struct c_scope *); static void implicit_decl_warning (tree); static tree lookup_tag (enum tree_code, tree, int); static tree lookup_name_current_level (tree); static tree grokdeclarator (tree, tree, enum decl_context, int, tree *); static tree grokparms (tree, int); static void layout_array_type (tree); static void store_parm_decls_newstyle (void); static void store_parm_decls_oldstyle (void); static tree c_make_fname_decl (tree, int); static void c_expand_body_1 (tree, int); static tree any_external_decl (tree); static void record_external_decl (tree); static void warn_if_shadowing (tree, tree); static void check_bitfield_type_and_width (tree *, tree *, const char *); static void clone_underlying_type (tree); static bool flexible_array_type_p (tree); static hashval_t link_hash_hash (const void *); static int link_hash_eq (const void *, const void *); /* States indicating how grokdeclarator() should handle declspecs marked with __attribute__((deprecated)). An object declared as __attribute__((deprecated)) suppresses warnings of uses of other deprecated items. */ enum deprecated_states { DEPRECATED_NORMAL, DEPRECATED_SUPPRESS }; static enum deprecated_states deprecated_state = DEPRECATED_NORMAL; void c_print_identifier (FILE *file, tree node, int indent) { print_node (file, "symbol", IDENTIFIER_SYMBOL_VALUE (node), indent + 4); print_node (file, "tag", IDENTIFIER_TAG_VALUE (node), indent + 4); print_node (file, "label", IDENTIFIER_LABEL_VALUE (node), indent + 4); if (C_IS_RESERVED_WORD (node)) { tree rid = ridpointers[C_RID_CODE (node)]; indent_to (file, indent + 4); fprintf (file, "rid " HOST_PTR_PRINTF " \"%s\"", (void *) rid, IDENTIFIER_POINTER (rid)); } } /* Hook called at end of compilation to assume 1 elt for a file-scope tentative array defn that wasn't complete before. */ void c_finish_incomplete_decl (tree decl) { if (TREE_CODE (decl) == VAR_DECL) { tree type = TREE_TYPE (decl); if (type != error_mark_node && TREE_CODE (type) == ARRAY_TYPE && ! DECL_EXTERNAL (decl) && TYPE_DOMAIN (type) == 0) { warning ("%Jarray '%D' assumed to have one element", decl, decl); complete_array_type (type, NULL_TREE, 1); layout_decl (decl, 0); } } } /* Reuse or create a struct for this scope. */ static struct c_scope * make_scope (void) { struct c_scope *result; if (scope_freelist) { result = scope_freelist; scope_freelist = result->outer; } else result = ggc_alloc_cleared (sizeof (struct c_scope)); return result; } /* Remove the topmost scope from the stack and add it to the free list, updating current_function_scope if necessary. */ static void pop_scope (void) { struct c_scope *scope = current_scope; current_scope = scope->outer; if (scope->function_body) current_function_scope = scope->outer_function; memset (scope, 0, sizeof (struct c_scope)); scope->outer = scope_freelist; scope_freelist = scope; } /* The Objective-C front-end often needs to determine the current scope. */ void * get_current_scope (void) { return current_scope; } /* The following function is used only by Objective-C. It needs to live here because it accesses the innards of c_scope. */ void objc_mark_locals_volatile (void *enclosing_blk) { struct c_scope *scope; for (scope = current_scope; scope && scope != enclosing_blk; scope = scope->outer) { tree decl; for (decl = scope->names; decl; decl = TREE_CHAIN (decl)) { DECL_REGISTER (decl) = 0; TREE_THIS_VOLATILE (decl) = 1; } /* Do not climb up past the current function. */ if (scope->function_body) break; } } /* Nonzero if we are currently in the global scope. */ int global_bindings_p (void) { return current_scope == global_scope; } void keep_next_level (void) { keep_next_level_flag = true; } /* Identify this scope as currently being filled with parameters. */ void declare_parm_level (void) { current_scope->parm_flag = true; } /* Nonzero if currently making parm declarations. */ int in_parm_level_p (void) { return current_scope->parm_flag; } /* Enter a new scope. The dummy parameter is for signature compatibility with lang_hooks.decls.pushlevel. */ void pushlevel (int dummy ATTRIBUTE_UNUSED) { if (next_is_function_body) { /* This is the transition from the parameters to the top level of the function body. These are the same scope (C99 6.2.1p4,6) so we do not push another scope structure. next_is_function_body is set only by store_parm_decls, which in turn is called when and only when we are about to encounter the opening curly brace for the function body. The outermost block of a function always gets a BLOCK node, because the debugging output routines expect that each function has at least one BLOCK. */ current_scope->parm_flag = false; current_scope->function_body = true; current_scope->keep = true; current_scope->outer_function = current_function_scope; current_function_scope = current_scope; keep_next_level_flag = false; next_is_function_body = false; } else { struct c_scope *scope = make_scope (); scope->keep = keep_next_level_flag; scope->outer = current_scope; current_scope = scope; keep_next_level_flag = false; } } /* Exit a scope. Restore the state of the identifier-decl mappings that were in effect when this scope was entered. If KEEP is KEEP_YES (1), this scope had explicit declarations, so create a BLOCK node to record its declarations and subblocks for debugging output. If KEEP is KEEP_MAYBE, do so only if the names or tags lists are nonempty. The second parameter is ignored; it is present only for signature compatibility with lang_hooks.decls.poplevel. If FUNCTIONBODY is nonzero, this level is the body of a function, even if current_scope->function_body is not set. This is used by language-independent code that generates synthetic functions, and cannot set current_scope->function_body. FIXME: Eliminate the need for all arguments. */ tree poplevel (int keep, int dummy ATTRIBUTE_UNUSED, int functionbody) { struct c_scope *scope = current_scope; tree block; tree decl; tree p; /* The following line does not use |= due to a bug in HP's C compiler. */ scope->function_body = scope->function_body | functionbody; if (keep == KEEP_MAYBE) keep = (scope->names || scope->tags); keep |= scope->keep; keep |= scope->function_body; /* If appropriate, create a BLOCK to record the decls for the life of this function. */ block = 0; if (keep) { block = make_node (BLOCK); BLOCK_VARS (block) = scope->names; BLOCK_SUBBLOCKS (block) = scope->blocks; TREE_USED (block) = 1; } /* In each subblock, record that this is its superior. */ for (p = scope->blocks; p; p = TREE_CHAIN (p)) BLOCK_SUPERCONTEXT (p) = block; /* Clear out the variable bindings in this scope. Propagate TREE_ADDRESSABLE from nested functions to their containing functions. Issue warnings for unused variables and labels, and errors for undefined labels, if there are any. */ for (p = scope->names; p; p = TREE_CHAIN (p)) { switch (TREE_CODE (p)) { case LABEL_DECL: if (TREE_USED (p) && !DECL_INITIAL (p)) { error ("%Jlabel `%D' used but not defined", p, p); DECL_INITIAL (p) = error_mark_node; } else if (!TREE_USED (p) && warn_unused_label) { if (DECL_INITIAL (p)) warning ("%Jlabel `%D' defined but not used", p, p); else warning ("%Jlabel `%D' declared but not defined", p, p); } IDENTIFIER_LABEL_VALUE (DECL_NAME (p)) = 0; break; case FUNCTION_DECL: if (! TREE_ASM_WRITTEN (p) && DECL_INITIAL (p) != 0 && TREE_ADDRESSABLE (p) && DECL_ABSTRACT_ORIGIN (p) != 0 && DECL_ABSTRACT_ORIGIN (p) != p) TREE_ADDRESSABLE (DECL_ABSTRACT_ORIGIN (p)) = 1; goto normal; case VAR_DECL: /* Keep this in sync with stmt.c:warn_about_unused_variables. No warnings when the global scope is popped because the global scope isn't popped for the last translation unit, so the warnings are done in c_write_global_declaration. */ if (warn_unused_variable && scope != global_scope && !TREE_USED (p) && !DECL_IN_SYSTEM_HEADER (p) && DECL_NAME (p) && !DECL_ARTIFICIAL (p)) warning ("%Junused variable `%D'", p, p); /* fall through */ default: normal: if (DECL_NAME (p)) { if (DECL_EXTERNAL (p) && scope != global_scope) /* External decls stay in the symbol-value slot but are inaccessible. */ C_DECL_INVISIBLE (p) = 1; else IDENTIFIER_SYMBOL_VALUE (DECL_NAME (p)) = 0; } break; } } /* Clear out the parameter bindings in this scope, if any. Unused-parameter warnings are handled by function.c. */ for (p = scope->parms; p; p = TREE_CHAIN (p)) if (DECL_NAME (p)) IDENTIFIER_SYMBOL_VALUE (DECL_NAME (p)) = 0; /* Clear out the tag-meanings declared in this scope. Set the TYPE_CONTEXTs for all of the tagged types belonging to this scope so that they point to the appropriate construct, i.e. either to the current FUNCTION_DECL node, or else to the BLOCK node we just constructed. Note that for tagged types whose scope is just the formal parameter list for some function type specification, we can't properly set their TYPE_CONTEXTs here, because we don't have a pointer to the appropriate FUNCTION_TYPE node readily available to us. For those cases, the TYPE_CONTEXTs of the relevant tagged type nodes get set in `grokdeclarator' as soon as we have created the FUNCTION_TYPE node which will represent the "scope" for these "parameter list local" tagged types. */ decl = scope->function_body ? current_function_decl : block; for (p = scope->tags; p; p = TREE_CHAIN (p)) { if (TREE_PURPOSE (p)) IDENTIFIER_TAG_VALUE (TREE_PURPOSE (p)) = 0; if (decl) TYPE_CONTEXT (TREE_VALUE (p)) = decl; } /* Restore all name- and label-meanings from outer scopes that were shadowed by this scope. */ for (p = scope->shadowed; p; p = TREE_CHAIN (p)) if (TREE_VALUE (p) && TREE_CODE (TREE_VALUE (p)) == LABEL_DECL) IDENTIFIER_LABEL_VALUE (TREE_PURPOSE (p)) = TREE_VALUE (p); else IDENTIFIER_SYMBOL_VALUE (TREE_PURPOSE (p)) = TREE_VALUE (p); /* Restore all tag-meanings from outer scopes that were shadowed by this scope. */ for (p = scope->shadowed_tags; p; p = TREE_CHAIN (p)) IDENTIFIER_TAG_VALUE (TREE_PURPOSE (p)) = TREE_VALUE (p); /* Dispose of the block that we just made inside some higher level. */ if (scope->function_body && current_function_decl) DECL_INITIAL (current_function_decl) = block; else if (scope->outer) { if (block) SCOPE_LIST_APPEND (scope->outer, blocks, block); /* If we did not make a block for the scope just exited, any blocks made for inner scopes must be carried forward so they will later become subblocks of something else. */ else if (scope->blocks) SCOPE_LIST_CONCAT (scope->outer, blocks, scope, blocks); } /* Pop the current scope, and free the structure for reuse. */ pop_scope (); return block; } /* Insert BLOCK at the end of the list of subblocks of the current scope. This is used when a BIND_EXPR is expanded, to handle the BLOCK node inside the BIND_EXPR. */ void insert_block (tree block) { TREE_USED (block) = 1; SCOPE_LIST_APPEND (current_scope, blocks, block); } /* Set the BLOCK node for the innermost scope (the one we are currently in). The RTL expansion machinery requires us to provide this hook, but it is not useful in function-at-a-time mode. */ void set_block (tree block ATTRIBUTE_UNUSED) { } /* Push a definition or a declaration of struct, union or enum tag "name". "type" should be the type node. We assume that the tag "name" is not already defined. Note that the definition may really be just a forward reference. In that case, the TYPE_SIZE will be zero. */ void pushtag (tree name, tree type) { struct c_scope *b = current_scope; /* Record the identifier as the type's name if it has none. */ if (name) { if (TYPE_NAME (type) == 0) TYPE_NAME (type) = name; if (IDENTIFIER_TAG_VALUE (name)) b->shadowed_tags = tree_cons (name, IDENTIFIER_TAG_VALUE (name), b->shadowed_tags); IDENTIFIER_TAG_VALUE (name) = type; } b->tags = tree_cons (name, type, b->tags); /* Create a fake NULL-named TYPE_DECL node whose TREE_TYPE will be the tagged type we just added to the current scope. This fake NULL-named TYPE_DECL node helps dwarfout.c to know when it needs to output a representation of a tagged type, and it also gives us a convenient place to record the "scope start" address for the tagged type. */ TYPE_STUB_DECL (type) = pushdecl (build_decl (TYPE_DECL, NULL_TREE, type)); /* An approximation for now, so we can tell this is a function-scope tag. This will be updated in poplevel. */ TYPE_CONTEXT (type) = DECL_CONTEXT (TYPE_STUB_DECL (type)); } /* Subroutine of compare_decls. Allow harmless mismatches in return and argument types provided that the type modes match. This function return a unified type given a suitable match, and 0 otherwise. */ static tree match_builtin_function_types (tree newtype, tree oldtype) { tree newrettype, oldrettype; tree newargs, oldargs; tree trytype, tryargs; /* Accept the return type of the new declaration if same modes. */ oldrettype = TREE_TYPE (oldtype); newrettype = TREE_TYPE (newtype); if (TYPE_MODE (oldrettype) != TYPE_MODE (newrettype)) return 0; oldargs = TYPE_ARG_TYPES (oldtype); newargs = TYPE_ARG_TYPES (newtype); tryargs = newargs; while (oldargs || newargs) { if (! oldargs || ! newargs || ! TREE_VALUE (oldargs) || ! TREE_VALUE (newargs) || TYPE_MODE (TREE_VALUE (oldargs)) != TYPE_MODE (TREE_VALUE (newargs))) return 0; oldargs = TREE_CHAIN (oldargs); newargs = TREE_CHAIN (newargs); } trytype = build_function_type (newrettype, tryargs); return build_type_attribute_variant (trytype, TYPE_ATTRIBUTES (oldtype)); } /* Subroutine of diagnose_mismathed_decls. Check for function type mismatch involving an empty arglist vs a nonempty one and give clearer diagnostics. */ static void diagnose_arglist_conflict (tree newdecl, tree olddecl, tree newtype, tree oldtype) { tree t; if (TREE_CODE (olddecl) != FUNCTION_DECL || !comptypes (TREE_TYPE (oldtype), TREE_TYPE (newtype), COMPARE_STRICT) || !((TYPE_ARG_TYPES (oldtype) == 0 && DECL_INITIAL (olddecl) == 0) || (TYPE_ARG_TYPES (newtype) == 0 && DECL_INITIAL (newdecl) == 0))) return; t = TYPE_ARG_TYPES (oldtype); if (t == 0) t = TYPE_ARG_TYPES (newtype); for (; t; t = TREE_CHAIN (t)) { tree type = TREE_VALUE (t); if (TREE_CHAIN (t) == 0 && TYPE_MAIN_VARIANT (type) != void_type_node) { inform ("a parameter list with an ellipsis can't match " "an empty parameter name list declaration"); break; } if (c_type_promotes_to (type) != type) { inform ("an argument type that has a default promotion can't match " "an empty parameter name list declaration"); break; } } } /* Another subroutine of diagnose_mismatched_decls. OLDDECL is an old-style function definition, NEWDECL is a prototype declaration. Diagnose inconsistencies in the argument list. Returns TRUE if the prototype is compatible, FALSE if not. */ static bool validate_proto_after_old_defn (tree newdecl, tree newtype, tree oldtype) { tree newargs, oldargs; int i; /* ??? Elsewhere TYPE_MAIN_VARIANT is not used in this context. */ #define END_OF_ARGLIST(t) (TYPE_MAIN_VARIANT (t) == void_type_node) oldargs = TYPE_ACTUAL_ARG_TYPES (oldtype); newargs = TYPE_ARG_TYPES (newtype); i = 1; for (;;) { tree oldargtype = TREE_VALUE (oldargs); tree newargtype = TREE_VALUE (newargs); if (END_OF_ARGLIST (oldargtype) && END_OF_ARGLIST (newargtype)) break; /* Reaching the end of just one list means the two decls don't agree on the number of arguments. */ if (END_OF_ARGLIST (oldargtype)) { error ("%Jprototype for '%D' declares more arguments " "than previous old-style definition", newdecl, newdecl); return false; } else if (END_OF_ARGLIST (newargtype)) { error ("%Jprototype for '%D' declares fewer arguments " "than previous old-style definition", newdecl, newdecl); return false; } /* Type for passing arg must be consistent with that declared for the arg. */ else if (! comptypes (oldargtype, newargtype, COMPARE_STRICT)) { error ("%Jprototype for '%D' declares arg %d with incompatible type", newdecl, newdecl, i); return false; } oldargs = TREE_CHAIN (oldargs); newargs = TREE_CHAIN (newargs); i++; } /* If we get here, no errors were found, but do issue a warning for this poor-style construct. */ warning ("%Jprototype for '%D' follows non-prototype definition", newdecl, newdecl); return true; #undef END_OF_ARGLIST } /* Subroutine of diagnose_mismatched_decls. Report the location of DECL, first in a pair of mismatched declarations, using the diagnostic function DIAG. */ static void locate_old_decl (tree decl, void (*diag)(const char *, ...)) { if (TREE_CODE (decl) == FUNCTION_DECL && DECL_BUILT_IN (decl)) ; else if (DECL_INITIAL (decl)) diag (N_("%Jprevious definition of '%D' was here"), decl, decl); else if (C_DECL_IMPLICIT (decl)) diag (N_("%Jprevious implicit declaration of '%D' was here"), decl, decl); else diag (N_("%Jprevious declaration of '%D' was here"), decl, decl); } /* Subroutine of duplicate_decls. Compare NEWDECL to OLDDECL. Returns true if the caller should proceed to merge the two, false if OLDDECL should simply be discarded. As a side effect, issues all necessary diagnostics for invalid or poor-style combinations. If it returns true, writes the types of NEWDECL and OLDDECL to *NEWTYPEP and *OLDTYPEP - these may have been adjusted from TREE_TYPE (NEWDECL, OLDDECL) respectively. */ static bool diagnose_mismatched_decls (tree newdecl, tree olddecl, tree *newtypep, tree *oldtypep) { tree newtype, oldtype; bool pedwarned = false; bool warned = false; /* If we have error_mark_node for either decl or type, just discard the previous decl - we're in an error cascade already. */ if (olddecl == error_mark_node || newdecl == error_mark_node) return false; *oldtypep = oldtype = TREE_TYPE (olddecl); *newtypep = newtype = TREE_TYPE (newdecl); if (oldtype == error_mark_node || newtype == error_mark_node) return false; /* Two different categories of symbol altogether. This is an error unless OLDDECL is a builtin. OLDDECL will be discarded in any case. */ if (TREE_CODE (olddecl) != TREE_CODE (newdecl)) { if (TREE_CODE (olddecl) != FUNCTION_DECL || !DECL_BUILT_IN (olddecl) || !C_DECL_INVISIBLE (olddecl)) { error ("%J'%D' redeclared as different kind of symbol", newdecl, newdecl); locate_old_decl (olddecl, error); } else if (TREE_PUBLIC (newdecl)) warning ("%Jbuilt-in function '%D' declared as non-function", newdecl, newdecl); else if (warn_shadow) warning ("%Jshadowing built-in function '%D'", newdecl, newdecl); return false; } + /* Enumerators have no linkage, so may only be declared once in a + given scope. */ + if (TREE_CODE (olddecl) == CONST_DECL) + { + error ("%Jredeclaration of enumerator `%D'", newdecl, newdecl); + locate_old_decl (olddecl, error); + return false; + } + if (!comptypes (oldtype, newtype, COMPARE_STRICT)) { if (TREE_CODE (olddecl) == FUNCTION_DECL && DECL_BUILT_IN (olddecl) && C_DECL_INVISIBLE (olddecl)) { /* Accept harmless mismatch in function types. This is for the ffs and fprintf builtins. */ tree trytype = match_builtin_function_types (newtype, oldtype); if (trytype && comptypes (newtype, trytype, COMPARE_STRICT)) *oldtypep = oldtype = trytype; else { /* If types don't match for a built-in, throw away the built-in. No point in calling locate_old_decl here, it won't print anything. */ warning ("%Jconflicting types for built-in function '%D'", newdecl, newdecl); return false; } } else if (TREE_CODE (olddecl) == FUNCTION_DECL && DECL_SOURCE_LINE (olddecl) == 0) { /* A conflicting function declaration for a predeclared function that isn't actually built in. Objective C uses these. The new declaration silently overrides everything but the volatility (i.e. noreturn) indication. See also below. FIXME: Make Objective C use normal builtins. */ TREE_THIS_VOLATILE (newdecl) |= TREE_THIS_VOLATILE (olddecl); return false; } /* Permit void foo (...) to match int foo (...) if the latter is the definition and implicit int was used. See c-torture/compile/920625-2.c. */ else if (TREE_CODE (newdecl) == FUNCTION_DECL && DECL_INITIAL (newdecl) && TYPE_MAIN_VARIANT (TREE_TYPE (oldtype)) == void_type_node && TYPE_MAIN_VARIANT (TREE_TYPE (newtype)) == integer_type_node && C_FUNCTION_IMPLICIT_INT (newdecl)) { pedwarn ("%Jconflicting types for '%D'", newdecl, newdecl); /* Make sure we keep void as the return type. */ TREE_TYPE (newdecl) = *newtypep = newtype = oldtype; C_FUNCTION_IMPLICIT_INT (newdecl) = 0; pedwarned = true; } else { error ("%Jconflicting types for '%D'", newdecl, newdecl); diagnose_arglist_conflict (newdecl, olddecl, newtype, oldtype); locate_old_decl (olddecl, error); return false; } } /* Redeclaration of a type is a constraint violation (6.7.2.3p1), but silently ignore the redeclaration if either is in a system header. (Conflicting redeclarations were handled above.) */ if (TREE_CODE (newdecl) == TYPE_DECL) { if (DECL_IN_SYSTEM_HEADER (newdecl) || DECL_IN_SYSTEM_HEADER (olddecl)) return true; /* allow OLDDECL to continue in use */ error ("%Jredefinition of typedef '%D'", newdecl, newdecl); locate_old_decl (olddecl, error); return false; } /* Function declarations can either be 'static' or 'extern' (no qualifier is equivalent to 'extern' - C99 6.2.2p5) and therefore can never conflict with each other on account of linkage (6.2.2p4). Multiple definitions are not allowed (6.9p3,5) but GCC permits two definitions if one is 'extern inline' and one is not. The non- extern-inline definition supersedes the extern-inline definition. */ else if (TREE_CODE (newdecl) == FUNCTION_DECL) { /* If you declare a built-in function name as static, or define the built-in with an old-style definition (so we can't validate the argument list) the built-in definition is overridden, but optionally warn this was a bad choice of name. */ if (DECL_BUILT_IN (olddecl) && C_DECL_INVISIBLE (olddecl) && (!TREE_PUBLIC (newdecl) || (DECL_INITIAL (newdecl) && !TYPE_ARG_TYPES (TREE_TYPE (newdecl))))) { if (warn_shadow) warning ("%Jshadowing built-in function '%D'", newdecl, newdecl); /* Discard the old built-in function. */ return false; } if (DECL_INITIAL (newdecl)) { if (DECL_INITIAL (olddecl) && !(DECL_DECLARED_INLINE_P (olddecl) && DECL_EXTERNAL (olddecl) && !(DECL_DECLARED_INLINE_P (newdecl) && DECL_EXTERNAL (newdecl)))) { error ("%Jredefinition of '%D'", newdecl, newdecl); locate_old_decl (olddecl, error); return false; } } /* If we have a prototype after an old-style function definition, the argument types must be checked specially. */ else if (DECL_INITIAL (olddecl) && !TYPE_ARG_TYPES (oldtype) && TYPE_ARG_TYPES (newtype) && TYPE_ACTUAL_ARG_TYPES (oldtype) && !validate_proto_after_old_defn (newdecl, newtype, oldtype)) { locate_old_decl (olddecl, error); return false; } /* Mismatched non-static and static is considered poor style. We only diagnose static then non-static if -Wtraditional, because it is the most convenient way to get some effects (see e.g. what unwind-dw2-fde-glibc.c does to the definition of _Unwind_Find_FDE in unwind-dw2-fde.c). Revisit? */ if (TREE_PUBLIC (olddecl) && !TREE_PUBLIC (newdecl)) { /* A static function declaration for a predeclared function that isn't actually built in, silently overrides the default. Objective C uses these. See also above. FIXME: Make Objective C use normal builtins. */ if (TREE_CODE (olddecl) == FUNCTION_DECL && DECL_SOURCE_LINE (olddecl) == 0) return false; else { warning ("%Jstatic declaration of '%D' follows " "non-static declaration", newdecl, newdecl); warned = true; } } else if (TREE_PUBLIC (newdecl) && !TREE_PUBLIC (olddecl) && warn_traditional) { warning ("%Jnon-static declaration of '%D' follows " "static declaration", newdecl, newdecl); warned = true; } } else if (TREE_CODE (newdecl) == VAR_DECL) { /* Only variables can be thread-local, and all declarations must agree on this property. */ if (DECL_THREAD_LOCAL (newdecl) != DECL_THREAD_LOCAL (olddecl)) { if (DECL_THREAD_LOCAL (newdecl)) error ("%Jthread-local declaration of '%D' follows " "non-thread-local declaration", newdecl, newdecl); else error ("%Jnon-thread-local declaration of '%D' follows " "thread-local declaration", newdecl, newdecl); locate_old_decl (olddecl, error); return false; } /* Multiple initialized definitions are not allowed (6.9p3,5). */ if (DECL_INITIAL (newdecl) && DECL_INITIAL (olddecl)) { error ("%Jredefinition of '%D'", newdecl, newdecl); locate_old_decl (olddecl, error); return false; } /* Objects declared at file scope: if at least one is 'extern', it's fine (6.2.2p4); otherwise the linkage must agree (6.2.2p7). */ if (DECL_FILE_SCOPE_P (newdecl)) { if (!DECL_EXTERNAL (newdecl) && !DECL_EXTERNAL (olddecl) && TREE_PUBLIC (newdecl) != TREE_PUBLIC (olddecl)) { if (TREE_PUBLIC (newdecl)) error ("%Jnon-static declaration of '%D' follows " "static declaration", newdecl, newdecl); else error ("%Jstatic declaration of '%D' follows " "non-static declaration", newdecl, newdecl); locate_old_decl (olddecl, error); return false; } } /* Two objects with the same name declared at the same block scope must both be external references (6.7p3). */ else if (DECL_CONTEXT (newdecl) == DECL_CONTEXT (olddecl) && (!DECL_EXTERNAL (newdecl) || !DECL_EXTERNAL (olddecl))) { if (DECL_EXTERNAL (newdecl)) error ("%Jextern declaration of '%D' follows " "declaration with no linkage", newdecl, newdecl); else if (DECL_EXTERNAL (olddecl)) error ("%Jdeclaration of '%D' with no linkage follows " "extern declaration", newdecl, newdecl); else error ("%Jredeclaration of '%D' with no linkage", newdecl, newdecl); locate_old_decl (olddecl, error); return false; } } /* warnings */ /* All decls must agree on a non-default visibility. */ if (DECL_VISIBILITY (newdecl) != VISIBILITY_DEFAULT && DECL_VISIBILITY (olddecl) != VISIBILITY_DEFAULT && DECL_VISIBILITY (newdecl) != DECL_VISIBILITY (olddecl)) { warning ("%Jredeclaration of '%D' with different visibility " "(old visibility preserved)", newdecl, newdecl); warned = true; } if (TREE_CODE (newdecl) == FUNCTION_DECL) { /* Diagnose inline __attribute__ ((noinline)) which is silly. */ if (DECL_DECLARED_INLINE_P (newdecl) && lookup_attribute ("noinline", DECL_ATTRIBUTES (olddecl))) { warning ("%Jinline declaration of '%D' follows " "declaration with attribute noinline", newdecl, newdecl); warned = true; } else if (DECL_DECLARED_INLINE_P (olddecl) && lookup_attribute ("noinline", DECL_ATTRIBUTES (newdecl))) { warning ("%Jdeclaration of '%D' with attribute noinline follows " "inline declaration ", newdecl, newdecl); warned = true; } /* Inline declaration after use or definition. ??? Should we still warn about this now we have unit-at-a-time mode and can get it right? */ if (DECL_DECLARED_INLINE_P (newdecl) && !DECL_DECLARED_INLINE_P (olddecl)) { if (TREE_USED (olddecl)) { warning ("%J'%D' declared inline after being called", olddecl, olddecl); warned = true; } else if (DECL_INITIAL (olddecl)) { warning ("%J'%D' declared inline after its definition", olddecl, olddecl); warned = true; } } } else /* PARM_DECL, VAR_DECL */ { /* Redeclaration of a PARM_DECL is invalid unless this is the real position of a forward-declared parameter (GCC extension). */ if (TREE_CODE (newdecl) == PARM_DECL && (!TREE_ASM_WRITTEN (olddecl) || TREE_ASM_WRITTEN (newdecl))) { error ("%Jredefinition of parameter '%D'", newdecl, newdecl); locate_old_decl (olddecl, error); return false; } /* These bits are only type qualifiers when applied to objects. */ if (TREE_THIS_VOLATILE (newdecl) != TREE_THIS_VOLATILE (olddecl)) { if (TREE_THIS_VOLATILE (newdecl)) pedwarn ("%Jvolatile declaration of '%D' follows " "non-volatile declaration", newdecl, newdecl); else pedwarn ("%Jnon-volatile declaration of '%D' follows " "volatile declaration", newdecl, newdecl); pedwarned = true; } if (TREE_READONLY (newdecl) != TREE_READONLY (olddecl)) { if (TREE_READONLY (newdecl)) pedwarn ("%Jconst declaration of '%D' follows " "non-const declaration", newdecl, newdecl); else pedwarn ("%Jnon-const declaration of '%D' follows " "const declaration", newdecl, newdecl); pedwarned = true; } } /* Optional warning for completely redundant decls. */ if (!warned && !pedwarned && warn_redundant_decls /* Don't warn about a function declaration followed by a definition. */ && !(TREE_CODE (newdecl) == FUNCTION_DECL && DECL_INITIAL (newdecl) && !DECL_INITIAL (olddecl)) /* Don't warn about redundant redeclarations of builtins. */ && !(TREE_CODE (newdecl) == FUNCTION_DECL && !DECL_BUILT_IN (newdecl) && DECL_BUILT_IN (olddecl) && C_DECL_INVISIBLE (olddecl)) /* Don't warn about an extern followed by a definition. */ && !(DECL_EXTERNAL (olddecl) && !DECL_EXTERNAL (newdecl)) /* Don't warn about forward parameter decls. */ && !(TREE_CODE (newdecl) == PARM_DECL && TREE_ASM_WRITTEN (olddecl) && !TREE_ASM_WRITTEN (newdecl)) /* Don't warn about a variable definition following a declaration. */ && !(TREE_CODE (newdecl) == VAR_DECL && DECL_INITIAL (newdecl) && !DECL_INITIAL (olddecl))) { warning ("%Jredundant redeclaration of '%D'", newdecl, newdecl); warned = true; } /* Report location of previous decl/defn in a consistent manner. */ if (warned || pedwarned) locate_old_decl (olddecl, pedwarned ? pedwarn : warning); return true; } /* Subroutine of duplicate_decls. NEWDECL has been found to be consistent with OLDDECL, but carries new information. Merge the new information into OLDDECL. This function issues no diagnostics. */ static void merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype) { int new_is_definition = (TREE_CODE (newdecl) == FUNCTION_DECL && DECL_INITIAL (newdecl) != 0); /* For real parm decl following a forward decl, return 1 so old decl will be reused. Only allow this to happen once. */ if (TREE_CODE (newdecl) == PARM_DECL && TREE_ASM_WRITTEN (olddecl) && ! TREE_ASM_WRITTEN (newdecl)) { TREE_ASM_WRITTEN (olddecl) = 0; return; } DECL_ATTRIBUTES (newdecl) = (*targetm.merge_decl_attributes) (olddecl, newdecl); /* Merge the data types specified in the two decls. */ TREE_TYPE (newdecl) = TREE_TYPE (olddecl) = common_type (newtype, oldtype); /* Lay the type out, unless already done. */ if (oldtype != TREE_TYPE (newdecl)) { if (TREE_TYPE (newdecl) != error_mark_node) layout_type (TREE_TYPE (newdecl)); if (TREE_CODE (newdecl) != FUNCTION_DECL && TREE_CODE (newdecl) != TYPE_DECL && TREE_CODE (newdecl) != CONST_DECL) layout_decl (newdecl, 0); } else { /* Since the type is OLDDECL's, make OLDDECL's size go with. */ DECL_SIZE (newdecl) = DECL_SIZE (olddecl); DECL_SIZE_UNIT (newdecl) = DECL_SIZE_UNIT (olddecl); DECL_MODE (newdecl) = DECL_MODE (olddecl); if (TREE_CODE (olddecl) != FUNCTION_DECL) if (DECL_ALIGN (olddecl) > DECL_ALIGN (newdecl)) { DECL_ALIGN (newdecl) = DECL_ALIGN (olddecl); DECL_USER_ALIGN (newdecl) |= DECL_ALIGN (olddecl); } } /* Keep the old rtl since we can safely use it. */ COPY_DECL_RTL (olddecl, newdecl); /* Merge the type qualifiers. */ if (TREE_READONLY (newdecl)) TREE_READONLY (olddecl) = 1; if (TREE_THIS_VOLATILE (newdecl)) { TREE_THIS_VOLATILE (olddecl) = 1; if (TREE_CODE (newdecl) == VAR_DECL) make_var_volatile (newdecl); } /* Keep source location of definition rather than declaration. */ if (DECL_INITIAL (newdecl) == 0 && DECL_INITIAL (olddecl) != 0) DECL_SOURCE_LOCATION (newdecl) = DECL_SOURCE_LOCATION (olddecl); /* Merge the unused-warning information. */ if (DECL_IN_SYSTEM_HEADER (olddecl)) DECL_IN_SYSTEM_HEADER (newdecl) = 1; else if (DECL_IN_SYSTEM_HEADER (newdecl)) DECL_IN_SYSTEM_HEADER (olddecl) = 1; /* Merge the initialization information. */ if (DECL_INITIAL (newdecl) == 0) DECL_INITIAL (newdecl) = DECL_INITIAL (olddecl); /* Merge the section attribute. We want to issue an error if the sections conflict but that must be done later in decl_attributes since we are called before attributes are assigned. */ if (DECL_SECTION_NAME (newdecl) == NULL_TREE) DECL_SECTION_NAME (newdecl) = DECL_SECTION_NAME (olddecl); /* Copy the assembler name. Currently, it can only be defined in the prototype. */ COPY_DECL_ASSEMBLER_NAME (olddecl, newdecl); /* If either declaration has a nondefault visibility, use it. */ if (DECL_VISIBILITY (olddecl) != VISIBILITY_DEFAULT) DECL_VISIBILITY (newdecl) = DECL_VISIBILITY (olddecl); if (TREE_CODE (newdecl) == FUNCTION_DECL) { DECL_STATIC_CONSTRUCTOR(newdecl) |= DECL_STATIC_CONSTRUCTOR(olddecl); DECL_STATIC_DESTRUCTOR (newdecl) |= DECL_STATIC_DESTRUCTOR (olddecl); DECL_NO_LIMIT_STACK (newdecl) |= DECL_NO_LIMIT_STACK (olddecl); DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (newdecl) |= DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (olddecl); TREE_THIS_VOLATILE (newdecl) |= TREE_THIS_VOLATILE (olddecl); TREE_READONLY (newdecl) |= TREE_READONLY (olddecl); DECL_IS_MALLOC (newdecl) |= DECL_IS_MALLOC (olddecl); DECL_IS_PURE (newdecl) |= DECL_IS_PURE (olddecl); } /* Merge the storage class information. */ merge_weak (newdecl, olddecl); /* For functions, static overrides non-static. */ if (TREE_CODE (newdecl) == FUNCTION_DECL) { TREE_PUBLIC (newdecl) &= TREE_PUBLIC (olddecl); /* This is since we don't automatically copy the attributes of NEWDECL into OLDDECL. */ TREE_PUBLIC (olddecl) = TREE_PUBLIC (newdecl); /* If this clears `static', clear it in the identifier too. */ if (! TREE_PUBLIC (olddecl)) TREE_PUBLIC (DECL_NAME (olddecl)) = 0; } if (DECL_EXTERNAL (newdecl)) { TREE_STATIC (newdecl) = TREE_STATIC (olddecl); DECL_EXTERNAL (newdecl) = DECL_EXTERNAL (olddecl); /* An extern decl does not override previous storage class. */ TREE_PUBLIC (newdecl) = TREE_PUBLIC (olddecl); if (! DECL_EXTERNAL (newdecl)) { DECL_CONTEXT (newdecl) = DECL_CONTEXT (olddecl); DECL_COMMON (newdecl) = DECL_COMMON (olddecl); } } else { TREE_STATIC (olddecl) = TREE_STATIC (newdecl); TREE_PUBLIC (olddecl) = TREE_PUBLIC (newdecl); } if (TREE_CODE (newdecl) == FUNCTION_DECL) { /* If we're redefining a function previously defined as extern inline, make sure we emit debug info for the inline before we throw it away, in case it was inlined into a function that hasn't been written out yet. */ if (new_is_definition && DECL_INITIAL (olddecl)) { if (TREE_USED (olddecl) /* In unit-at-a-time mode we never inline re-defined extern inline functions. */ && !flag_unit_at_a_time && cgraph_function_possibly_inlined_p (olddecl)) (*debug_hooks->outlining_inline_function) (olddecl); /* The new defn must not be inline. */ DECL_INLINE (newdecl) = 0; DECL_UNINLINABLE (newdecl) = 1; } else { /* If either decl says `inline', this fn is inline, unless its definition was passed already. */ if (DECL_DECLARED_INLINE_P (newdecl) || DECL_DECLARED_INLINE_P (olddecl)) DECL_DECLARED_INLINE_P (newdecl) = 1; DECL_UNINLINABLE (newdecl) = DECL_UNINLINABLE (olddecl) = (DECL_UNINLINABLE (newdecl) || DECL_UNINLINABLE (olddecl)); } if (DECL_BUILT_IN (olddecl)) { /* If redeclaring a builtin function, it stays built in. */ DECL_BUILT_IN_CLASS (newdecl) = DECL_BUILT_IN_CLASS (olddecl); DECL_FUNCTION_CODE (newdecl) = DECL_FUNCTION_CODE (olddecl); } /* Also preserve various other info from the definition. */ if (! new_is_definition) { DECL_RESULT (newdecl) = DECL_RESULT (olddecl); DECL_INITIAL (newdecl) = DECL_INITIAL (olddecl); DECL_SAVED_INSNS (newdecl) = DECL_SAVED_INSNS (olddecl); DECL_SAVED_TREE (newdecl) = DECL_SAVED_TREE (olddecl); DECL_ARGUMENTS (newdecl) = DECL_ARGUMENTS (olddecl); /* Set DECL_INLINE on the declaration if we've got a body from which to instantiate. */ if (DECL_INLINE (olddecl) && ! DECL_UNINLINABLE (newdecl)) { DECL_INLINE (newdecl) = 1; DECL_ABSTRACT_ORIGIN (newdecl) = DECL_ABSTRACT_ORIGIN (olddecl); } } else { /* If a previous declaration said inline, mark the definition as inlinable. */ if (DECL_DECLARED_INLINE_P (newdecl) && ! DECL_UNINLINABLE (newdecl)) DECL_INLINE (newdecl) = 1; } } /* Copy most of the decl-specific fields of NEWDECL into OLDDECL. But preserve OLDDECL's DECL_UID and C_DECL_INVISIBLE. */ { unsigned olddecl_uid = DECL_UID (olddecl); unsigned olddecl_invisible = C_DECL_INVISIBLE (olddecl); memcpy ((char *) olddecl + sizeof (struct tree_common), (char *) newdecl + sizeof (struct tree_common), sizeof (struct tree_decl) - sizeof (struct tree_common)); DECL_UID (olddecl) = olddecl_uid; C_DECL_INVISIBLE (olddecl) = olddecl_invisible; } /* If OLDDECL had its DECL_RTL instantiated, re-invoke make_decl_rtl so that encode_section_info has a chance to look at the new decl flags and attributes. */ if (DECL_RTL_SET_P (olddecl) && (TREE_CODE (olddecl) == FUNCTION_DECL || (TREE_CODE (olddecl) == VAR_DECL && TREE_STATIC (olddecl)))) make_decl_rtl (olddecl, NULL); } /* Handle when a new declaration NEWDECL has the same name as an old one OLDDECL in the same binding contour. Prints an error message if appropriate. If safely possible, alter OLDDECL to look like NEWDECL, and return true. Otherwise, return false. */ static bool duplicate_decls (tree newdecl, tree olddecl) { tree newtype, oldtype; if (!diagnose_mismatched_decls (newdecl, olddecl, &newtype, &oldtype)) return false; merge_decls (newdecl, olddecl, newtype, oldtype); return true; } /* Return any external DECL associated with ID, whether or not it is currently in scope. */ static tree any_external_decl (tree id) { tree decl = IDENTIFIER_SYMBOL_VALUE (id); tree t; if (decl == 0 || TREE_CODE (decl) == ERROR_MARK) return 0; else if (TREE_CODE (decl) != TYPE_DECL && DECL_EXTERNAL (decl)) return decl; t = purpose_member (id, truly_local_externals); if (t) return TREE_VALUE (t); return 0; } /* Record an external decl DECL. This only does something if a shadowing decl already exists. */ static void record_external_decl (tree decl) { tree name = DECL_NAME (decl); if (!IDENTIFIER_SYMBOL_VALUE (name)) return; truly_local_externals = tree_cons (name, decl, truly_local_externals); } /* Check whether decl-node X shadows an existing declaration. OLD is the old IDENTIFIER_SYMBOL_VALUE of the DECL_NAME of X, which might be a NULL_TREE. */ static void warn_if_shadowing (tree x, tree old) { /* Nothing to shadow? */ if (old == 0 /* Shadow warnings not wanted? */ || !warn_shadow /* No shadow warnings for internally generated vars. */ || DECL_SOURCE_LINE (x) == 0 /* No shadow warnings for vars made for inlining. */ || DECL_FROM_INLINE (x) /* Don't warn about the parm names in function declarator within a function declarator. It would be nice to avoid warning in any function declarator in a declaration, as opposed to a definition, but there is no way to tell it's not a definition. */ || (TREE_CODE (x) == PARM_DECL && current_scope->outer->parm_flag) /* Shadow warnings only apply to local variables and parameters. */ || (TREE_CODE (x) != PARM_DECL && DECL_FILE_SCOPE_P (x))) return; if (TREE_CODE (old) == PARM_DECL) warning ("%Jdeclaration of '%D' shadows a parameter", x, x); else if (DECL_FILE_SCOPE_P (old)) warning ("%Jdeclaration of '%D' shadows a global declaration", x, x); else warning ("%Jdeclaration of '%D' shadows a previous local", x, x); warning ("%Jshadowed declaration is here", old); } /* Subroutine of pushdecl. X is a TYPE_DECL for a typedef statement. Create a brand new ..._TYPE node (which will be just a variant of the existing ..._TYPE node with identical properties) and then install X as the TYPE_NAME of this brand new (duplicate) ..._TYPE node. The whole point here is to end up with a situation where each and every ..._TYPE node the compiler creates will be uniquely associated with AT MOST one node representing a typedef name. This way, even though the compiler substitutes corresponding ..._TYPE nodes for TYPE_DECL (i.e. "typedef name") nodes very early on, later parts of the compiler can always do the reverse translation and get back the corresponding typedef name. For example, given: typedef struct S MY_TYPE; MY_TYPE object; Later parts of the compiler might only know that `object' was of type `struct S' if it were not for code just below. With this code however, later parts of the compiler see something like: struct S' == struct S typedef struct S' MY_TYPE; struct S' object; And they can then deduce (from the node for type struct S') that the original object declaration was: MY_TYPE object; Being able to do this is important for proper support of protoize, and also for generating precise symbolic debugging information which takes full account of the programmer's (typedef) vocabulary. Obviously, we don't want to generate a duplicate ..._TYPE node if the TYPE_DECL node that we are now processing really represents a standard built-in type. Since all standard types are effectively declared at line zero in the source file, we can easily check to see if we are working on a standard type by checking the current value of lineno. */ static void clone_underlying_type (tree x) { if (DECL_SOURCE_LINE (x) == 0) { if (TYPE_NAME (TREE_TYPE (x)) == 0) TYPE_NAME (TREE_TYPE (x)) = x; } else if (TREE_TYPE (x) != error_mark_node && DECL_ORIGINAL_TYPE (x) == NULL_TREE) { tree tt = TREE_TYPE (x); DECL_ORIGINAL_TYPE (x) = tt; tt = build_type_copy (tt); TYPE_NAME (tt) = x; TREE_USED (tt) = TREE_USED (x); TREE_TYPE (x) = tt; } } /* Record a decl-node X as belonging to the current lexical scope. Check for errors (such as an incompatible declaration for the same name already seen in the same scope). Returns either X or an old decl for the same name. If an old decl is returned, it may have been smashed to agree with what X says. */ tree pushdecl (tree x) { tree name = DECL_NAME (x); struct c_scope *scope = current_scope; #ifdef ENABLE_CHECKING if (error_mark_node == 0) /* Called too early. */ abort (); #endif /* Functions need the lang_decl data. */ if (TREE_CODE (x) == FUNCTION_DECL && ! DECL_LANG_SPECIFIC (x)) DECL_LANG_SPECIFIC (x) = ggc_alloc_cleared (sizeof (struct lang_decl)); /* A local extern declaration for a function doesn't constitute nesting. A local auto declaration does, since it's a forward decl for a nested function coming later. */ if (current_function_decl == NULL || ((TREE_CODE (x) == FUNCTION_DECL || TREE_CODE (x) == VAR_DECL) && DECL_INITIAL (x) == 0 && DECL_EXTERNAL (x))) DECL_CONTEXT (x) = current_file_decl; else DECL_CONTEXT (x) = current_function_decl; if (name) { tree old; if (warn_nested_externs && scope != global_scope && DECL_EXTERNAL (x) && !DECL_IN_SYSTEM_HEADER (x)) warning ("nested extern declaration of `%s'", IDENTIFIER_POINTER (name)); old = lookup_name_current_level (name); if (old && duplicate_decls (x, old)) { /* For PARM_DECLs, old may be a forward declaration. If so, we want to remove it from its old location (in the variables chain) and rechain it in the location given by the new declaration. */ if (TREE_CODE (x) == PARM_DECL) { tree *p; for (p = &scope->names; *p; p = &TREE_CHAIN (*p)) if (*p == old) { *p = TREE_CHAIN (old); SCOPE_LIST_APPEND (scope, parms, old); break; } } return old; } if (DECL_EXTERNAL (x) || scope == global_scope) { /* Find and check against a previous, not-in-scope, external decl for this identifier. (C99 6.2.7p2: All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined.) */ tree ext = any_external_decl (name); if (ext) { if (duplicate_decls (x, ext)) x = copy_node (ext); } else record_external_decl (x); } if (TREE_CODE (x) == TYPE_DECL) clone_underlying_type (x); /* If storing a local value, there may already be one (inherited). If so, record it for restoration when this scope ends. Take care not to do this if we are replacing an older decl in the same scope (i.e. duplicate_decls returned false, above). */ if (scope != global_scope) { tree inherited_decl = lookup_name (name); if (inherited_decl && inherited_decl != old) { warn_if_shadowing (x, inherited_decl); scope->shadowed = tree_cons (name, inherited_decl, scope->shadowed); } } /* Install the new declaration in the requested scope. */ IDENTIFIER_SYMBOL_VALUE (name) = x; C_DECL_INVISIBLE (x) = 0; /* If x's type is incomplete because it's based on a structure or union which has not yet been fully declared, attach it to that structure or union type, so we can go back and complete the variable declaration later, if the structure or union gets fully declared. If the input is erroneous, we can have error_mark in the type slot (e.g. "f(void a, ...)") - that doesn't count as an incomplete type. */ if (TREE_TYPE (x) != error_mark_node && !COMPLETE_TYPE_P (TREE_TYPE (x))) { tree element = TREE_TYPE (x); while (TREE_CODE (element) == ARRAY_TYPE) element = TREE_TYPE (element); element = TYPE_MAIN_VARIANT (element); if ((TREE_CODE (element) == RECORD_TYPE || TREE_CODE (element) == UNION_TYPE) && (TREE_CODE (x) != TYPE_DECL || TREE_CODE (TREE_TYPE (x)) == ARRAY_TYPE) && !COMPLETE_TYPE_P (element)) C_TYPE_INCOMPLETE_VARS (element) = tree_cons (NULL_TREE, x, C_TYPE_INCOMPLETE_VARS (element)); } } if (TREE_CODE (x) == PARM_DECL) SCOPE_LIST_APPEND (scope, parms, x); else SCOPE_LIST_APPEND (scope, names, x); return x; } /* Record X as belonging to the global scope (C99 "file scope"). This is used only internally by the Objective-C front end, and is limited to its needs. duplicate_decls is not called; if there is any preexisting decl for this identifier, it is an ICE. */ tree pushdecl_top_level (tree x) { tree name; if (TREE_CODE (x) != VAR_DECL) abort (); name = DECL_NAME (x); if (IDENTIFIER_SYMBOL_VALUE (name)) abort (); DECL_CONTEXT (x) = current_file_decl; IDENTIFIER_SYMBOL_VALUE (name) = x; SCOPE_LIST_APPEND (global_scope, names, x); return x; } /* Generate an implicit declaration for identifier FUNCTIONID as a function of type int (). */ tree implicitly_declare (tree functionid) { tree decl = any_external_decl (functionid); if (decl) { /* Implicit declaration of a function already declared (somehow) in a different scope, or as a built-in. If this is the first time this has happened, warn; then recycle the old declaration. */ if (!C_DECL_IMPLICIT (decl)) { implicit_decl_warning (DECL_NAME (decl)); if (! DECL_FILE_SCOPE_P (decl)) warning ("%Jprevious declaration of '%D'", decl, decl); C_DECL_IMPLICIT (decl) = 1; } /* If this function is global, then it must already be in the global scope, so there's no need to push it again. */ if (current_scope == global_scope) return decl; /* If this is a local declaration, make a copy; we can't have the same DECL listed in two different scopes. */ return pushdecl (copy_node (decl)); } /* Not seen before. */ decl = build_decl (FUNCTION_DECL, functionid, default_function_type); DECL_EXTERNAL (decl) = 1; TREE_PUBLIC (decl) = 1; C_DECL_IMPLICIT (decl) = 1; implicit_decl_warning (functionid); /* C89 says implicit declarations are in the innermost block. So we record the decl in the standard fashion. */ decl = pushdecl (decl); /* No need to call objc_check_decl here - it's a function type. */ rest_of_decl_compilation (decl, NULL, 0, 0); /* Write a record describing this implicit function declaration to the prototypes file (if requested). */ gen_aux_info_record (decl, 0, 1, 0); /* Possibly apply some default attributes to this implicit declaration. */ decl_attributes (&decl, NULL_TREE, 0); return decl; } static void implicit_decl_warning (tree id) { const char *name = IDENTIFIER_POINTER (id); if (mesg_implicit_function_declaration == 2) error ("implicit declaration of function `%s'", name); else if (mesg_implicit_function_declaration == 1) warning ("implicit declaration of function `%s'", name); } /* Issue an error message for a reference to an undeclared variable ID, including a reference to a builtin outside of function-call context. Establish a binding of the identifier to error_mark_node in an appropriate scope, which will suppress further errors for the same identifier. */ void undeclared_variable (tree id) { static bool already = false; struct c_scope *scope; if (current_function_decl == 0) { error ("`%s' undeclared here (not in a function)", IDENTIFIER_POINTER (id)); scope = current_scope; } else { error ("`%s' undeclared (first use in this function)", IDENTIFIER_POINTER (id)); if (! already) { error ("(Each undeclared identifier is reported only once"); error ("for each function it appears in.)"); already = true; } scope = current_function_scope; } scope->shadowed = tree_cons (id, IDENTIFIER_SYMBOL_VALUE (id), scope->shadowed); IDENTIFIER_SYMBOL_VALUE (id) = error_mark_node; } /* Subroutine of lookup_label, declare_label, define_label: construct a LABEL_DECL with all the proper frills. */ static tree make_label (tree name, location_t location) { tree label = build_decl (LABEL_DECL, name, void_type_node); DECL_CONTEXT (label) = current_function_decl; DECL_MODE (label) = VOIDmode; DECL_SOURCE_LOCATION (label) = location; return label; } /* Another subroutine of lookup_label, declare_label, define_label: set up the binding of name to LABEL_DECL in the given SCOPE. */ static void bind_label (tree name, tree label, struct c_scope *scope) { if (IDENTIFIER_LABEL_VALUE (name)) scope->shadowed = tree_cons (name, IDENTIFIER_LABEL_VALUE (name), scope->shadowed); IDENTIFIER_LABEL_VALUE (name) = label; SCOPE_LIST_APPEND (scope, names, label); } /* Get the LABEL_DECL corresponding to identifier NAME as a label. Create one if none exists so far for the current function. This is called when a label is used in a goto expression or has its address taken. */ tree lookup_label (tree name) { tree label; if (current_function_decl == 0) { error ("label %s referenced outside of any function", IDENTIFIER_POINTER (name)); return 0; } /* Use a label already defined or ref'd with this name, but not if it is inherited from a containing function and wasn't declared using __label__. */ label = IDENTIFIER_LABEL_VALUE (name); if (label && (DECL_CONTEXT (label) == current_function_decl || C_DECLARED_LABEL_FLAG (label))) { /* If the label has only been declared, update its apparent location to point here, for better diagnostics if it turns out not to have been defined. */ if (!TREE_USED (label)) DECL_SOURCE_LOCATION (label) = input_location; return label; } /* No label binding for that identifier; make one. */ label = make_label (name, input_location); /* Ordinary labels go in the current function scope. */ bind_label (name, label, current_function_scope); return label; } /* Make a label named NAME in the current function, shadowing silently any that may be inherited from containing functions or containing scopes. This is called for __label__ declarations. */ /* Note that valid use, if the label being shadowed comes from another scope in the same function, requires calling declare_nonlocal_label right away. (Is this still true? -zw 2003-07-17) */ tree declare_label (tree name) { tree label = IDENTIFIER_LABEL_VALUE (name); tree dup; /* Check to make sure that the label hasn't already been declared at this scope */ for (dup = current_scope->names; dup; dup = TREE_CHAIN (dup)) if (dup == label) { error ("duplicate label declaration `%s'", IDENTIFIER_POINTER (name)); error ("%Jthis is a previous declaration", dup); /* Just use the previous declaration. */ return dup; } label = make_label (name, input_location); C_DECLARED_LABEL_FLAG (label) = 1; /* Declared labels go in the current scope. */ bind_label (name, label, current_scope); return label; } /* Define a label, specifying the location in the source file. Return the LABEL_DECL node for the label, if the definition is valid. Otherwise return 0. */ tree define_label (location_t location, tree name) { tree label; /* Find any preexisting label with this name. It is an error if that label has already been defined in this function, or if there is a containing function with a declared label with the same name. */ label = IDENTIFIER_LABEL_VALUE (name); if (label && ((DECL_CONTEXT (label) == current_function_decl && DECL_INITIAL (label) != 0) || (DECL_CONTEXT (label) != current_function_decl && C_DECLARED_LABEL_FLAG (label)))) { error ("%Hduplicate label `%D'", &location, label); if (DECL_INITIAL (label)) error ("%J`%D' previously defined here", label, label); else error ("%J`%D' previously declared here", label, label); return 0; } else if (label && DECL_CONTEXT (label) == current_function_decl) { /* The label has been used or declared already in this function, but not defined. Update its location to point to this definition. */ DECL_SOURCE_LOCATION (label) = location; } else { /* No label binding for that identifier; make one. */ label = make_label (name, location); /* Ordinary labels go in the current function scope. */ bind_label (name, label, current_function_scope); } if (warn_traditional && !in_system_header && lookup_name (name)) warning ("%Htraditional C lacks a separate namespace for labels, " "identifier `%s' conflicts", &location, IDENTIFIER_POINTER (name)); /* Mark label as having been defined. */ DECL_INITIAL (label) = error_mark_node; return label; } /* Return the list of declarations of the current scope. */ tree getdecls (void) { return current_scope->names; } /* Given NAME, an IDENTIFIER_NODE, return the structure (or union or enum) definition for that name. If THISLEVEL_ONLY is nonzero, searches only the current_scope. CODE says which kind of type the caller wants; it is RECORD_TYPE or UNION_TYPE or ENUMERAL_TYPE. If the wrong kind of type is found, an error is reported. */ static tree lookup_tag (enum tree_code code, tree name, int thislevel_only) { tree tag = IDENTIFIER_TAG_VALUE (name); int thislevel = 0; if (!tag) return 0; /* We only care about whether it's in this level if thislevel_only was set or it might be a type clash. */ if (thislevel_only || TREE_CODE (tag) != code) { if (current_scope == global_scope || purpose_member (name, current_scope->tags)) thislevel = 1; } if (thislevel_only && !thislevel) return 0; if (TREE_CODE (tag) != code) { /* Definition isn't the kind we were looking for. */ pending_invalid_xref = name; pending_invalid_xref_location = input_location; /* If in the same binding level as a declaration as a tag of a different type, this must not be allowed to shadow that tag, so give the error immediately. (For example, "struct foo; union foo;" is invalid.) */ if (thislevel) pending_xref_error (); } return tag; } /* Print an error message now for a recent invalid struct, union or enum cross reference. We don't print them immediately because they are not invalid when used in the `struct foo;' construct for shadowing. */ void pending_xref_error (void) { if (pending_invalid_xref != 0) error ("%H`%s' defined as wrong kind of tag", &pending_invalid_xref_location, IDENTIFIER_POINTER (pending_invalid_xref)); pending_invalid_xref = 0; } /* Look up NAME in the current scope and its superiors in the namespace of variables, functions and typedefs. Return a ..._DECL node of some kind representing its definition, or return 0 if it is undefined. */ tree lookup_name (tree name) { tree decl = IDENTIFIER_SYMBOL_VALUE (name); if (decl == 0 || decl == error_mark_node) return decl; if (C_DECL_INVISIBLE (decl)) return 0; return decl; } /* Similar to `lookup_name' but look only at the current scope. */ static tree lookup_name_current_level (tree name) { tree decl = IDENTIFIER_SYMBOL_VALUE (name); if (decl == 0 || decl == error_mark_node || C_DECL_INVISIBLE (decl)) return 0; if (current_scope == global_scope) return decl; /* Scan the current scope for a decl with name NAME. For PARM_DECLs, we have to look at both ->parms and ->names, since forward parameter declarations wind up on the ->names list. */ if (TREE_CODE (decl) == PARM_DECL && chain_member (decl, current_scope->parms)) return decl; if (chain_member (decl, current_scope->names)) return decl; return 0; } /* Create the predefined scalar types of C, and some nodes representing standard constants (0, 1, (void *) 0). Initialize the global scope. Make definitions for built-in primitive functions. */ void c_init_decl_processing (void) { tree endlink; tree ptr_ftype_void, ptr_ftype_ptr; location_t save_loc = input_location; /* Adds some ggc roots, and reserved words for c-parse.in. */ c_parse_init (); current_function_decl = 0; /* Make the c_scope structure for global names. */ pushlevel (0); global_scope = current_scope; /* Declarations from c_common_nodes_and_builtins must not be associated with this input file, lest we get differences between using and not using preprocessed headers. */ input_location.file = ""; input_location.line = 0; /* Make the DECL for the toplevel file scope. */ current_file_decl = build_decl (TRANSLATION_UNIT_DECL, NULL, NULL); build_common_tree_nodes (flag_signed_char); c_common_nodes_and_builtins (); /* In C, comparisons and TRUTH_* expressions have type int. */ truthvalue_type_node = integer_type_node; truthvalue_true_node = integer_one_node; truthvalue_false_node = integer_zero_node; /* Even in C99, which has a real boolean type. */ pushdecl (build_decl (TYPE_DECL, get_identifier ("_Bool"), boolean_type_node)); endlink = void_list_node; ptr_ftype_void = build_function_type (ptr_type_node, endlink); ptr_ftype_ptr = build_function_type (ptr_type_node, tree_cons (NULL_TREE, ptr_type_node, endlink)); input_location = save_loc; pedantic_lvalues = pedantic; make_fname_decl = c_make_fname_decl; start_fname_decls (); first_builtin_decl = global_scope->names; last_builtin_decl = global_scope->names_last; } /* Create the VAR_DECL for __FUNCTION__ etc. ID is the name to give the decl, NAME is the initialization string and TYPE_DEP indicates whether NAME depended on the type of the function. As we don't yet implement delayed emission of static data, we mark the decl as emitted so it is not placed in the output. Anything using it must therefore pull out the STRING_CST initializer directly. FIXME. */ static tree c_make_fname_decl (tree id, int type_dep) { const char *name = fname_as_string (type_dep); tree decl, type, init; size_t length = strlen (name); type = build_array_type (build_qualified_type (char_type_node, TYPE_QUAL_CONST), build_index_type (size_int (length))); decl = build_decl (VAR_DECL, id, type); TREE_STATIC (decl) = 1; TREE_READONLY (decl) = 1; DECL_ARTIFICIAL (decl) = 1; init = build_string (length + 1, name); TREE_TYPE (init) = type; DECL_INITIAL (decl) = init; TREE_USED (decl) = 1; if (current_function_decl) { DECL_CONTEXT (decl) = current_function_decl; IDENTIFIER_SYMBOL_VALUE (id) = decl; SCOPE_LIST_APPEND (current_function_scope, names, decl); } finish_decl (decl, init, NULL_TREE); return decl; } /* Return a definition for a builtin function named NAME and whose data type is TYPE. TYPE should be a function type with argument types. FUNCTION_CODE tells later passes how to compile calls to this function. See tree.h for its possible values. If LIBRARY_NAME is nonzero, use that for DECL_ASSEMBLER_NAME, the name to be called if we can't opencode the function. If ATTRS is nonzero, use that for the function's attribute list. */ tree builtin_function (const char *name, tree type, int function_code, enum built_in_class class, const char *library_name, tree attrs) { tree decl = build_decl (FUNCTION_DECL, get_identifier (name), type); DECL_EXTERNAL (decl) = 1; TREE_PUBLIC (decl) = 1; if (library_name) SET_DECL_ASSEMBLER_NAME (decl, get_identifier (library_name)); make_decl_rtl (decl, NULL); pushdecl (decl); DECL_BUILT_IN_CLASS (decl) = class; DECL_FUNCTION_CODE (decl) = function_code; /* Warn if a function in the namespace for users is used without an occasion to consider it declared. */ if (name[0] != '_' || name[1] != '_') C_DECL_INVISIBLE (decl) = 1; /* Possibly apply some default attributes to this built-in function. */ if (attrs) decl_attributes (&decl, attrs, ATTR_FLAG_BUILT_IN); else decl_attributes (&decl, NULL_TREE, 0); return decl; } /* Called when a declaration is seen that contains no names to declare. If its type is a reference to a structure, union or enum inherited from a containing scope, shadow that tag name for the current scope with a forward reference. If its type defines a new named structure or union or defines an enum, it is valid but we need not do anything here. Otherwise, it is an error. */ void shadow_tag (tree declspecs) { shadow_tag_warned (declspecs, 0); } void shadow_tag_warned (tree declspecs, int warned) /* 1 => we have done a pedwarn. 2 => we have done a warning, but no pedwarn. */ { int found_tag = 0; tree link; tree specs, attrs; pending_invalid_xref = 0; /* Remove the attributes from declspecs, since they will confuse the following code. */ split_specs_attrs (declspecs, &specs, &attrs); for (link = specs; link; link = TREE_CHAIN (link)) { tree value = TREE_VALUE (link); enum tree_code code = TREE_CODE (value); if (code == RECORD_TYPE || code == UNION_TYPE || code == ENUMERAL_TYPE) /* Used to test also that TYPE_SIZE (value) != 0. That caused warning for `struct foo;' at top level in the file. */ { tree name = TYPE_NAME (value); tree t; found_tag++; if (name == 0) { if (warned != 1 && code != ENUMERAL_TYPE) /* Empty unnamed enum OK */ { pedwarn ("unnamed struct/union that defines no instances"); warned = 1; } } else { t = lookup_tag (code, name, 1); if (t == 0) { t = make_node (code); pushtag (name, t); } } } else { if (!warned && ! in_system_header) { warning ("useless keyword or type name in empty declaration"); warned = 2; } } } if (found_tag > 1) error ("two types specified in one empty declaration"); if (warned != 1) { if (found_tag == 0) pedwarn ("empty declaration"); } } /* Construct an array declarator. EXPR is the expression inside [], or NULL_TREE. QUALS are the type qualifiers inside the [] (to be applied to the pointer to which a parameter array is converted). STATIC_P is nonzero if "static" is inside the [], zero otherwise. VLA_UNSPEC_P is nonzero is the array is [*], a VLA of unspecified length which is nevertheless a complete type (not currently implemented by GCC), zero otherwise. The declarator is constructed as an ARRAY_REF (to be decoded by grokdeclarator), whose operand 0 is what's on the left of the [] (filled by in set_array_declarator_type) and operand 1 is the expression inside; whose TREE_TYPE is the type qualifiers and which has TREE_STATIC set if "static" is used. */ tree build_array_declarator (tree expr, tree quals, int static_p, int vla_unspec_p) { tree decl; decl = build_nt (ARRAY_REF, NULL_TREE, expr); TREE_TYPE (decl) = quals; TREE_STATIC (decl) = (static_p ? 1 : 0); if (pedantic && !flag_isoc99) { if (static_p || quals != NULL_TREE) pedwarn ("ISO C90 does not support `static' or type qualifiers in parameter array declarators"); if (vla_unspec_p) pedwarn ("ISO C90 does not support `[*]' array declarators"); } if (vla_unspec_p) warning ("GCC does not yet properly implement `[*]' array declarators"); return decl; } /* Set the type of an array declarator. DECL is the declarator, as constructed by build_array_declarator; TYPE is what appears on the left of the [] and goes in operand 0. ABSTRACT_P is nonzero if it is an abstract declarator, zero otherwise; this is used to reject static and type qualifiers in abstract declarators, where they are not in the C99 grammar. */ tree set_array_declarator_type (tree decl, tree type, int abstract_p) { TREE_OPERAND (decl, 0) = type; if (abstract_p && (TREE_TYPE (decl) != NULL_TREE || TREE_STATIC (decl))) error ("static or type qualifiers in abstract declarator"); return decl; } /* Decode a "typename", such as "int **", returning a ..._TYPE node. */ tree groktypename (tree typename) { tree specs, attrs; if (TREE_CODE (typename) != TREE_LIST) return typename; split_specs_attrs (TREE_PURPOSE (typename), &specs, &attrs); typename = grokdeclarator (TREE_VALUE (typename), specs, TYPENAME, 0, NULL); /* Apply attributes. */ decl_attributes (&typename, attrs, 0); return typename; } /* Return a PARM_DECL node for a given pair of specs and declarator. */ tree groktypename_in_parm_context (tree typename) { if (TREE_CODE (typename) != TREE_LIST) return typename; return grokdeclarator (TREE_VALUE (typename), TREE_PURPOSE (typename), PARM, 0, NULL); } /* Decode a declarator in an ordinary declaration or data definition. This is called as soon as the type information and variable name have been parsed, before parsing the initializer if any. Here we create the ..._DECL node, fill in its type, and put it on the list of decls for the current context. The ..._DECL node is returned as the value. Exception: for arrays where the length is not specified, the type is left null, to be filled in by `finish_decl'. Function definitions do not come here; they go to start_function instead. However, external and forward declarations of functions do go through here. Structure field declarations are done by grokfield and not through here. */ tree start_decl (tree declarator, tree declspecs, int initialized, tree attributes) { tree decl; tree tem; /* An object declared as __attribute__((deprecated)) suppresses warnings of uses of other deprecated items. */ if (lookup_attribute ("deprecated", attributes)) deprecated_state = DEPRECATED_SUPPRESS; decl = grokdeclarator (declarator, declspecs, NORMAL, initialized, NULL); deprecated_state = DEPRECATED_NORMAL; if (warn_main > 0 && TREE_CODE (decl) != FUNCTION_DECL && MAIN_NAME_P (DECL_NAME (decl))) warning ("%J'%D' is usually a function", decl, decl); if (initialized) /* Is it valid for this decl to have an initializer at all? If not, set INITIALIZED to zero, which will indirectly tell `finish_decl' to ignore the initializer once it is parsed. */ switch (TREE_CODE (decl)) { case TYPE_DECL: error ("typedef `%s' is initialized (use __typeof__ instead)", IDENTIFIER_POINTER (DECL_NAME (decl))); initialized = 0; break; case FUNCTION_DECL: error ("function `%s' is initialized like a variable", IDENTIFIER_POINTER (DECL_NAME (decl))); initialized = 0; break; case PARM_DECL: /* DECL_INITIAL in a PARM_DECL is really DECL_ARG_TYPE. */ error ("parameter `%s' is initialized", IDENTIFIER_POINTER (DECL_NAME (decl))); initialized = 0; break; default: /* Don't allow initializations for incomplete types except for arrays which might be completed by the initialization. */ /* This can happen if the array size is an undefined macro. We already gave a warning, so we don't need another one. */ if (TREE_TYPE (decl) == error_mark_node) initialized = 0; else if (COMPLETE_TYPE_P (TREE_TYPE (decl))) { /* A complete type is ok if size is fixed. */ if (TREE_CODE (TYPE_SIZE (TREE_TYPE (decl))) != INTEGER_CST || C_DECL_VARIABLE_SIZE (decl)) { error ("variable-sized object may not be initialized"); initialized = 0; } } else if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE) { error ("variable `%s' has initializer but incomplete type", IDENTIFIER_POINTER (DECL_NAME (decl))); initialized = 0; } else if (!COMPLETE_TYPE_P (TREE_TYPE (TREE_TYPE (decl)))) { error ("elements of array `%s' have incomplete type", IDENTIFIER_POINTER (DECL_NAME (decl))); initialized = 0; } } if (initialized) { DECL_EXTERNAL (decl) = 0; if (current_scope == global_scope) TREE_STATIC (decl) = 1; /* Tell `pushdecl' this is an initialized decl even though we don't yet have the initializer expression. Also tell `finish_decl' it may store the real initializer. */ DECL_INITIAL (decl) = error_mark_node; } /* If this is a function declaration, write a record describing it to the prototypes file (if requested). */ if (TREE_CODE (decl) == FUNCTION_DECL) gen_aux_info_record (decl, 0, 0, TYPE_ARG_TYPES (TREE_TYPE (decl)) != 0); /* ANSI specifies that a tentative definition which is not merged with a non-tentative definition behaves exactly like a definition with an initializer equal to zero. (Section 3.7.2) -fno-common gives strict ANSI behavior, though this tends to break a large body of code that grew up without this rule. Thread-local variables are never common, since there's no entrenched body of code to break, and it allows more efficient variable references in the presence of dynamic linking. */ if (TREE_CODE (decl) == VAR_DECL && !initialized && TREE_PUBLIC (decl) && !DECL_THREAD_LOCAL (decl) && !flag_no_common) DECL_COMMON (decl) = 1; /* Set attributes here so if duplicate decl, will have proper attributes. */ decl_attributes (&decl, attributes, 0); if (TREE_CODE (decl) == FUNCTION_DECL && targetm.calls.promote_prototypes (TREE_TYPE (decl))) { tree ce = declarator; if (TREE_CODE (ce) == INDIRECT_REF) ce = TREE_OPERAND (declarator, 0); if (TREE_CODE (ce) == CALL_EXPR) { tree args = TREE_PURPOSE (TREE_OPERAND (ce, 1)); for (; args; args = TREE_CHAIN (args)) { tree type = TREE_TYPE (args); if (INTEGRAL_TYPE_P (type) && TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node)) DECL_ARG_TYPE (args) = integer_type_node; } } } if (TREE_CODE (decl) == FUNCTION_DECL && DECL_DECLARED_INLINE_P (decl) && DECL_UNINLINABLE (decl) && lookup_attribute ("noinline", DECL_ATTRIBUTES (decl))) warning ("%Jinline function '%D' given attribute noinline", decl, decl); /* Add this decl to the current scope. TEM may equal DECL or it may be a previous decl of the same name. */ tem = pushdecl (decl); /* For a local variable, define the RTL now. */ if (current_scope != global_scope /* But not if this is a duplicate decl and we preserved the rtl from the previous one (which may or may not happen). */ && !DECL_RTL_SET_P (tem) && DECL_FILE_SCOPE_P (tem)) { if (TREE_TYPE (tem) != error_mark_node && (COMPLETE_TYPE_P (TREE_TYPE (tem)) || (TREE_CODE (TREE_TYPE (tem)) == ARRAY_TYPE && DECL_INITIAL (tem) != 0))) expand_decl (tem); } return tem; } /* Finish processing of a declaration; install its initial value. If the length of an array type is not known before, it must be determined now, from the initial value, or it is an error. */ void finish_decl (tree decl, tree init, tree asmspec_tree) { tree type = TREE_TYPE (decl); int was_incomplete = (DECL_SIZE (decl) == 0); const char *asmspec = 0; /* If a name was specified, get the string. */ if (current_scope == global_scope) asmspec_tree = maybe_apply_renaming_pragma (decl, asmspec_tree); if (asmspec_tree) asmspec = TREE_STRING_POINTER (asmspec_tree); /* If `start_decl' didn't like having an initialization, ignore it now. */ if (init != 0 && DECL_INITIAL (decl) == 0) init = 0; /* Don't crash if parm is initialized. */ if (TREE_CODE (decl) == PARM_DECL) init = 0; if (init) store_init_value (decl, init); if (c_dialect_objc () && (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == FUNCTION_DECL || TREE_CODE (decl) == FIELD_DECL)) objc_check_decl (decl); /* Deduce size of array from initialization, if not already known. */ if (TREE_CODE (type) == ARRAY_TYPE && TYPE_DOMAIN (type) == 0 && TREE_CODE (decl) != TYPE_DECL) { int do_default = (TREE_STATIC (decl) /* Even if pedantic, an external linkage array may have incomplete type at first. */ ? pedantic && !TREE_PUBLIC (decl) : !DECL_EXTERNAL (decl)); int failure = complete_array_type (type, DECL_INITIAL (decl), do_default); /* Get the completed type made by complete_array_type. */ type = TREE_TYPE (decl); if (failure == 1) error ("%Jinitializer fails to determine size of '%D'", decl, decl); else if (failure == 2) { if (do_default) error ("%Jarray size missing in '%D'", decl, decl); /* If a `static' var's size isn't known, make it extern as well as static, so it does not get allocated. If it is not `static', then do not mark extern; finish_incomplete_decl will give it a default size and it will get allocated. */ else if (!pedantic && TREE_STATIC (decl) && ! TREE_PUBLIC (decl)) DECL_EXTERNAL (decl) = 1; } /* TYPE_MAX_VALUE is always one less than the number of elements in the array, because we start counting at zero. Therefore, warn only if the value is less than zero. */ else if (pedantic && TYPE_DOMAIN (type) != 0 && tree_int_cst_sgn (TYPE_MAX_VALUE (TYPE_DOMAIN (type))) < 0) error ("%Jzero or negative size array '%D'", decl, decl); layout_decl (decl, 0); } if (TREE_CODE (decl) == VAR_DECL) { if (DECL_SIZE (decl) == 0 && TREE_TYPE (decl) != error_mark_node && COMPLETE_TYPE_P (TREE_TYPE (decl))) layout_decl (decl, 0); if (DECL_SIZE (decl) == 0 /* Don't give an error if we already gave one earlier. */ && TREE_TYPE (decl) != error_mark_node && (TREE_STATIC (decl) ? /* A static variable with an incomplete type is an error if it is initialized. Also if it is not file scope. Otherwise, let it through, but if it is not `extern' then it may cause an error message later. */ (DECL_INITIAL (decl) != 0 || !DECL_FILE_SCOPE_P (decl)) : /* An automatic variable with an incomplete type is an error. */ !DECL_EXTERNAL (decl))) { error ("%Jstorage size of '%D' isn't known", decl, decl); TREE_TYPE (decl) = error_mark_node; } if ((DECL_EXTERNAL (decl) || TREE_STATIC (decl)) && DECL_SIZE (decl) != 0) { if (TREE_CODE (DECL_SIZE (decl)) == INTEGER_CST) constant_expression_warning (DECL_SIZE (decl)); else error ("%Jstorage size of '%D' isn't constant", decl, decl); } if (TREE_USED (type)) TREE_USED (decl) = 1; } /* If this is a function and an assembler name is specified, reset DECL_RTL so we can give it its new name. Also, update built_in_decls if it was a normal built-in. */ if (TREE_CODE (decl) == FUNCTION_DECL && asmspec) { /* ASMSPEC is given, and not the name of a register. Mark the name with a star so assemble_name won't munge it. */ char *starred = alloca (strlen (asmspec) + 2); starred[0] = '*'; strcpy (starred + 1, asmspec); if (DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL) { tree builtin = built_in_decls [DECL_FUNCTION_CODE (decl)]; SET_DECL_RTL (builtin, NULL_RTX); SET_DECL_ASSEMBLER_NAME (builtin, get_identifier (starred)); #ifdef TARGET_MEM_FUNCTIONS if (DECL_FUNCTION_CODE (decl) == BUILT_IN_MEMCPY) init_block_move_fn (starred); else if (DECL_FUNCTION_CODE (decl) == BUILT_IN_MEMSET) init_block_clear_fn (starred); #else if (DECL_FUNCTION_CODE (decl) == BUILT_IN_BCOPY) init_block_move_fn (starred); else if (DECL_FUNCTION_CODE (decl) == BUILT_IN_BZERO) init_block_clear_fn (starred); #endif } SET_DECL_RTL (decl, NULL_RTX); change_decl_assembler_name (decl, get_identifier (starred)); } /* If #pragma weak was used, mark the decl weak now. */ if (current_scope == global_scope) maybe_apply_pragma_weak (decl); /* Output the assembler code and/or RTL code for variables and functions, unless the type is an undefined structure or union. If not, it will get done when the type is completed. */ if (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == FUNCTION_DECL) { /* This is a no-op in c-lang.c or something real in objc-act.c. */ if (c_dialect_objc ()) objc_check_decl (decl); if (DECL_FILE_SCOPE_P (decl)) { if (DECL_INITIAL (decl) == NULL_TREE || DECL_INITIAL (decl) == error_mark_node) /* Don't output anything when a tentative file-scope definition is seen. But at end of compilation, do output code for them. */ DECL_DEFER_OUTPUT (decl) = 1; rest_of_decl_compilation (decl, asmspec, true, 0); } else { /* This is a local variable. If there is an ASMSPEC, the user has requested that we handle it specially. */ if (asmspec) { /* In conjunction with an ASMSPEC, the `register' keyword indicates that we should place the variable in a particular register. */ if (DECL_REGISTER (decl)) DECL_C_HARD_REGISTER (decl) = 1; /* If this is not a static variable, issue a warning. It doesn't make any sense to give an ASMSPEC for an ordinary, non-register local variable. Historically, GCC has accepted -- but ignored -- the ASMSPEC in this case. */ if (TREE_CODE (decl) == VAR_DECL && !DECL_REGISTER (decl) && !TREE_STATIC (decl)) warning ("%Jignoring asm-specifier for non-static local " "variable '%D'", decl, decl); else change_decl_assembler_name (decl, get_identifier (asmspec)); } if (TREE_CODE (decl) != FUNCTION_DECL) add_decl_stmt (decl); } if (!DECL_FILE_SCOPE_P (decl)) { /* Recompute the RTL of a local array now if it used to be an incomplete type. */ if (was_incomplete && ! TREE_STATIC (decl) && ! DECL_EXTERNAL (decl)) { /* If we used it already as memory, it must stay in memory. */ TREE_ADDRESSABLE (decl) = TREE_USED (decl); /* If it's still incomplete now, no init will save it. */ if (DECL_SIZE (decl) == 0) DECL_INITIAL (decl) = 0; } } } /* If this was marked 'used', be sure it will be output. */ if (lookup_attribute ("used", DECL_ATTRIBUTES (decl))) mark_referenced (DECL_ASSEMBLER_NAME (decl)); if (TREE_CODE (decl) == TYPE_DECL) rest_of_decl_compilation (decl, NULL, DECL_FILE_SCOPE_P (decl), 0); /* At the end of a declaration, throw away any variable type sizes of types defined inside that declaration. There is no use computing them in the following function definition. */ if (current_scope == global_scope) get_pending_sizes (); /* Install a cleanup (aka destructor) if one was given. */ if (TREE_CODE (decl) == VAR_DECL && !TREE_STATIC (decl)) { tree attr = lookup_attribute ("cleanup", DECL_ATTRIBUTES (decl)); if (attr) { static bool eh_initialized_p; tree cleanup_id = TREE_VALUE (TREE_VALUE (attr)); tree cleanup_decl = lookup_name (cleanup_id); tree cleanup; /* Build "cleanup(&decl)" for the destructor. */ cleanup = build_unary_op (ADDR_EXPR, decl, 0); cleanup = build_tree_list (NULL_TREE, cleanup); cleanup = build_function_call (cleanup_decl, cleanup); /* Don't warn about decl unused; the cleanup uses it. */ TREE_USED (decl) = 1; /* Initialize EH, if we've been told to do so. */ if (flag_exceptions && !eh_initialized_p) { eh_initialized_p = true; eh_personality_libfunc = init_one_libfunc (USING_SJLJ_EXCEPTIONS ? "__gcc_personality_sj0" : "__gcc_personality_v0"); using_eh_for_cleanups (); } add_stmt (build_stmt (CLEANUP_STMT, decl, cleanup)); } } } /* Given a parsed parameter declaration, decode it into a PARM_DECL and push that on the current scope. */ void push_parm_decl (tree parm) { tree decl; + int old_dont_save_pending_sizes_p = 0; /* Don't attempt to expand sizes while parsing this decl. (We can get here with i_s_e 1 somehow from Objective-C.) */ int save_immediate_size_expand = immediate_size_expand; immediate_size_expand = 0; + /* If this is a nested function, we do want to keep SAVE_EXPRs for + the argument sizes, regardless of the parent's setting. */ + if (cfun) + { + old_dont_save_pending_sizes_p = cfun->x_dont_save_pending_sizes_p; + cfun->x_dont_save_pending_sizes_p = 0; + } + decl = grokdeclarator (TREE_VALUE (TREE_PURPOSE (parm)), TREE_PURPOSE (TREE_PURPOSE (parm)), PARM, 0, NULL); decl_attributes (&decl, TREE_VALUE (parm), 0); decl = pushdecl (decl); finish_decl (decl, NULL_TREE, NULL_TREE); + if (cfun) + cfun->x_dont_save_pending_sizes_p = old_dont_save_pending_sizes_p; immediate_size_expand = save_immediate_size_expand; } /* Mark all the parameter declarations to date as forward decls, shift them to the variables list, and reset the parameters list. Also diagnose use of this extension. */ void mark_forward_parm_decls (void) { tree parm; if (pedantic && !current_scope->warned_forward_parm_decls) { pedwarn ("ISO C forbids forward parameter declarations"); current_scope->warned_forward_parm_decls = true; } for (parm = current_scope->parms; parm; parm = TREE_CHAIN (parm)) TREE_ASM_WRITTEN (parm) = 1; SCOPE_LIST_CONCAT (current_scope, names, current_scope, parms); current_scope->parms = 0; current_scope->parms_last = 0; } static GTY(()) int compound_literal_number; /* Build a COMPOUND_LITERAL_EXPR. TYPE is the type given in the compound literal, which may be an incomplete array type completed by the initializer; INIT is a CONSTRUCTOR that initializes the compound literal. */ tree build_compound_literal (tree type, tree init) { /* We do not use start_decl here because we have a type, not a declarator; and do not use finish_decl because the decl should be stored inside the COMPOUND_LITERAL_EXPR rather than added elsewhere as a DECL_STMT. */ tree decl = build_decl (VAR_DECL, NULL_TREE, type); tree complit; tree stmt; DECL_EXTERNAL (decl) = 0; TREE_PUBLIC (decl) = 0; TREE_STATIC (decl) = (current_scope == global_scope); DECL_CONTEXT (decl) = current_function_decl; TREE_USED (decl) = 1; TREE_TYPE (decl) = type; TREE_READONLY (decl) = TREE_READONLY (type); store_init_value (decl, init); if (TREE_CODE (type) == ARRAY_TYPE && !COMPLETE_TYPE_P (type)) { int failure = complete_array_type (type, DECL_INITIAL (decl), 1); if (failure) abort (); } type = TREE_TYPE (decl); if (type == error_mark_node || !COMPLETE_TYPE_P (type)) return error_mark_node; stmt = build_stmt (DECL_STMT, decl); complit = build1 (COMPOUND_LITERAL_EXPR, TREE_TYPE (decl), stmt); TREE_SIDE_EFFECTS (complit) = 1; layout_decl (decl, 0); if (TREE_STATIC (decl)) { /* This decl needs a name for the assembler output. We also need a unique suffix to be added to the name. */ char *name; ASM_FORMAT_PRIVATE_NAME (name, "__compound_literal", compound_literal_number); compound_literal_number++; DECL_NAME (decl) = get_identifier (name); DECL_DEFER_OUTPUT (decl) = 1; DECL_COMDAT (decl) = 1; DECL_ARTIFICIAL (decl) = 1; pushdecl (decl); rest_of_decl_compilation (decl, NULL, 1, 0); } return complit; } /* Make TYPE a complete type based on INITIAL_VALUE. Return 0 if successful, 1 if INITIAL_VALUE can't be deciphered, 2 if there was no information (in which case assume 1 if DO_DEFAULT). */ int complete_array_type (tree type, tree initial_value, int do_default) { tree maxindex = NULL_TREE; int value = 0; if (initial_value) { /* Note MAXINDEX is really the maximum index, one less than the size. */ if (TREE_CODE (initial_value) == STRING_CST) { int eltsize = int_size_in_bytes (TREE_TYPE (TREE_TYPE (initial_value))); maxindex = build_int_2 ((TREE_STRING_LENGTH (initial_value) / eltsize) - 1, 0); } else if (TREE_CODE (initial_value) == CONSTRUCTOR) { tree elts = CONSTRUCTOR_ELTS (initial_value); maxindex = build_int_2 (-1, -1); for (; elts; elts = TREE_CHAIN (elts)) { if (TREE_PURPOSE (elts)) maxindex = TREE_PURPOSE (elts); else maxindex = fold (build (PLUS_EXPR, integer_type_node, maxindex, integer_one_node)); } maxindex = copy_node (maxindex); } else { /* Make an error message unless that happened already. */ if (initial_value != error_mark_node) value = 1; /* Prevent further error messages. */ maxindex = build_int_2 (0, 0); } } if (!maxindex) { if (do_default) maxindex = build_int_2 (0, 0); value = 2; } if (maxindex) { TYPE_DOMAIN (type) = build_index_type (maxindex); if (!TREE_TYPE (maxindex)) TREE_TYPE (maxindex) = TYPE_DOMAIN (type); } /* Lay out the type now that we can get the real answer. */ layout_type (type); return value; } /* Determine whether TYPE is a structure with a flexible array member, or a union containing such a structure (possibly recursively). */ static bool flexible_array_type_p (tree type) { tree x; switch (TREE_CODE (type)) { case RECORD_TYPE: x = TYPE_FIELDS (type); if (x == NULL_TREE) return false; while (TREE_CHAIN (x) != NULL_TREE) x = TREE_CHAIN (x); if (TREE_CODE (TREE_TYPE (x)) == ARRAY_TYPE && TYPE_SIZE (TREE_TYPE (x)) == NULL_TREE && TYPE_DOMAIN (TREE_TYPE (x)) != NULL_TREE && TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (x))) == NULL_TREE) return true; return false; case UNION_TYPE: for (x = TYPE_FIELDS (type); x != NULL_TREE; x = TREE_CHAIN (x)) { if (flexible_array_type_p (TREE_TYPE (x))) return true; } return false; default: return false; } } /* Performs sanity checks on the TYPE and WIDTH of the bit-field NAME, replacing with appropriate values if they are invalid. */ static void check_bitfield_type_and_width (tree *type, tree *width, const char *orig_name) { tree type_mv; unsigned int max_width; unsigned HOST_WIDE_INT w; const char *name = orig_name ? orig_name: _(""); /* Necessary? */ STRIP_NOPS (*width); /* Detect and ignore out of range field width and process valid field widths. */ if (TREE_CODE (*width) != INTEGER_CST) { error ("bit-field `%s' width not an integer constant", name); *width = integer_one_node; } else { constant_expression_warning (*width); if (tree_int_cst_sgn (*width) < 0) { error ("negative width in bit-field `%s'", name); *width = integer_one_node; } else if (integer_zerop (*width) && orig_name) { error ("zero width for bit-field `%s'", name); *width = integer_one_node; } } /* Detect invalid bit-field type. */ if (TREE_CODE (*type) != INTEGER_TYPE && TREE_CODE (*type) != BOOLEAN_TYPE && TREE_CODE (*type) != ENUMERAL_TYPE) { error ("bit-field `%s' has invalid type", name); *type = unsigned_type_node; } type_mv = TYPE_MAIN_VARIANT (*type); if (pedantic && type_mv != integer_type_node && type_mv != unsigned_type_node && type_mv != boolean_type_node) pedwarn ("type of bit-field `%s' is a GCC extension", name); if (type_mv == boolean_type_node) max_width = CHAR_TYPE_SIZE; else max_width = TYPE_PRECISION (*type); if (0 < compare_tree_int (*width, max_width)) { error ("width of `%s' exceeds its type", name); w = max_width; *width = build_int_2 (w, 0); } else w = tree_low_cst (*width, 1); if (TREE_CODE (*type) == ENUMERAL_TYPE && (w < min_precision (TYPE_MIN_VALUE (*type), TREE_UNSIGNED (*type)) || w < min_precision (TYPE_MAX_VALUE (*type), TREE_UNSIGNED (*type)))) warning ("`%s' is narrower than values of its type", name); } /* Given declspecs and a declarator, determine the name and type of the object declared and construct a ..._DECL node for it. (In one case we can return a ..._TYPE node instead. For invalid input we sometimes return 0.) DECLSPECS is a chain of tree_list nodes whose value fields are the storage classes and type specifiers. DECL_CONTEXT says which syntactic context this declaration is in: NORMAL for most contexts. Make a VAR_DECL or FUNCTION_DECL or TYPE_DECL. FUNCDEF for a function definition. Like NORMAL but a few different error messages in each case. Return value may be zero meaning this definition is too screwy to try to parse. PARM for a parameter declaration (either within a function prototype or before a function body). Make a PARM_DECL, or return void_type_node. TYPENAME if for a typename (in a cast or sizeof). Don't make a DECL node; just return the ..._TYPE node. FIELD for a struct or union field; make a FIELD_DECL. INITIALIZED is 1 if the decl has an initializer. WIDTH is non-NULL for bit-fields, and is a pointer to an INTEGER_CST node representing the width of the bit-field. In the TYPENAME case, DECLARATOR is really an absolute declarator. It may also be so in the PARM case, for a prototype where the argument type is specified but not the name. This function is where the complicated C meanings of `static' and `extern' are interpreted. */ static tree grokdeclarator (tree declarator, tree declspecs, enum decl_context decl_context, int initialized, tree *width) { int specbits = 0; tree spec; tree type = NULL_TREE; int longlong = 0; int constp; int restrictp; int volatilep; int type_quals = TYPE_UNQUALIFIED; int inlinep; int explicit_int = 0; int explicit_char = 0; int defaulted_int = 0; tree typedef_decl = 0; const char *name, *orig_name; tree typedef_type = 0; int funcdef_flag = 0; enum tree_code innermost_code = ERROR_MARK; int size_varies = 0; tree decl_attr = NULL_TREE; tree array_ptr_quals = NULL_TREE; int array_parm_static = 0; tree returned_attrs = NULL_TREE; bool bitfield = width != NULL; tree element_type; if (decl_context == FUNCDEF) funcdef_flag = 1, decl_context = NORMAL; /* Look inside a declarator for the name being declared and get it as a string, for an error message. */ { tree decl = declarator; name = 0; while (decl) switch (TREE_CODE (decl)) { case ARRAY_REF: case INDIRECT_REF: case CALL_EXPR: innermost_code = TREE_CODE (decl); decl = TREE_OPERAND (decl, 0); break; case TREE_LIST: decl = TREE_VALUE (decl); break; case IDENTIFIER_NODE: name = IDENTIFIER_POINTER (decl); decl = 0; break; default: abort (); } orig_name = name; if (name == 0) name = "type name"; } /* A function definition's declarator must have the form of a function declarator. */ if (funcdef_flag && innermost_code != CALL_EXPR) return 0; /* If this looks like a function definition, make it one, even if it occurs where parms are expected. Then store_parm_decls will reject it and not use it as a parm. */ if (decl_context == NORMAL && !funcdef_flag && current_scope->parm_flag) decl_context = PARM; /* Look through the decl specs and record which ones appear. Some typespecs are defined as built-in typenames. Others, the ones that are modifiers of other types, are represented by bits in SPECBITS: set the bits for the modifiers that appear. Storage class keywords are also in SPECBITS. If there is a typedef name or a type, store the type in TYPE. This includes builtin typedefs such as `int'. Set EXPLICIT_INT or EXPLICIT_CHAR if the type is `int' or `char' and did not come from a user typedef. Set LONGLONG if `long' is mentioned twice. */ for (spec = declspecs; spec; spec = TREE_CHAIN (spec)) { tree id = TREE_VALUE (spec); /* If the entire declaration is itself tagged as deprecated then suppress reports of deprecated items. */ if (id && TREE_DEPRECATED (id)) { if (deprecated_state != DEPRECATED_SUPPRESS) warn_deprecated_use (id); } if (id == ridpointers[(int) RID_INT]) explicit_int = 1; if (id == ridpointers[(int) RID_CHAR]) explicit_char = 1; if (TREE_CODE (id) == IDENTIFIER_NODE && C_IS_RESERVED_WORD (id)) { enum rid i = C_RID_CODE (id); if ((int) i <= (int) RID_LAST_MODIFIER) { if (i == RID_LONG && (specbits & (1 << (int) RID_LONG))) { if (longlong) error ("`long long long' is too long for GCC"); else { if (pedantic && !flag_isoc99 && ! in_system_header && warn_long_long) pedwarn ("ISO C90 does not support `long long'"); longlong = 1; } } else if (specbits & (1 << (int) i)) { if (i == RID_CONST || i == RID_VOLATILE || i == RID_RESTRICT) { if (pedantic && !flag_isoc99) pedwarn ("duplicate `%s'", IDENTIFIER_POINTER (id)); } else error ("duplicate `%s'", IDENTIFIER_POINTER (id)); } /* Diagnose "__thread extern". Recall that this list is in the reverse order seen in the text. */ if (i == RID_THREAD && (specbits & (1 << (int) RID_EXTERN | 1 << (int) RID_STATIC))) { if (specbits & 1 << (int) RID_EXTERN) error ("`__thread' before `extern'"); else error ("`__thread' before `static'"); } specbits |= 1 << (int) i; goto found; } } if (type) error ("two or more data types in declaration of `%s'", name); /* Actual typedefs come to us as TYPE_DECL nodes. */ else if (TREE_CODE (id) == TYPE_DECL) { if (TREE_TYPE (id) == error_mark_node) ; /* Allow the type to default to int to avoid cascading errors. */ else { type = TREE_TYPE (id); decl_attr = DECL_ATTRIBUTES (id); typedef_decl = id; } } /* Built-in types come as identifiers. */ else if (TREE_CODE (id) == IDENTIFIER_NODE) { tree t = lookup_name (id); if (TREE_TYPE (t) == error_mark_node) ; else if (!t || TREE_CODE (t) != TYPE_DECL) error ("`%s' fails to be a typedef or built in type", IDENTIFIER_POINTER (id)); else { type = TREE_TYPE (t); typedef_decl = t; } } else if (TREE_CODE (id) != ERROR_MARK) type = id; found: ; } typedef_type = type; if (type) size_varies = C_TYPE_VARIABLE_SIZE (type); /* No type at all: default to `int', and set DEFAULTED_INT because it was not a user-defined typedef. */ if (type == 0) { if ((! (specbits & ((1 << (int) RID_LONG) | (1 << (int) RID_SHORT) | (1 << (int) RID_SIGNED) | (1 << (int) RID_UNSIGNED) | (1 << (int) RID_COMPLEX)))) /* Don't warn about typedef foo = bar. */ && ! (specbits & (1 << (int) RID_TYPEDEF) && initialized) && ! in_system_header) { /* Issue a warning if this is an ISO C 99 program or if -Wreturn-type and this is a function, or if -Wimplicit; prefer the former warning since it is more explicit. */ if ((warn_implicit_int || warn_return_type || flag_isoc99) && funcdef_flag) warn_about_return_type = 1; else if (warn_implicit_int || flag_isoc99) pedwarn_c99 ("type defaults to `int' in declaration of `%s'", name); } defaulted_int = 1; type = integer_type_node; } /* Now process the modifiers that were specified and check for invalid combinations. */ /* Long double is a special combination. */ if ((specbits & 1 << (int) RID_LONG) && ! longlong && TYPE_MAIN_VARIANT (type) == double_type_node) { specbits &= ~(1 << (int) RID_LONG); type = long_double_type_node; } /* Check all other uses of type modifiers. */ if (specbits & ((1 << (int) RID_LONG) | (1 << (int) RID_SHORT) | (1 << (int) RID_UNSIGNED) | (1 << (int) RID_SIGNED))) { int ok = 0; if ((specbits & 1 << (int) RID_LONG) && (specbits & 1 << (int) RID_SHORT)) error ("both long and short specified for `%s'", name); else if (((specbits & 1 << (int) RID_LONG) || (specbits & 1 << (int) RID_SHORT)) && explicit_char) error ("long or short specified with char for `%s'", name); else if (((specbits & 1 << (int) RID_LONG) || (specbits & 1 << (int) RID_SHORT)) && TREE_CODE (type) == REAL_TYPE) { static int already = 0; error ("long or short specified with floating type for `%s'", name); if (! already && ! pedantic) { error ("the only valid combination is `long double'"); already = 1; } } else if ((specbits & 1 << (int) RID_SIGNED) && (specbits & 1 << (int) RID_UNSIGNED)) error ("both signed and unsigned specified for `%s'", name); else if (TREE_CODE (type) != INTEGER_TYPE) error ("long, short, signed or unsigned invalid for `%s'", name); else { ok = 1; if (!explicit_int && !defaulted_int && !explicit_char) { error ("long, short, signed or unsigned used invalidly for `%s'", name); ok = 0; } } /* Discard the type modifiers if they are invalid. */ if (! ok) { specbits &= ~((1 << (int) RID_LONG) | (1 << (int) RID_SHORT) | (1 << (int) RID_UNSIGNED) | (1 << (int) RID_SIGNED)); longlong = 0; } } if ((specbits & (1 << (int) RID_COMPLEX)) && TREE_CODE (type) != INTEGER_TYPE && TREE_CODE (type) != REAL_TYPE) { error ("complex invalid for `%s'", name); specbits &= ~(1 << (int) RID_COMPLEX); } /* Decide whether an integer type is signed or not. Optionally treat bit-fields as signed by default. */ if (specbits & 1 << (int) RID_UNSIGNED || (bitfield && ! flag_signed_bitfields && (explicit_int || defaulted_int || explicit_char /* A typedef for plain `int' without `signed' can be controlled just like plain `int'. */ || ! (typedef_decl != 0 && C_TYPEDEF_EXPLICITLY_SIGNED (typedef_decl))) && TREE_CODE (type) != ENUMERAL_TYPE && !(specbits & 1 << (int) RID_SIGNED))) { if (longlong) type = long_long_unsigned_type_node; else if (specbits & 1 << (int) RID_LONG) type = long_unsigned_type_node; else if (specbits & 1 << (int) RID_SHORT) type = short_unsigned_type_node; else if (type == char_type_node) type = unsigned_char_type_node; else if (typedef_decl) type = c_common_unsigned_type (type); else type = unsigned_type_node; } else if ((specbits & 1 << (int) RID_SIGNED) && type == char_type_node) type = signed_char_type_node; else if (longlong) type = long_long_integer_type_node; else if (specbits & 1 << (int) RID_LONG) type = long_integer_type_node; else if (specbits & 1 << (int) RID_SHORT) type = short_integer_type_node; if (specbits & 1 << (int) RID_COMPLEX) { if (pedantic && !flag_isoc99) pedwarn ("ISO C90 does not support complex types"); /* If we just have "complex", it is equivalent to "complex double", but if any modifiers at all are specified it is the complex form of TYPE. E.g, "complex short" is "complex short int". */ if (defaulted_int && ! longlong && ! (specbits & ((1 << (int) RID_LONG) | (1 << (int) RID_SHORT) | (1 << (int) RID_SIGNED) | (1 << (int) RID_UNSIGNED)))) { if (pedantic) pedwarn ("ISO C does not support plain `complex' meaning `double complex'"); type = complex_double_type_node; } else if (type == integer_type_node) { if (pedantic) pedwarn ("ISO C does not support complex integer types"); type = complex_integer_type_node; } else if (type == float_type_node) type = complex_float_type_node; else if (type == double_type_node) type = complex_double_type_node; else if (type == long_double_type_node) type = complex_long_double_type_node; else { if (pedantic) pedwarn ("ISO C does not support complex integer types"); type = build_complex_type (type); } } /* Figure out the type qualifiers for the declaration. There are two ways a declaration can become qualified. One is something like `const int i' where the `const' is explicit. Another is something like `typedef const int CI; CI i' where the type of the declaration contains the `const'. A third possibility is that there is a type qualifier on the element type of a typedefed array type, in which case we should extract that qualifier so that c_apply_type_quals_to_decls receives the full list of qualifiers to work with (C90 is not entirely clear about whether duplicate qualifiers should be diagnosed in this case, but it seems most appropriate to do so). */ element_type = strip_array_types (type); constp = !! (specbits & 1 << (int) RID_CONST) + TYPE_READONLY (element_type); restrictp = !! (specbits & 1 << (int) RID_RESTRICT) + TYPE_RESTRICT (element_type); volatilep = !! (specbits & 1 << (int) RID_VOLATILE) + TYPE_VOLATILE (element_type); inlinep = !! (specbits & (1 << (int) RID_INLINE)); if (pedantic && !flag_isoc99) { if (constp > 1) pedwarn ("duplicate `const'"); if (restrictp > 1) pedwarn ("duplicate `restrict'"); if (volatilep > 1) pedwarn ("duplicate `volatile'"); } if (! flag_gen_aux_info && (TYPE_QUALS (type))) type = TYPE_MAIN_VARIANT (type); type_quals = ((constp ? TYPE_QUAL_CONST : 0) | (restrictp ? TYPE_QUAL_RESTRICT : 0) | (volatilep ? TYPE_QUAL_VOLATILE : 0)); /* Warn if two storage classes are given. Default to `auto'. */ { int nclasses = 0; if (specbits & 1 << (int) RID_AUTO) nclasses++; if (specbits & 1 << (int) RID_STATIC) nclasses++; if (specbits & 1 << (int) RID_EXTERN) nclasses++; if (specbits & 1 << (int) RID_REGISTER) nclasses++; if (specbits & 1 << (int) RID_TYPEDEF) nclasses++; /* "static __thread" and "extern __thread" are allowed. */ if ((specbits & (1 << (int) RID_THREAD | 1 << (int) RID_STATIC | 1 << (int) RID_EXTERN)) == (1 << (int) RID_THREAD)) nclasses++; /* Warn about storage classes that are invalid for certain kinds of declarations (parameters, typenames, etc.). */ if (nclasses > 1) error ("multiple storage classes in declaration of `%s'", name); else if (funcdef_flag && (specbits & ((1 << (int) RID_REGISTER) | (1 << (int) RID_AUTO) | (1 << (int) RID_TYPEDEF) | (1 << (int) RID_THREAD)))) { if (specbits & 1 << (int) RID_AUTO && (pedantic || current_scope == global_scope)) pedwarn ("function definition declared `auto'"); if (specbits & 1 << (int) RID_REGISTER) error ("function definition declared `register'"); if (specbits & 1 << (int) RID_TYPEDEF) error ("function definition declared `typedef'"); if (specbits & 1 << (int) RID_THREAD) error ("function definition declared `__thread'"); specbits &= ~((1 << (int) RID_TYPEDEF) | (1 << (int) RID_REGISTER) | (1 << (int) RID_AUTO) | (1 << (int) RID_THREAD)); } else if (decl_context != NORMAL && nclasses > 0) { if (decl_context == PARM && specbits & 1 << (int) RID_REGISTER) ; else { switch (decl_context) { case FIELD: error ("storage class specified for structure field `%s'", name); break; case PARM: error ("storage class specified for parameter `%s'", name); break; default: error ("storage class specified for typename"); break; } specbits &= ~((1 << (int) RID_TYPEDEF) | (1 << (int) RID_REGISTER) | (1 << (int) RID_AUTO) | (1 << (int) RID_STATIC) | (1 << (int) RID_EXTERN) | (1 << (int) RID_THREAD)); } } else if (specbits & 1 << (int) RID_EXTERN && initialized && ! funcdef_flag) { /* `extern' with initialization is invalid if not at file scope. */ if (current_scope == global_scope) warning ("`%s' initialized and declared `extern'", name); else error ("`%s' has both `extern' and initializer", name); } else if (current_scope == global_scope) { if (specbits & 1 << (int) RID_AUTO) error ("file-scope declaration of `%s' specifies `auto'", name); } else { if (specbits & 1 << (int) RID_EXTERN && funcdef_flag) error ("nested function `%s' declared `extern'", name); else if ((specbits & (1 << (int) RID_THREAD | 1 << (int) RID_EXTERN | 1 << (int) RID_STATIC)) == (1 << (int) RID_THREAD)) { error ("function-scope `%s' implicitly auto and declared `__thread'", name); specbits &= ~(1 << (int) RID_THREAD); } } } /* Now figure out the structure of the declarator proper. Descend through it, creating more complex types, until we reach the declared identifier (or NULL_TREE, in an absolute declarator). */ while (declarator && TREE_CODE (declarator) != IDENTIFIER_NODE) { if (type == error_mark_node) { declarator = TREE_OPERAND (declarator, 0); continue; } /* Each level of DECLARATOR is either an ARRAY_REF (for ...[..]), an INDIRECT_REF (for *...), a CALL_EXPR (for ...(...)), a TREE_LIST (for nested attributes), an identifier (for the name being declared) or a null pointer (for the place in an absolute declarator where the name was omitted). For the last two cases, we have just exited the loop. At this point, TYPE is the type of elements of an array, or for a function to return, or for a pointer to point to. After this sequence of ifs, TYPE is the type of the array or function or pointer, and DECLARATOR has had its outermost layer removed. */ if (array_ptr_quals != NULL_TREE || array_parm_static) { /* Only the innermost declarator (making a parameter be of array type which is converted to pointer type) may have static or type qualifiers. */ error ("static or type qualifiers in non-parameter array declarator"); array_ptr_quals = NULL_TREE; array_parm_static = 0; } if (TREE_CODE (declarator) == TREE_LIST) { /* We encode a declarator with embedded attributes using a TREE_LIST. */ tree attrs = TREE_PURPOSE (declarator); tree inner_decl; int attr_flags = 0; declarator = TREE_VALUE (declarator); inner_decl = declarator; while (inner_decl != NULL_TREE && TREE_CODE (inner_decl) == TREE_LIST) inner_decl = TREE_VALUE (inner_decl); if (inner_decl == NULL_TREE || TREE_CODE (inner_decl) == IDENTIFIER_NODE) attr_flags |= (int) ATTR_FLAG_DECL_NEXT; else if (TREE_CODE (inner_decl) == CALL_EXPR) attr_flags |= (int) ATTR_FLAG_FUNCTION_NEXT; else if (TREE_CODE (inner_decl) == ARRAY_REF) attr_flags |= (int) ATTR_FLAG_ARRAY_NEXT; returned_attrs = decl_attributes (&type, chainon (returned_attrs, attrs), attr_flags); } else if (TREE_CODE (declarator) == ARRAY_REF) { tree itype = NULL_TREE; tree size = TREE_OPERAND (declarator, 1); /* The index is a signed object `sizetype' bits wide. */ tree index_type = c_common_signed_type (sizetype); array_ptr_quals = TREE_TYPE (declarator); array_parm_static = TREE_STATIC (declarator); declarator = TREE_OPERAND (declarator, 0); /* Check for some types that there cannot be arrays of. */ if (VOID_TYPE_P (type)) { error ("declaration of `%s' as array of voids", name); type = error_mark_node; } if (TREE_CODE (type) == FUNCTION_TYPE) { error ("declaration of `%s' as array of functions", name); type = error_mark_node; } if (pedantic && !in_system_header && flexible_array_type_p (type)) pedwarn ("invalid use of structure with flexible array member"); if (size == error_mark_node) type = error_mark_node; if (type == error_mark_node) continue; /* If size was specified, set ITYPE to a range-type for that size. Otherwise, ITYPE remains null. finish_decl may figure it out from an initial value. */ if (size) { /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */ STRIP_TYPE_NOPS (size); if (! INTEGRAL_TYPE_P (TREE_TYPE (size))) { error ("size of array `%s' has non-integer type", name); size = integer_one_node; } if (pedantic && integer_zerop (size)) pedwarn ("ISO C forbids zero-size array `%s'", name); if (TREE_CODE (size) == INTEGER_CST) { constant_expression_warning (size); if (tree_int_cst_sgn (size) < 0) { error ("size of array `%s' is negative", name); size = integer_one_node; } } else { /* Make sure the array size remains visibly nonconstant even if it is (eg) a const variable with known value. */ size_varies = 1; if (!flag_isoc99 && pedantic) { if (TREE_CONSTANT (size)) pedwarn ("ISO C90 forbids array `%s' whose size can't be evaluated", name); else pedwarn ("ISO C90 forbids variable-size array `%s'", name); } } if (integer_zerop (size)) { /* A zero-length array cannot be represented with an unsigned index type, which is what we'll get with build_index_type. Create an open-ended range instead. */ itype = build_range_type (sizetype, size, NULL_TREE); } else { /* Compute the maximum valid index, that is, size - 1. Do the calculation in index_type, so that if it is a variable the computations will be done in the proper mode. */ itype = fold (build (MINUS_EXPR, index_type, convert (index_type, size), convert (index_type, size_one_node))); /* If that overflowed, the array is too big. ??? While a size of INT_MAX+1 technically shouldn't cause an overflow (because we subtract 1), the overflow is recorded during the conversion to index_type, before the subtraction. Handling this case seems like an unnecessary complication. */ if (TREE_OVERFLOW (itype)) { error ("size of array `%s' is too large", name); type = error_mark_node; continue; } if (size_varies) { /* We must be able to distinguish the SAVE_EXPR_CONTEXT for the variably-sized type so that we can set it correctly in set_save_expr_context. The convention is that all SAVE_EXPRs that need to be reset have NULL_TREE for their SAVE_EXPR_CONTEXT. */ tree cfd = current_function_decl; if (decl_context == PARM) current_function_decl = NULL_TREE; itype = variable_size (itype); if (decl_context == PARM) current_function_decl = cfd; } itype = build_index_type (itype); } } else if (decl_context == FIELD) { if (pedantic && !flag_isoc99 && !in_system_header) pedwarn ("ISO C90 does not support flexible array members"); /* ISO C99 Flexible array members are effectively identical to GCC's zero-length array extension. */ itype = build_range_type (sizetype, size_zero_node, NULL_TREE); } /* If pedantic, complain about arrays of incomplete types. */ if (pedantic && !COMPLETE_TYPE_P (type)) pedwarn ("array type has incomplete element type"); /* Build the array type itself, then merge any constancy or volatility into the target type. We must do it in this order to ensure that the TYPE_MAIN_VARIANT field of the array type is set correctly. */ type = build_array_type (type, itype); if (type_quals) type = c_build_qualified_type (type, type_quals); if (size_varies) C_TYPE_VARIABLE_SIZE (type) = 1; /* The GCC extension for zero-length arrays differs from ISO flexible array members in that sizeof yields zero. */ if (size && integer_zerop (size)) { layout_type (type); TYPE_SIZE (type) = bitsize_zero_node; TYPE_SIZE_UNIT (type) = size_zero_node; } if (decl_context != PARM && (array_ptr_quals != NULL_TREE || array_parm_static)) { error ("static or type qualifiers in non-parameter array declarator"); array_ptr_quals = NULL_TREE; array_parm_static = 0; } } else if (TREE_CODE (declarator) == CALL_EXPR) { /* Say it's a definition only for the declarator closest to the identifier, apart possibly from some attributes. */ bool really_funcdef = false; tree arg_types; if (funcdef_flag) { tree t = TREE_OPERAND (declarator, 0); while (TREE_CODE (t) == TREE_LIST) t = TREE_VALUE (t); really_funcdef = (TREE_CODE (t) == IDENTIFIER_NODE); } /* Declaring a function type. Make sure we have a valid type for the function to return. */ if (type == error_mark_node) continue; size_varies = 0; /* Warn about some types functions can't return. */ if (TREE_CODE (type) == FUNCTION_TYPE) { error ("`%s' declared as function returning a function", name); type = integer_type_node; } if (TREE_CODE (type) == ARRAY_TYPE) { error ("`%s' declared as function returning an array", name); type = integer_type_node; } /* Construct the function type and go to the next inner layer of declarator. */ arg_types = grokparms (TREE_OPERAND (declarator, 1), really_funcdef); /* Type qualifiers before the return type of the function qualify the return type, not the function type. */ if (type_quals) { /* Type qualifiers on a function return type are normally permitted by the standard but have no effect, so give a warning at -Wextra. Qualifiers on a void return type have meaning as a GNU extension, and are banned on function definitions in ISO C. FIXME: strictly we shouldn't pedwarn for qualified void return types except on function definitions, but not doing so could lead to the undesirable state of a "volatile void" function return type not being warned about, and a use of the function being compiled with GNU semantics, with no diagnostics under -pedantic. */ if (VOID_TYPE_P (type) && pedantic && !in_system_header) pedwarn ("ISO C forbids qualified void function return type"); else if (extra_warnings && !(VOID_TYPE_P (type) && type_quals == TYPE_QUAL_VOLATILE)) warning ("type qualifiers ignored on function return type"); type = c_build_qualified_type (type, type_quals); } type_quals = TYPE_UNQUALIFIED; type = build_function_type (type, arg_types); declarator = TREE_OPERAND (declarator, 0); /* Set the TYPE_CONTEXTs for each tagged type which is local to the formal parameter list of this FUNCTION_TYPE to point to the FUNCTION_TYPE node itself. */ { tree link; for (link = last_function_parm_tags; link; link = TREE_CHAIN (link)) TYPE_CONTEXT (TREE_VALUE (link)) = type; } } else if (TREE_CODE (declarator) == INDIRECT_REF) { /* Merge any constancy or volatility into the target type for the pointer. */ if (pedantic && TREE_CODE (type) == FUNCTION_TYPE && type_quals) pedwarn ("ISO C forbids qualified function types"); if (type_quals) type = c_build_qualified_type (type, type_quals); type_quals = TYPE_UNQUALIFIED; size_varies = 0; type = build_pointer_type (type); /* Process a list of type modifier keywords (such as const or volatile) that were given inside the `*'. */ if (TREE_TYPE (declarator)) { tree typemodlist; int erred = 0; constp = 0; volatilep = 0; restrictp = 0; for (typemodlist = TREE_TYPE (declarator); typemodlist; typemodlist = TREE_CHAIN (typemodlist)) { tree qualifier = TREE_VALUE (typemodlist); if (C_IS_RESERVED_WORD (qualifier)) { if (C_RID_CODE (qualifier) == RID_CONST) constp++; else if (C_RID_CODE (qualifier) == RID_VOLATILE) volatilep++; else if (C_RID_CODE (qualifier) == RID_RESTRICT) restrictp++; else erred++; } else erred++; } if (erred) error ("invalid type modifier within pointer declarator"); if (pedantic && !flag_isoc99) { if (constp > 1) pedwarn ("duplicate `const'"); if (volatilep > 1) pedwarn ("duplicate `volatile'"); if (restrictp > 1) pedwarn ("duplicate `restrict'"); } type_quals = ((constp ? TYPE_QUAL_CONST : 0) | (restrictp ? TYPE_QUAL_RESTRICT : 0) | (volatilep ? TYPE_QUAL_VOLATILE : 0)); } declarator = TREE_OPERAND (declarator, 0); } else abort (); } /* Now TYPE has the actual type. */ /* Check the type and width of a bit-field. */ if (bitfield) check_bitfield_type_and_width (&type, width, orig_name); /* Did array size calculations overflow? */ if (TREE_CODE (type) == ARRAY_TYPE && COMPLETE_TYPE_P (type) && TREE_OVERFLOW (TYPE_SIZE (type))) { error ("size of array `%s' is too large", name); /* If we proceed with the array type as it is, we'll eventually crash in tree_low_cst(). */ type = error_mark_node; } /* If this is declaring a typedef name, return a TYPE_DECL. */ if (specbits & (1 << (int) RID_TYPEDEF)) { tree decl; /* Note that the grammar rejects storage classes in typenames, fields or parameters */ if (pedantic && TREE_CODE (type) == FUNCTION_TYPE && type_quals) pedwarn ("ISO C forbids qualified function types"); if (type_quals) type = c_build_qualified_type (type, type_quals); decl = build_decl (TYPE_DECL, declarator, type); if ((specbits & (1 << (int) RID_SIGNED)) || (typedef_decl && C_TYPEDEF_EXPLICITLY_SIGNED (typedef_decl))) C_TYPEDEF_EXPLICITLY_SIGNED (decl) = 1; decl_attributes (&decl, returned_attrs, 0); return decl; } /* Detect the case of an array type of unspecified size which came, as such, direct from a typedef name. We must copy the type, so that each identifier gets a distinct type, so that each identifier's size can be controlled separately by its own initializer. */ if (type != 0 && typedef_type != 0 && TREE_CODE (type) == ARRAY_TYPE && TYPE_DOMAIN (type) == 0 && TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (typedef_type)) { type = build_array_type (TREE_TYPE (type), 0); if (size_varies) C_TYPE_VARIABLE_SIZE (type) = 1; } /* If this is a type name (such as, in a cast or sizeof), compute the type and return it now. */ if (decl_context == TYPENAME) { /* Note that the grammar rejects storage classes in typenames, fields or parameters */ if (pedantic && TREE_CODE (type) == FUNCTION_TYPE && type_quals) pedwarn ("ISO C forbids const or volatile function types"); if (type_quals) type = c_build_qualified_type (type, type_quals); decl_attributes (&type, returned_attrs, 0); return type; } /* Aside from typedefs and type names (handle above), `void' at top level (not within pointer) is allowed only in public variables. We don't complain about parms either, but that is because a better error message can be made later. */ if (VOID_TYPE_P (type) && decl_context != PARM && ! ((decl_context != FIELD && TREE_CODE (type) != FUNCTION_TYPE) && ((specbits & (1 << (int) RID_EXTERN)) || (current_scope == global_scope && !(specbits & ((1 << (int) RID_STATIC) | (1 << (int) RID_REGISTER))))))) { error ("variable or field `%s' declared void", name); type = integer_type_node; } /* Now create the decl, which may be a VAR_DECL, a PARM_DECL or a FUNCTION_DECL, depending on DECL_CONTEXT and TYPE. */ { tree decl; if (decl_context == PARM) { tree type_as_written; tree promoted_type; /* A parameter declared as an array of T is really a pointer to T. One declared as a function is really a pointer to a function. */ if (TREE_CODE (type) == ARRAY_TYPE) { /* Transfer const-ness of array into that of type pointed to. */ type = TREE_TYPE (type); if (type_quals) type = c_build_qualified_type (type, type_quals); type = build_pointer_type (type); type_quals = TYPE_UNQUALIFIED; if (array_ptr_quals) { tree new_ptr_quals, new_ptr_attrs; int erred = 0; split_specs_attrs (array_ptr_quals, &new_ptr_quals, &new_ptr_attrs); /* We don't yet implement attributes in this context. */ if (new_ptr_attrs != NULL_TREE) warning ("attributes in parameter array declarator ignored"); constp = 0; volatilep = 0; restrictp = 0; for (; new_ptr_quals; new_ptr_quals = TREE_CHAIN (new_ptr_quals)) { tree qualifier = TREE_VALUE (new_ptr_quals); if (C_IS_RESERVED_WORD (qualifier)) { if (C_RID_CODE (qualifier) == RID_CONST) constp++; else if (C_RID_CODE (qualifier) == RID_VOLATILE) volatilep++; else if (C_RID_CODE (qualifier) == RID_RESTRICT) restrictp++; else erred++; } else erred++; } if (erred) error ("invalid type modifier within array declarator"); type_quals = ((constp ? TYPE_QUAL_CONST : 0) | (restrictp ? TYPE_QUAL_RESTRICT : 0) | (volatilep ? TYPE_QUAL_VOLATILE : 0)); } size_varies = 0; } else if (TREE_CODE (type) == FUNCTION_TYPE) { if (pedantic && type_quals) pedwarn ("ISO C forbids qualified function types"); if (type_quals) type = c_build_qualified_type (type, type_quals); type = build_pointer_type (type); type_quals = TYPE_UNQUALIFIED; } else if (type_quals) type = c_build_qualified_type (type, type_quals); type_as_written = type; decl = build_decl (PARM_DECL, declarator, type); if (size_varies) C_DECL_VARIABLE_SIZE (decl) = 1; /* Compute the type actually passed in the parmlist, for the case where there is no prototype. (For example, shorts and chars are passed as ints.) When there is a prototype, this is overridden later. */ if (type == error_mark_node) promoted_type = type; else promoted_type = c_type_promotes_to (type); DECL_ARG_TYPE (decl) = promoted_type; DECL_ARG_TYPE_AS_WRITTEN (decl) = type_as_written; } else if (decl_context == FIELD) { /* Structure field. It may not be a function. */ if (TREE_CODE (type) == FUNCTION_TYPE) { error ("field `%s' declared as a function", name); type = build_pointer_type (type); } else if (TREE_CODE (type) != ERROR_MARK && !COMPLETE_OR_UNBOUND_ARRAY_TYPE_P (type)) { error ("field `%s' has incomplete type", name); type = error_mark_node; } /* Move type qualifiers down to element of an array. */ if (TREE_CODE (type) == ARRAY_TYPE && type_quals) type = build_array_type (c_build_qualified_type (TREE_TYPE (type), type_quals), TYPE_DOMAIN (type)); decl = build_decl (FIELD_DECL, declarator, type); DECL_NONADDRESSABLE_P (decl) = bitfield; if (size_varies) C_DECL_VARIABLE_SIZE (decl) = 1; } else if (TREE_CODE (type) == FUNCTION_TYPE) { /* Every function declaration is "external" except for those which are inside a function body in which `auto' is used. That is a case not specified by ANSI C, and we use it for forward declarations for nested functions. */ int extern_ref = (!(specbits & (1 << (int) RID_AUTO)) || current_scope == global_scope); if (specbits & (1 << (int) RID_AUTO) && (pedantic || current_scope == global_scope)) pedwarn ("invalid storage class for function `%s'", name); if (specbits & (1 << (int) RID_REGISTER)) error ("invalid storage class for function `%s'", name); if (specbits & (1 << (int) RID_THREAD)) error ("invalid storage class for function `%s'", name); /* Function declaration not at file scope. Storage classes other than `extern' are not allowed and `extern' makes no difference. */ if (current_scope != global_scope && (specbits & ((1 << (int) RID_STATIC) | (1 << (int) RID_INLINE))) && pedantic) pedwarn ("invalid storage class for function `%s'", name); decl = build_decl (FUNCTION_DECL, declarator, type); decl = build_decl_attribute_variant (decl, decl_attr); DECL_LANG_SPECIFIC (decl) = ggc_alloc_cleared (sizeof (struct lang_decl)); if (pedantic && type_quals && ! DECL_IN_SYSTEM_HEADER (decl)) pedwarn ("ISO C forbids qualified function types"); /* GNU C interprets a `volatile void' return type to indicate that the function does not return. */ if ((type_quals & TYPE_QUAL_VOLATILE) && !VOID_TYPE_P (TREE_TYPE (TREE_TYPE (decl)))) warning ("`noreturn' function returns non-void value"); if (extern_ref) DECL_EXTERNAL (decl) = 1; /* Record absence of global scope for `static' or `auto'. */ TREE_PUBLIC (decl) = !(specbits & ((1 << (int) RID_STATIC) | (1 << (int) RID_AUTO))); if (defaulted_int) C_FUNCTION_IMPLICIT_INT (decl) = 1; /* Record presence of `inline', if it is reasonable. */ if (MAIN_NAME_P (declarator)) { if (inlinep) warning ("cannot inline function `main'"); } else if (inlinep) { /* Record that the function is declared `inline'. */ DECL_DECLARED_INLINE_P (decl) = 1; /* Do not mark bare declarations as DECL_INLINE. Doing so in the presence of multiple declarations can result in the abstract origin pointing between the declarations, which will confuse dwarf2out. */ if (initialized) { DECL_INLINE (decl) = 1; if (specbits & (1 << (int) RID_EXTERN)) current_extern_inline = 1; } } /* If -finline-functions, assume it can be inlined. This does two things: let the function be deferred until it is actually needed, and let dwarf2 know that the function is inlinable. */ else if (flag_inline_trees == 2 && initialized) DECL_INLINE (decl) = 1; } else { /* It's a variable. */ /* An uninitialized decl with `extern' is a reference. */ int extern_ref = !initialized && (specbits & (1 << (int) RID_EXTERN)); /* Move type qualifiers down to element of an array. */ if (TREE_CODE (type) == ARRAY_TYPE && type_quals) { int saved_align = TYPE_ALIGN(type); type = build_array_type (c_build_qualified_type (TREE_TYPE (type), type_quals), TYPE_DOMAIN (type)); TYPE_ALIGN (type) = saved_align; } else if (type_quals) type = c_build_qualified_type (type, type_quals); /* It is invalid to create an `extern' declaration for a variable if there is a global declaration that is `static' and the global declaration is not visible. */ if (extern_ref && current_scope != global_scope) { tree global_decl; global_decl = identifier_global_value (declarator); if (global_decl && TREE_CODE (global_decl) == VAR_DECL && lookup_name (declarator) != global_decl && !TREE_PUBLIC (global_decl)) error ("variable previously declared `static' redeclared " "`extern'"); } decl = build_decl (VAR_DECL, declarator, type); if (size_varies) C_DECL_VARIABLE_SIZE (decl) = 1; if (inlinep) pedwarn ("%Jvariable '%D' declared `inline'", decl, decl); DECL_EXTERNAL (decl) = extern_ref; /* At file scope, the presence of a `static' or `register' storage class specifier, or the absence of all storage class specifiers makes this declaration a definition (perhaps tentative). Also, the absence of both `static' and `register' makes it public. */ if (current_scope == global_scope) { TREE_PUBLIC (decl) = !(specbits & ((1 << (int) RID_STATIC) | (1 << (int) RID_REGISTER))); TREE_STATIC (decl) = !extern_ref; } /* Not at file scope, only `static' makes a static definition. */ else { TREE_STATIC (decl) = (specbits & (1 << (int) RID_STATIC)) != 0; TREE_PUBLIC (decl) = extern_ref; } if (specbits & 1 << (int) RID_THREAD) { if (targetm.have_tls) DECL_THREAD_LOCAL (decl) = 1; else /* A mere warning is sure to result in improper semantics at runtime. Don't bother to allow this to compile. */ error ("thread-local storage not supported for this target"); } } /* Record `register' declaration for warnings on & and in case doing stupid register allocation. */ if (specbits & (1 << (int) RID_REGISTER)) DECL_REGISTER (decl) = 1; /* Record constancy and volatility. */ c_apply_type_quals_to_decl (type_quals, decl); /* If a type has volatile components, it should be stored in memory. Otherwise, the fact that those components are volatile will be ignored, and would even crash the compiler. */ if (C_TYPE_FIELDS_VOLATILE (TREE_TYPE (decl))) c_mark_addressable (decl); #ifdef ENABLE_CHECKING /* This is the earliest point at which we might know the assembler name of a variable. Thus, if it's known before this, die horribly. */ if (DECL_ASSEMBLER_NAME_SET_P (decl)) abort (); #endif decl_attributes (&decl, returned_attrs, 0); return decl; } } /* Decode the parameter-list info for a function type or function definition. The argument is the value returned by `get_parm_info' (or made in parse.y if there is an identifier list instead of a parameter decl list). These two functions are separate because when a function returns or receives functions then each is called multiple times but the order of calls is different. The last call to `grokparms' is always the one that contains the formal parameter names of a function definition. Store in `last_function_parms' a chain of the decls of parms. Also store in `last_function_parm_tags' a chain of the struct, union, and enum tags declared among the parms. Return a list of arg types to use in the FUNCTION_TYPE for this function. FUNCDEF_FLAG is nonzero for a function definition, 0 for a mere declaration. A nonempty identifier-list gets an error message when FUNCDEF_FLAG is zero. */ static tree grokparms (tree parms_info, int funcdef_flag) { tree first_parm = TREE_CHAIN (parms_info); last_function_parms = TREE_PURPOSE (parms_info); last_function_parm_tags = TREE_VALUE (parms_info); last_function_parm_others = TREE_TYPE (parms_info); if (warn_strict_prototypes && first_parm == 0 && !funcdef_flag && !in_system_header) warning ("function declaration isn't a prototype"); if (first_parm != 0 && TREE_CODE (TREE_VALUE (first_parm)) == IDENTIFIER_NODE) { if (! funcdef_flag) pedwarn ("parameter names (without types) in function declaration"); last_function_parms = first_parm; return 0; } else { tree parm; tree typelt; /* If the arg types are incomplete in a declaration, they must include undefined tags. These tags can never be defined in the scope of the declaration, so the types can never be completed, and no call can be compiled successfully. */ for (parm = last_function_parms, typelt = first_parm; parm; parm = TREE_CHAIN (parm)) /* Skip over any enumeration constants declared here. */ if (TREE_CODE (parm) == PARM_DECL) { /* Barf if the parameter itself has an incomplete type. */ tree type = TREE_VALUE (typelt); if (type == error_mark_node) continue; if (!COMPLETE_TYPE_P (type)) { if (funcdef_flag && DECL_NAME (parm) != 0) error ("parameter `%s' has incomplete type", IDENTIFIER_POINTER (DECL_NAME (parm))); else warning ("parameter has incomplete type"); if (funcdef_flag) { TREE_VALUE (typelt) = error_mark_node; TREE_TYPE (parm) = error_mark_node; } } typelt = TREE_CHAIN (typelt); } return first_parm; } } /* Return a tree_list node with info on a parameter list just parsed. The TREE_PURPOSE is a list of decls of those parms. The TREE_VALUE is a list of structure, union and enum tags defined. The TREE_CHAIN is a list of argument types to go in the FUNCTION_TYPE. The TREE_TYPE is a list of non-parameter decls which appeared with the parameters. This tree_list node is later fed to `grokparms'. VOID_AT_END nonzero means append `void' to the end of the type-list. Zero means the parmlist ended with an ellipsis so don't append `void'. */ tree get_parm_info (int void_at_end) { tree decl, type, list; tree types = 0; tree *last_type = &types; tree tags = current_scope->tags; tree parms = current_scope->parms; tree others = current_scope->names; static bool explained_incomplete_types = false; bool gave_void_only_once_err = false; /* Just "void" (and no ellipsis) is special. There are really no parms. But if the "void" is qualified (by "const" or "volatile"), or has a storage class specifier ("register"), then the behavior is undefined; issue an error. Typedefs for "void" are OK (see DR#157). */ if (void_at_end && parms != 0 && TREE_CHAIN (parms) == 0 && VOID_TYPE_P (TREE_TYPE (parms)) && !DECL_NAME (parms)) { if (TREE_THIS_VOLATILE (parms) || TREE_READONLY (parms) || DECL_REGISTER (parms)) error ("\"void\" as only parameter may not be qualified"); return tree_cons (0, 0, tree_cons (0, void_type_node, 0)); } /* Sanity check all of the parameter declarations. */ for (decl = parms; decl; decl = TREE_CHAIN (decl)) { if (TREE_CODE (decl) != PARM_DECL) abort (); if (TREE_ASM_WRITTEN (decl)) abort (); /* Since there is a prototype, args are passed in their declared types. The back end may override this. */ type = TREE_TYPE (decl); DECL_ARG_TYPE (decl) = type; /* Check for (..., void, ...) and issue an error. */ if (VOID_TYPE_P (type) && !DECL_NAME (decl) && !gave_void_only_once_err) { error ("\"void\" must be the only parameter"); gave_void_only_once_err = true; } type = build_tree_list (0, type); *last_type = type; last_type = &TREE_CHAIN (type); } /* Check the list of non-parameter decls for any forward parm decls that never got real decls. */ for (decl = others; decl; decl = TREE_CHAIN (decl)) if (TREE_CODE (decl) == PARM_DECL) { if (!TREE_ASM_WRITTEN (decl)) abort (); error ("%Jparameter \"%D\" has just a forward declaration", decl, decl); } /* Warn about any struct, union or enum tags defined within this list. The scope of such types is limited to this declaration, which is rarely if ever desirable (it's impossible to call such a function with type-correct arguments). */ for (decl = tags; decl; decl = TREE_CHAIN (decl)) { enum tree_code code = TREE_CODE (TREE_VALUE (decl)); const char *keyword; /* An anonymous union parm type is meaningful as a GNU extension. So don't warn for that. */ if (code == UNION_TYPE && TREE_PURPOSE (decl) == 0 && !pedantic) continue; /* The keyword should not be translated. */ switch (code) { case RECORD_TYPE: keyword = "struct"; break; case UNION_TYPE: keyword = "union"; break; case ENUMERAL_TYPE: keyword = "enum"; break; default: abort (); } if (TREE_PURPOSE (decl)) /* The first %s will be one of 'struct', 'union', or 'enum'. */ warning ("\"%s %s\" declared inside parameter list", keyword, IDENTIFIER_POINTER (TREE_PURPOSE (decl))); else /* The %s will be one of 'struct', 'union', or 'enum'. */ warning ("anonymous %s declared inside parameter list", keyword); if (! explained_incomplete_types) { warning ("its scope is only this definition or declaration," " which is probably not what you want"); explained_incomplete_types = true; } } if (void_at_end) { type = build_tree_list (0, void_type_node); *last_type = type; } list = tree_cons (parms, tags, types); TREE_TYPE (list) = others; return list; } /* Get the struct, enum or union (CODE says which) with tag NAME. Define the tag as a forward-reference if it is not defined. */ tree xref_tag (enum tree_code code, tree name) { /* If a cross reference is requested, look up the type already defined for this tag and return it. */ tree ref = lookup_tag (code, name, 0); /* If this is the right type of tag, return what we found. (This reference will be shadowed by shadow_tag later if appropriate.) If this is the wrong type of tag, do not return it. If it was the wrong type in the same scope, we will have had an error message already; if in a different scope and declaring a name, pending_xref_error will give an error message; but if in a different scope and not declaring a name, this tag should shadow the previous declaration of a different type of tag, and this would not work properly if we return the reference found. (For example, with "struct foo" in an outer scope, "union foo;" must shadow that tag with a new one of union type.) */ if (ref && TREE_CODE (ref) == code) return ref; /* If no such tag is yet defined, create a forward-reference node and record it as the "definition". When a real declaration of this type is found, the forward-reference will be altered into a real type. */ ref = make_node (code); if (code == ENUMERAL_TYPE) { /* Give the type a default layout like unsigned int to avoid crashing if it does not get defined. */ TYPE_MODE (ref) = TYPE_MODE (unsigned_type_node); TYPE_ALIGN (ref) = TYPE_ALIGN (unsigned_type_node); TYPE_USER_ALIGN (ref) = 0; TREE_UNSIGNED (ref) = 1; TYPE_PRECISION (ref) = TYPE_PRECISION (unsigned_type_node); TYPE_MIN_VALUE (ref) = TYPE_MIN_VALUE (unsigned_type_node); TYPE_MAX_VALUE (ref) = TYPE_MAX_VALUE (unsigned_type_node); } pushtag (name, ref); return ref; } /* Make sure that the tag NAME is defined *in the current scope* at least as a forward reference. CODE says which kind of tag NAME ought to be. */ tree start_struct (enum tree_code code, tree name) { /* If there is already a tag defined at this scope (as a forward reference), just return it. */ tree ref = 0; if (name != 0) ref = lookup_tag (code, name, 1); if (ref && TREE_CODE (ref) == code) { - if (TYPE_FIELDS (ref)) + if (TYPE_SIZE (ref)) { if (code == UNION_TYPE) error ("redefinition of `union %s'", IDENTIFIER_POINTER (name)); else error ("redefinition of `struct %s'", IDENTIFIER_POINTER (name)); } + else if (C_TYPE_BEING_DEFINED (ref)) + { + if (code == UNION_TYPE) + error ("nested redefinition of `union %s'", + IDENTIFIER_POINTER (name)); + else + error ("nested redefinition of `struct %s'", + IDENTIFIER_POINTER (name)); + } } else { /* Otherwise create a forward-reference just so the tag is in scope. */ ref = make_node (code); pushtag (name, ref); } C_TYPE_BEING_DEFINED (ref) = 1; TYPE_PACKED (ref) = flag_pack_struct; return ref; } /* Process the specs, declarator (NULL if omitted) and width (NULL if omitted) of a structure component, returning a FIELD_DECL node. WIDTH is non-NULL for bit-fields only, and is an INTEGER_CST node. This is done during the parsing of the struct declaration. The FIELD_DECL nodes are chained together and the lot of them are ultimately passed to `build_struct' to make the RECORD_TYPE node. */ tree grokfield (tree declarator, tree declspecs, tree width) { tree value; if (declarator == NULL_TREE && width == NULL_TREE) { /* This is an unnamed decl. If we have something of the form "union { list } ;" then this is the anonymous union extension. Similarly for struct. If this is something of the form "struct foo;", then If MS extensions are enabled, this is handled as an anonymous struct. Otherwise this is a forward declaration of a structure tag. If this is something of the form "foo;" and foo is a TYPE_DECL, then If MS extensions are enabled and foo names a structure, then again this is an anonymous struct. Otherwise this is an error. Oh what a horrid tangled web we weave. I wonder if MS consciously took this from Plan 9 or if it was an accident of implementation that took root before someone noticed the bug... */ tree type = TREE_VALUE (declspecs); if (flag_ms_extensions && TREE_CODE (type) == TYPE_DECL) type = TREE_TYPE (type); if (TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE) { if (flag_ms_extensions) ; /* ok */ else if (flag_iso) goto warn_unnamed_field; else if (TYPE_NAME (type) == NULL) ; /* ok */ else goto warn_unnamed_field; } else { warn_unnamed_field: warning ("declaration does not declare anything"); return NULL_TREE; } } value = grokdeclarator (declarator, declspecs, FIELD, 0, width ? &width : NULL); finish_decl (value, NULL_TREE, NULL_TREE); DECL_INITIAL (value) = width; return value; } /* Generate an error for any duplicate field names in FIELDLIST. Munge the list such that this does not present a problem later. */ static void detect_field_duplicates (tree fieldlist) { tree x, y; int timeout = 10; /* First, see if there are more than "a few" fields. This is trivially true if there are zero or one fields. */ if (!fieldlist) return; x = TREE_CHAIN (fieldlist); if (!x) return; do { timeout--; x = TREE_CHAIN (x); } while (timeout > 0 && x); /* If there were "few" fields, avoid the overhead of allocating a hash table. Instead just do the nested traversal thing. */ if (timeout > 0) { for (x = TREE_CHAIN (fieldlist); x ; x = TREE_CHAIN (x)) if (DECL_NAME (x)) { for (y = fieldlist; y != x; y = TREE_CHAIN (y)) if (DECL_NAME (y) == DECL_NAME (x)) { error ("%Jduplicate member '%D'", x, x); DECL_NAME (x) = NULL_TREE; } } } else { htab_t htab = htab_create (37, htab_hash_pointer, htab_eq_pointer, NULL); void **slot; for (x = fieldlist; x ; x = TREE_CHAIN (x)) if ((y = DECL_NAME (x)) != 0) { slot = htab_find_slot (htab, y, INSERT); if (*slot) { error ("%Jduplicate member '%D'", x, x); DECL_NAME (x) = NULL_TREE; } *slot = y; } htab_delete (htab); } } /* Fill in the fields of a RECORD_TYPE or UNION_TYPE node, T. FIELDLIST is a chain of FIELD_DECL nodes for the fields. ATTRIBUTES are attributes to be applied to the structure. */ tree finish_struct (tree t, tree fieldlist, tree attributes) { tree x; int toplevel = global_scope == current_scope; int saw_named_field; /* If this type was previously laid out as a forward reference, make sure we lay it out again. */ TYPE_SIZE (t) = 0; decl_attributes (&t, attributes, (int) ATTR_FLAG_TYPE_IN_PLACE); /* Nameless union parm types are useful as GCC extension. */ if (! (TREE_CODE (t) == UNION_TYPE && TYPE_NAME (t) == 0) && !pedantic) /* Otherwise, warn about any struct or union def. in parmlist. */ if (in_parm_level_p ()) { if (pedantic) pedwarn ("%s defined inside parms", TREE_CODE (t) == UNION_TYPE ? _("union") : _("structure")); else warning ("%s defined inside parms", TREE_CODE (t) == UNION_TYPE ? _("union") : _("structure")); } if (pedantic) { for (x = fieldlist; x; x = TREE_CHAIN (x)) if (DECL_NAME (x) != 0) break; if (x == 0) pedwarn ("%s has no %s", TREE_CODE (t) == UNION_TYPE ? _("union") : _("struct"), fieldlist ? _("named members") : _("members")); } /* Install struct as DECL_CONTEXT of each field decl. Also process specified field sizes,m which is found in the DECL_INITIAL. Store 0 there, except for ": 0" fields (so we can find them and delete them, below). */ saw_named_field = 0; for (x = fieldlist; x; x = TREE_CHAIN (x)) { DECL_CONTEXT (x) = t; DECL_PACKED (x) |= TYPE_PACKED (t); /* If any field is const, the structure type is pseudo-const. */ if (TREE_READONLY (x)) C_TYPE_FIELDS_READONLY (t) = 1; else { /* A field that is pseudo-const makes the structure likewise. */ tree t1 = TREE_TYPE (x); while (TREE_CODE (t1) == ARRAY_TYPE) t1 = TREE_TYPE (t1); if ((TREE_CODE (t1) == RECORD_TYPE || TREE_CODE (t1) == UNION_TYPE) && C_TYPE_FIELDS_READONLY (t1)) C_TYPE_FIELDS_READONLY (t) = 1; } /* Any field that is volatile means variables of this type must be treated in some ways as volatile. */ if (TREE_THIS_VOLATILE (x)) C_TYPE_FIELDS_VOLATILE (t) = 1; /* Any field of nominal variable size implies structure is too. */ if (C_DECL_VARIABLE_SIZE (x)) C_TYPE_VARIABLE_SIZE (t) = 1; - /* Detect invalid nested redefinition. */ - if (TREE_TYPE (x) == t) - error ("nested redefinition of `%s'", - IDENTIFIER_POINTER (TYPE_NAME (t))); - if (DECL_INITIAL (x)) { unsigned HOST_WIDE_INT width = tree_low_cst (DECL_INITIAL (x), 1); DECL_SIZE (x) = bitsize_int (width); DECL_BIT_FIELD (x) = 1; SET_DECL_C_BIT_FIELD (x); } DECL_INITIAL (x) = 0; /* Detect flexible array member in an invalid context. */ if (TREE_CODE (TREE_TYPE (x)) == ARRAY_TYPE && TYPE_SIZE (TREE_TYPE (x)) == NULL_TREE && TYPE_DOMAIN (TREE_TYPE (x)) != NULL_TREE && TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (x))) == NULL_TREE) { if (TREE_CODE (t) == UNION_TYPE) { error ("%Jflexible array member in union", x); TREE_TYPE (x) = error_mark_node; } else if (TREE_CHAIN (x) != NULL_TREE) { error ("%Jflexible array member not at end of struct", x); TREE_TYPE (x) = error_mark_node; } else if (! saw_named_field) { error ("%Jflexible array member in otherwise empty struct", x); TREE_TYPE (x) = error_mark_node; } } if (pedantic && !in_system_header && TREE_CODE (t) == RECORD_TYPE && flexible_array_type_p (TREE_TYPE (x))) pedwarn ("%Jinvalid use of structure with flexible array member", x); if (DECL_NAME (x)) saw_named_field = 1; } detect_field_duplicates (fieldlist); /* Now we have the nearly final fieldlist. Record it, then lay out the structure or union (including the fields). */ TYPE_FIELDS (t) = fieldlist; layout_type (t); /* Delete all zero-width bit-fields from the fieldlist. */ { tree *fieldlistp = &fieldlist; while (*fieldlistp) if (TREE_CODE (*fieldlistp) == FIELD_DECL && DECL_INITIAL (*fieldlistp)) *fieldlistp = TREE_CHAIN (*fieldlistp); else fieldlistp = &TREE_CHAIN (*fieldlistp); } /* Now we have the truly final field list. Store it in this type and in the variants. */ TYPE_FIELDS (t) = fieldlist; /* If there are lots of fields, sort so we can look through them fast. We arbitrarily consider 16 or more elts to be "a lot". */ { int len = 0; for (x = fieldlist; x; x = TREE_CHAIN (x)) { if (len > 15 || DECL_NAME (x) == NULL) break; len += 1; } if (len > 15) { tree *field_array; struct lang_type *space; struct sorted_fields_type *space2; len += list_length (x); /* Use the same allocation policy here that make_node uses, to ensure that this lives as long as the rest of the struct decl. All decls in an inline function need to be saved. */ space = ggc_alloc (sizeof (struct lang_type)); space2 = ggc_alloc (sizeof (struct sorted_fields_type) + len * sizeof (tree)); len = 0; space->s = space2; field_array = &space2->elts[0]; for (x = fieldlist; x; x = TREE_CHAIN (x)) { field_array[len++] = x; /* If there is anonymous struct or union, break out of the loop. */ if (DECL_NAME (x) == NULL) break; } /* Found no anonymous struct/union. Add the TYPE_LANG_SPECIFIC. */ if (x == NULL) { TYPE_LANG_SPECIFIC (t) = space; TYPE_LANG_SPECIFIC (t)->s->len = len; field_array = TYPE_LANG_SPECIFIC (t)->s->elts; qsort (field_array, len, sizeof (tree), field_decl_cmp); } } } for (x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x)) { TYPE_FIELDS (x) = TYPE_FIELDS (t); TYPE_LANG_SPECIFIC (x) = TYPE_LANG_SPECIFIC (t); TYPE_ALIGN (x) = TYPE_ALIGN (t); TYPE_USER_ALIGN (x) = TYPE_USER_ALIGN (t); + C_TYPE_FIELDS_READONLY (x) = C_TYPE_FIELDS_READONLY (t); + C_TYPE_FIELDS_VOLATILE (x) = C_TYPE_FIELDS_VOLATILE (t); + C_TYPE_VARIABLE_SIZE (x) = C_TYPE_VARIABLE_SIZE (t); } /* If this was supposed to be a transparent union, but we can't make it one, warn and turn off the flag. */ if (TREE_CODE (t) == UNION_TYPE && TYPE_TRANSPARENT_UNION (t) && (!TYPE_FIELDS (t) || TYPE_MODE (t) != DECL_MODE (TYPE_FIELDS (t)))) { TYPE_TRANSPARENT_UNION (t) = 0; warning ("union cannot be made transparent"); } /* If this structure or union completes the type of any previous variable declaration, lay it out and output its rtl. */ for (x = C_TYPE_INCOMPLETE_VARS (TYPE_MAIN_VARIANT (t)); x; x = TREE_CHAIN (x)) { tree decl = TREE_VALUE (x); if (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE) layout_array_type (TREE_TYPE (decl)); if (TREE_CODE (decl) != TYPE_DECL) { layout_decl (decl, 0); if (c_dialect_objc ()) objc_check_decl (decl); rest_of_decl_compilation (decl, NULL, toplevel, 0); if (! toplevel) expand_decl (decl); } } C_TYPE_INCOMPLETE_VARS (TYPE_MAIN_VARIANT (t)) = 0; /* Finish debugging output for this type. */ rest_of_type_compilation (t, toplevel); return t; } /* Lay out the type T, and its element type, and so on. */ static void layout_array_type (tree t) { if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE) layout_array_type (TREE_TYPE (t)); layout_type (t); } /* Begin compiling the definition of an enumeration type. NAME is its name (or null if anonymous). Returns the type object, as yet incomplete. Also records info about it so that build_enumerator may be used to declare the individual values as they are read. */ tree start_enum (tree name) { tree enumtype = 0; /* If this is the real definition for a previous forward reference, fill in the contents in the same object that used to be the forward reference. */ if (name != 0) enumtype = lookup_tag (ENUMERAL_TYPE, name, 1); if (enumtype == 0 || TREE_CODE (enumtype) != ENUMERAL_TYPE) { enumtype = make_node (ENUMERAL_TYPE); pushtag (name, enumtype); } + if (C_TYPE_BEING_DEFINED (enumtype)) + error ("nested redefinition of `enum %s'", IDENTIFIER_POINTER (name)); + C_TYPE_BEING_DEFINED (enumtype) = 1; if (TYPE_VALUES (enumtype) != 0) { /* This enum is a named one that has been declared already. */ error ("redeclaration of `enum %s'", IDENTIFIER_POINTER (name)); /* Completely replace its old definition. The old enumerators remain defined, however. */ TYPE_VALUES (enumtype) = 0; } enum_next_value = integer_zero_node; enum_overflow = 0; if (flag_short_enums) TYPE_PACKED (enumtype) = 1; return enumtype; } /* After processing and defining all the values of an enumeration type, install their decls in the enumeration type and finish it off. ENUMTYPE is the type object, VALUES a list of decl-value pairs, and ATTRIBUTES are the specified attributes. Returns ENUMTYPE. */ tree finish_enum (tree enumtype, tree values, tree attributes) { tree pair, tem; tree minnode = 0, maxnode = 0, enum_value_type; int precision, unsign; int toplevel = (global_scope == current_scope); if (in_parm_level_p ()) warning ("enum defined inside parms"); decl_attributes (&enumtype, attributes, (int) ATTR_FLAG_TYPE_IN_PLACE); /* Calculate the maximum value of any enumerator in this type. */ if (values == error_mark_node) minnode = maxnode = integer_zero_node; else { minnode = maxnode = TREE_VALUE (values); for (pair = TREE_CHAIN (values); pair; pair = TREE_CHAIN (pair)) { tree value = TREE_VALUE (pair); if (tree_int_cst_lt (maxnode, value)) maxnode = value; if (tree_int_cst_lt (value, minnode)) minnode = value; } } /* Construct the final type of this enumeration. It is the same as one of the integral types - the narrowest one that fits, except that normally we only go as narrow as int - and signed iff any of the values are negative. */ unsign = (tree_int_cst_sgn (minnode) >= 0); precision = MAX (min_precision (minnode, unsign), min_precision (maxnode, unsign)); if (TYPE_PACKED (enumtype) || precision > TYPE_PRECISION (integer_type_node)) { tree narrowest = c_common_type_for_size (precision, unsign); if (narrowest == 0) { warning ("enumeration values exceed range of largest integer"); narrowest = long_long_integer_type_node; } precision = TYPE_PRECISION (narrowest); } else precision = TYPE_PRECISION (integer_type_node); if (precision == TYPE_PRECISION (integer_type_node)) enum_value_type = c_common_type_for_size (precision, 0); else enum_value_type = enumtype; TYPE_MIN_VALUE (enumtype) = minnode; TYPE_MAX_VALUE (enumtype) = maxnode; TREE_UNSIGNED (enumtype) = unsign; TYPE_SIZE (enumtype) = 0; /* If the precision of the type was specific with an attribute and it was too small, give an error. Otherwise, use it. */ if (TYPE_PRECISION (enumtype)) { if (precision > TYPE_PRECISION (enumtype)) error ("specified mode too small for enumeral values"); } else TYPE_PRECISION (enumtype) = precision; layout_type (enumtype); if (values != error_mark_node) { /* Change the type of the enumerators to be the enum type. We need to do this irrespective of the size of the enum, for proper type checking. Replace the DECL_INITIALs of the enumerators, and the value slots of the list, with copies that have the enum type; they cannot be modified in place because they may be shared (e.g. integer_zero_node) Finally, change the purpose slots to point to the names of the decls. */ for (pair = values; pair; pair = TREE_CHAIN (pair)) { tree enu = TREE_PURPOSE (pair); TREE_TYPE (enu) = enumtype; /* The ISO C Standard mandates enumerators to have type int, even though the underlying type of an enum type is unspecified. Here we convert any enumerators that fit in an int to type int, to avoid promotions to unsigned types when comparing integers with enumerators that fit in the int range. When -pedantic is given, build_enumerator() would have already taken care of those that don't fit. */ if (int_fits_type_p (DECL_INITIAL (enu), enum_value_type)) DECL_INITIAL (enu) = convert (enum_value_type, DECL_INITIAL (enu)); else DECL_INITIAL (enu) = convert (enumtype, DECL_INITIAL (enu)); TREE_PURPOSE (pair) = DECL_NAME (enu); TREE_VALUE (pair) = DECL_INITIAL (enu); } TYPE_VALUES (enumtype) = values; } /* Fix up all variant types of this enum type. */ for (tem = TYPE_MAIN_VARIANT (enumtype); tem; tem = TYPE_NEXT_VARIANT (tem)) { if (tem == enumtype) continue; TYPE_VALUES (tem) = TYPE_VALUES (enumtype); TYPE_MIN_VALUE (tem) = TYPE_MIN_VALUE (enumtype); TYPE_MAX_VALUE (tem) = TYPE_MAX_VALUE (enumtype); TYPE_SIZE (tem) = TYPE_SIZE (enumtype); TYPE_SIZE_UNIT (tem) = TYPE_SIZE_UNIT (enumtype); TYPE_MODE (tem) = TYPE_MODE (enumtype); TYPE_PRECISION (tem) = TYPE_PRECISION (enumtype); TYPE_ALIGN (tem) = TYPE_ALIGN (enumtype); TYPE_USER_ALIGN (tem) = TYPE_USER_ALIGN (enumtype); TREE_UNSIGNED (tem) = TREE_UNSIGNED (enumtype); } /* Finish debugging output for this type. */ rest_of_type_compilation (enumtype, toplevel); return enumtype; } /* Build and install a CONST_DECL for one value of the current enumeration type (one that was begun with start_enum). Return a tree-list containing the CONST_DECL and its value. Assignment of sequential values by default is handled here. */ tree build_enumerator (tree name, tree value) { tree decl, type; /* Validate and default VALUE. */ /* Remove no-op casts from the value. */ if (value) STRIP_TYPE_NOPS (value); if (value != 0) { if (TREE_CODE (value) == INTEGER_CST) { value = default_conversion (value); constant_expression_warning (value); } else { error ("enumerator value for `%s' not integer constant", IDENTIFIER_POINTER (name)); value = 0; } } /* Default based on previous value. */ /* It should no longer be possible to have NON_LVALUE_EXPR in the default. */ if (value == 0) { value = enum_next_value; if (enum_overflow) error ("overflow in enumeration values"); } if (pedantic && ! int_fits_type_p (value, integer_type_node)) { pedwarn ("ISO C restricts enumerator values to range of `int'"); value = convert (integer_type_node, value); } /* Set basis for default for next value. */ enum_next_value = build_binary_op (PLUS_EXPR, value, integer_one_node, 0); enum_overflow = tree_int_cst_lt (enum_next_value, value); /* Now create a declaration for the enum value name. */ type = TREE_TYPE (value); type = c_common_type_for_size (MAX (TYPE_PRECISION (type), TYPE_PRECISION (integer_type_node)), (TYPE_PRECISION (type) >= TYPE_PRECISION (integer_type_node) && TREE_UNSIGNED (type))); decl = build_decl (CONST_DECL, name, type); DECL_INITIAL (decl) = convert (type, value); pushdecl (decl); return tree_cons (decl, value, NULL_TREE); } /* Create the FUNCTION_DECL for a function definition. DECLSPECS, DECLARATOR and ATTRIBUTES are the parts of the declaration; they describe the function's name and the type it returns, but twisted together in a fashion that parallels the syntax of C. This function creates a binding context for the function body as well as setting up the FUNCTION_DECL in current_function_decl. Returns 1 on success. If the DECLARATOR is not suitable for a function (it defines a datum instead), we return 0, which tells yyparse to report a parse error. */ int start_function (tree declspecs, tree declarator, tree attributes) { tree decl1, old_decl; tree restype; int old_immediate_size_expand = immediate_size_expand; current_function_returns_value = 0; /* Assume, until we see it does. */ current_function_returns_null = 0; current_function_returns_abnormally = 0; warn_about_return_type = 0; current_extern_inline = 0; c_in_iteration_stmt = 0; c_in_case_stmt = 0; /* Don't expand any sizes in the return type of the function. */ immediate_size_expand = 0; decl1 = grokdeclarator (declarator, declspecs, FUNCDEF, 1, NULL); /* If the declarator is not suitable for a function definition, cause a syntax error. */ if (decl1 == 0) { immediate_size_expand = old_immediate_size_expand; return 0; } decl_attributes (&decl1, attributes, 0); if (DECL_DECLARED_INLINE_P (decl1) && DECL_UNINLINABLE (decl1) && lookup_attribute ("noinline", DECL_ATTRIBUTES (decl1))) warning ("%Jinline function '%D' given attribute noinline", decl1, decl1); announce_function (decl1); if (!COMPLETE_OR_VOID_TYPE_P (TREE_TYPE (TREE_TYPE (decl1)))) { error ("return type is an incomplete type"); /* Make it return void instead. */ TREE_TYPE (decl1) = build_function_type (void_type_node, TYPE_ARG_TYPES (TREE_TYPE (decl1))); } if (warn_about_return_type) pedwarn_c99 ("return type defaults to `int'"); /* Save the parm names or decls from this function's declarator where store_parm_decls will find them. */ current_function_parms = last_function_parms; current_function_parm_tags = last_function_parm_tags; current_function_parm_others = last_function_parm_others; /* Make the init_value nonzero so pushdecl knows this is not tentative. error_mark_node is replaced below (in poplevel) with the BLOCK. */ DECL_INITIAL (decl1) = error_mark_node; /* If this definition isn't a prototype and we had a prototype declaration before, copy the arg type info from that prototype. But not if what we had before was a builtin function. */ old_decl = lookup_name_current_level (DECL_NAME (decl1)); if (old_decl != 0 && TREE_CODE (TREE_TYPE (old_decl)) == FUNCTION_TYPE && !DECL_BUILT_IN (old_decl) && (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (decl1))) == TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (old_decl)))) && TYPE_ARG_TYPES (TREE_TYPE (decl1)) == 0) { TREE_TYPE (decl1) = TREE_TYPE (old_decl); current_function_prototype_locus = DECL_SOURCE_LOCATION (old_decl); } /* Optionally warn of old-fashioned def with no previous prototype. */ if (warn_strict_prototypes && TYPE_ARG_TYPES (TREE_TYPE (decl1)) == 0 && C_DECL_ISNT_PROTOTYPE (old_decl)) warning ("function declaration isn't a prototype"); /* Optionally warn of any global def with no previous prototype. */ else if (warn_missing_prototypes && TREE_PUBLIC (decl1) && ! MAIN_NAME_P (DECL_NAME (decl1)) && C_DECL_ISNT_PROTOTYPE (old_decl)) warning ("%Jno previous prototype for '%D'", decl1, decl1); /* Optionally warn of any def with no previous prototype if the function has already been used. */ else if (warn_missing_prototypes && old_decl != 0 && TREE_USED (old_decl) && TYPE_ARG_TYPES (TREE_TYPE (old_decl)) == 0) warning ("%J'%D' was used with no prototype before its definition", decl1, decl1); /* Optionally warn of any global def with no previous declaration. */ else if (warn_missing_declarations && TREE_PUBLIC (decl1) && old_decl == 0 && ! MAIN_NAME_P (DECL_NAME (decl1))) warning ("%Jno previous declaration for '%D'", decl1, decl1); /* Optionally warn of any def with no previous declaration if the function has already been used. */ else if (warn_missing_declarations && old_decl != 0 && TREE_USED (old_decl) && C_DECL_IMPLICIT (old_decl)) warning ("%J`%D' was used with no declaration before its definition", decl1, decl1); /* This is a definition, not a reference. So normally clear DECL_EXTERNAL. However, `extern inline' acts like a declaration except for defining how to inline. So set DECL_EXTERNAL in that case. */ DECL_EXTERNAL (decl1) = current_extern_inline; /* This function exists in static storage. (This does not mean `static' in the C sense!) */ TREE_STATIC (decl1) = 1; /* A nested function is not global. */ if (current_function_decl != 0) TREE_PUBLIC (decl1) = 0; #ifdef ENABLE_CHECKING /* This is the earliest point at which we might know the assembler name of the function. Thus, if it's set before this, die horribly. */ if (DECL_ASSEMBLER_NAME_SET_P (decl1)) abort (); #endif /* If #pragma weak was used, mark the decl weak now. */ if (current_scope == global_scope) maybe_apply_pragma_weak (decl1); /* Warn for unlikely, improbable, or stupid declarations of `main'. */ if (warn_main > 0 && MAIN_NAME_P (DECL_NAME (decl1))) { tree args; int argct = 0; if (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (decl1))) != integer_type_node) pedwarn ("%Jreturn type of '%D' is not `int'", decl1, decl1); for (args = TYPE_ARG_TYPES (TREE_TYPE (decl1)); args; args = TREE_CHAIN (args)) { tree type = args ? TREE_VALUE (args) : 0; if (type == void_type_node) break; ++argct; switch (argct) { case 1: if (TYPE_MAIN_VARIANT (type) != integer_type_node) pedwarn ("%Jfirst argument of '%D' should be `int'", decl1, decl1); break; case 2: if (TREE_CODE (type) != POINTER_TYPE || TREE_CODE (TREE_TYPE (type)) != POINTER_TYPE || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (type))) != char_type_node)) pedwarn ("%Jsecond argument of '%D' should be 'char **'", decl1, decl1); break; case 3: if (TREE_CODE (type) != POINTER_TYPE || TREE_CODE (TREE_TYPE (type)) != POINTER_TYPE || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (type))) != char_type_node)) pedwarn ("%Jthird argument of '%D' should probably be " "'char **'", decl1, decl1); break; } } /* It is intentional that this message does not mention the third argument because it's only mentioned in an appendix of the standard. */ if (argct > 0 && (argct < 2 || argct > 3)) pedwarn ("%J'%D' takes only zero or two arguments", decl1, decl1); if (! TREE_PUBLIC (decl1)) pedwarn ("%J'%D' is normally a non-static function", decl1, decl1); } /* Record the decl so that the function name is defined. If we already have a decl for this name, and it is a FUNCTION_DECL, use the old decl. */ current_function_decl = pushdecl (decl1); pushlevel (0); declare_parm_level (); make_decl_rtl (current_function_decl, NULL); restype = TREE_TYPE (TREE_TYPE (current_function_decl)); /* Promote the value to int before returning it. */ if (c_promoting_integer_type_p (restype)) { /* It retains unsignedness if not really getting wider. */ if (TREE_UNSIGNED (restype) && (TYPE_PRECISION (restype) == TYPE_PRECISION (integer_type_node))) restype = unsigned_type_node; else restype = integer_type_node; } DECL_RESULT (current_function_decl) = build_decl (RESULT_DECL, NULL_TREE, restype); /* If this fcn was already referenced via a block-scope `extern' decl (or an implicit decl), propagate certain information about the usage. */ if (TREE_ADDRESSABLE (DECL_ASSEMBLER_NAME (current_function_decl))) TREE_ADDRESSABLE (current_function_decl) = 1; immediate_size_expand = old_immediate_size_expand; start_fname_decls (); return 1; } /* Subroutine of store_parm_decls which handles new-style function definitions (prototype format). The parms already have decls, so we need only record them as in effect and complain if any redundant old-style parm decls were written. */ static void store_parm_decls_newstyle (void) { tree decl, last; tree fndecl = current_function_decl; tree parms = current_function_parms; tree tags = current_function_parm_tags; tree others = current_function_parm_others; if (current_scope->parms || current_scope->names || current_scope->tags) { error ("%Jold-style parameter declarations in prototyped " "function definition", fndecl); /* Get rid of the old-style declarations. */ poplevel (0, 0, 0); pushlevel (0); } /* Now make all the parameter declarations visible in the function body. We can bypass most of the grunt work of pushdecl. */ for (last = 0, decl = parms; decl; last = decl, decl = TREE_CHAIN (decl)) { DECL_CONTEXT (decl) = current_function_decl; if (DECL_NAME (decl) == 0) error ("%Jparameter name omitted", decl); else { if (IDENTIFIER_SYMBOL_VALUE (DECL_NAME (decl))) current_scope->shadowed = tree_cons (DECL_NAME (decl), IDENTIFIER_SYMBOL_VALUE (DECL_NAME (decl)), current_scope->shadowed); IDENTIFIER_SYMBOL_VALUE (DECL_NAME (decl)) = decl; } } current_scope->parms = parms; current_scope->parms_last = last; /* Record the parameter list in the function declaration. */ DECL_ARGUMENTS (fndecl) = parms; /* Now make all the ancillary declarations visible, likewise. */ for (last = 0, decl = others; decl; last = decl, decl = TREE_CHAIN (decl)) { DECL_CONTEXT (decl) = current_function_decl; if (DECL_NAME (decl) && TYPE_MAIN_VARIANT (TREE_TYPE (decl)) != void_type_node) { if (IDENTIFIER_SYMBOL_VALUE (DECL_NAME (decl))) current_scope->shadowed = tree_cons (DECL_NAME (decl), IDENTIFIER_SYMBOL_VALUE (DECL_NAME (decl)), current_scope->shadowed); IDENTIFIER_SYMBOL_VALUE (DECL_NAME (decl)) = decl; } } current_scope->names = others; current_scope->names_last = last; /* And all the tag declarations. */ for (decl = tags; decl; decl = TREE_CHAIN (decl)) if (TREE_PURPOSE (decl)) { if (IDENTIFIER_TAG_VALUE (TREE_PURPOSE (decl))) current_scope->shadowed_tags = tree_cons (TREE_PURPOSE (decl), IDENTIFIER_SYMBOL_VALUE (TREE_PURPOSE (decl)), current_scope->shadowed_tags); IDENTIFIER_TAG_VALUE (TREE_PURPOSE (decl)) = TREE_VALUE (decl); } current_scope->tags = tags; } /* Subroutine of store_parm_decls which handles old-style function definitions (separate parameter list and declarations). */ static void store_parm_decls_oldstyle (void) { tree parm, decl, last; tree fndecl = current_function_decl; /* This is the identifier list from the function declarator. */ tree parmids = current_function_parms; /* We use DECL_WEAK as a flag to show which parameters have been seen already, since it is not used on PARM_DECL. */ #ifdef ENABLE_CHECKING for (parm = current_scope->parms; parm; parm = TREE_CHAIN (parm)) if (DECL_WEAK (parm)) abort (); #endif /* Match each formal parameter name with its declaration. Save each decl in the appropriate TREE_PURPOSE slot of the parmids chain. */ for (parm = parmids; parm; parm = TREE_CHAIN (parm)) { if (TREE_VALUE (parm) == 0) { error ("%Jparameter name missing from parameter list", fndecl); TREE_PURPOSE (parm) = 0; continue; } decl = IDENTIFIER_SYMBOL_VALUE (TREE_VALUE (parm)); if (decl && DECL_CONTEXT (decl) == fndecl) { /* If we got something other than a PARM_DECL it is an error. */ if (TREE_CODE (decl) != PARM_DECL) error ("%J\"%D\" declared as a non-parameter", decl, decl); /* If the declaration is already marked, we have a duplicate name. Complain and ignore the duplicate. */ else if (DECL_WEAK (decl)) { error ("%Jmultiple parameters named \"%D\"", decl, decl); TREE_PURPOSE (parm) = 0; continue; } /* If the declaration says "void", complain and turn it into an int. */ else if (VOID_TYPE_P (TREE_TYPE (decl))) { error ("%Jparameter \"%D\" declared void", decl, decl); TREE_TYPE (decl) = integer_type_node; DECL_ARG_TYPE (decl) = integer_type_node; layout_decl (decl, 0); } } /* If no declaration found, default to int. */ else { decl = build_decl (PARM_DECL, TREE_VALUE (parm), integer_type_node); DECL_ARG_TYPE (decl) = TREE_TYPE (decl); DECL_SOURCE_LOCATION (decl) = DECL_SOURCE_LOCATION (fndecl); pushdecl (decl); if (flag_isoc99) pedwarn ("%Jtype of \"%D\" defaults to \"int\"", decl, decl); else if (extra_warnings) warning ("%Jtype of \"%D\" defaults to \"int\"", decl, decl); } TREE_PURPOSE (parm) = decl; DECL_WEAK (decl) = 1; } /* Now examine the parms chain for incomplete declarations and declarations with no corresponding names. */ for (parm = current_scope->parms; parm; parm = TREE_CHAIN (parm)) { if (!COMPLETE_TYPE_P (TREE_TYPE (parm))) { error ("%Jparameter \"%D\" has incomplete type", parm, parm); TREE_TYPE (parm) = error_mark_node; } if (! DECL_WEAK (parm)) { error ("%Jdeclaration for parameter \"%D\" but no such parameter", parm, parm); /* Pretend the parameter was not missing. This gets us to a standard state and minimizes further error messages. */ parmids = chainon (parmids, tree_cons (parm, 0, 0)); } } /* Chain the declarations together in the order of the list of names. Store that chain in the function decl, replacing the list of names. Update the current scope to match. */ DECL_ARGUMENTS (fndecl) = 0; for (parm = parmids; parm; parm = TREE_CHAIN (parm)) if (TREE_PURPOSE (parm)) break; if (parm && TREE_PURPOSE (parm)) { last = TREE_PURPOSE (parm); DECL_ARGUMENTS (fndecl) = last; current_scope->parms = last; DECL_WEAK (last) = 0; for (parm = TREE_CHAIN (parm); parm; parm = TREE_CHAIN (parm)) if (TREE_PURPOSE (parm)) { TREE_CHAIN (last) = TREE_PURPOSE (parm); last = TREE_PURPOSE (parm); DECL_WEAK (last) = 0; } current_scope->parms_last = last; TREE_CHAIN (last) = 0; } /* If there was a previous prototype, set the DECL_ARG_TYPE of each argument according to the type previously specified, and report any mismatches. */ if (TYPE_ARG_TYPES (TREE_TYPE (fndecl))) { tree type; for (parm = DECL_ARGUMENTS (fndecl), type = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); parm || (type && (TYPE_MAIN_VARIANT (TREE_VALUE (type)) != void_type_node)); parm = TREE_CHAIN (parm), type = TREE_CHAIN (type)) { if (parm == 0 || type == 0 || TYPE_MAIN_VARIANT (TREE_VALUE (type)) == void_type_node) { error ("number of arguments doesn't match prototype"); error ("%Hprototype declaration", ¤t_function_prototype_locus); break; } /* Type for passing arg must be consistent with that declared for the arg. ISO C says we take the unqualified type for parameters declared with qualified type. */ if (! comptypes (TYPE_MAIN_VARIANT (DECL_ARG_TYPE (parm)), TYPE_MAIN_VARIANT (TREE_VALUE (type)), COMPARE_STRICT)) { if (TYPE_MAIN_VARIANT (TREE_TYPE (parm)) == TYPE_MAIN_VARIANT (TREE_VALUE (type))) { /* Adjust argument to match prototype. E.g. a previous `int foo(float);' prototype causes `int foo(x) float x; {...}' to be treated like `int foo(float x) {...}'. This is particularly useful for argument types like uid_t. */ DECL_ARG_TYPE (parm) = TREE_TYPE (parm); if (targetm.calls.promote_prototypes (TREE_TYPE (current_function_decl)) && INTEGRAL_TYPE_P (TREE_TYPE (parm)) && TYPE_PRECISION (TREE_TYPE (parm)) < TYPE_PRECISION (integer_type_node)) DECL_ARG_TYPE (parm) = integer_type_node; if (pedantic) { pedwarn ("promoted argument \"%D\" " "doesn't match prototype", parm); pedwarn ("%Hprototype declaration", ¤t_function_prototype_locus); } } else { error ("argument \"%D\" doesn't match prototype", parm); error ("%Hprototype declaration", ¤t_function_prototype_locus); } } } TYPE_ACTUAL_ARG_TYPES (TREE_TYPE (fndecl)) = 0; } /* Otherwise, create a prototype that would match. */ else { tree actual = 0, last = 0, type; for (parm = DECL_ARGUMENTS (fndecl); parm; parm = TREE_CHAIN (parm)) { type = tree_cons (NULL_TREE, DECL_ARG_TYPE (parm), NULL_TREE); if (last) TREE_CHAIN (last) = type; else actual = type; last = type; } type = tree_cons (NULL_TREE, void_type_node, NULL_TREE); if (last) TREE_CHAIN (last) = type; else actual = type; /* We are going to assign a new value for the TYPE_ACTUAL_ARG_TYPES of the type of this function, but we need to avoid having this affect the types of other similarly-typed functions, so we must first force the generation of an identical (but separate) type node for the relevant function type. The new node we create will be a variant of the main variant of the original function type. */ TREE_TYPE (fndecl) = build_type_copy (TREE_TYPE (fndecl)); TYPE_ACTUAL_ARG_TYPES (TREE_TYPE (fndecl)) = actual; } } /* Store the parameter declarations into the current function declaration. This is called after parsing the parameter declarations, before digesting the body of the function. For an old-style definition, construct a prototype out of the old-style parameter declarations and inject it into the function's type. */ void store_parm_decls (void) { tree fndecl = current_function_decl; - /* The function containing FNDECL, if any. */ - tree context = decl_function_context (fndecl); - /* True if this definition is written with a prototype. */ bool prototype = (current_function_parms && TREE_CODE (current_function_parms) != TREE_LIST); if (prototype) store_parm_decls_newstyle (); else store_parm_decls_oldstyle (); /* The next call to pushlevel will be a function body. */ next_is_function_body = true; /* Write a record describing this function definition to the prototypes file (if requested). */ gen_aux_info_record (fndecl, 1, 0, prototype); /* Initialize the RTL code for the function. */ allocate_struct_function (fndecl); /* Begin the statement tree for this function. */ begin_stmt_tree (&DECL_SAVED_TREE (fndecl)); - /* If this is a nested function, save away the sizes of any - variable-size types so that we can expand them when generating - RTL. */ - if (context) - { - tree t; - - DECL_LANG_SPECIFIC (fndecl)->pending_sizes - = nreverse (get_pending_sizes ()); - for (t = DECL_LANG_SPECIFIC (fndecl)->pending_sizes; - t; - t = TREE_CHAIN (t)) - SAVE_EXPR_CONTEXT (TREE_VALUE (t)) = context; - } + /* Save away the sizes of any variable-size types so that we can + expand them when generating RTL. */ + DECL_LANG_SPECIFIC (fndecl)->pending_sizes = get_pending_sizes (); /* This function is being processed in whole-function mode. */ cfun->x_whole_function_mode_p = 1; /* Even though we're inside a function body, we still don't want to call expand_expr to calculate the size of a variable-sized array. We haven't necessarily assigned RTL to all variables yet, so it's not safe to try to expand expressions involving them. */ immediate_size_expand = 0; cfun->x_dont_save_pending_sizes_p = 1; } /* Finish up a function declaration and compile that function all the way to assembler language output. The free the storage for the function definition. This is called after parsing the body of the function definition. */ void finish_function (void) { tree fndecl = current_function_decl; /* When a function declaration is totally empty, e.g. void foo(void) { } (the argument list is irrelevant) the compstmt rule will not bother calling pushlevel/poplevel, which means we get here with the scope stack out of sync. Detect this situation by noticing that current_scope is still as store_parm_decls left it, and do a dummy push/pop to get back to consistency. Note that the call to pushlevel does not actually push another scope - see there for details. */ if (current_scope->parm_flag && next_is_function_body) { pushlevel (0); poplevel (0, 0, 0); } if (TREE_CODE (fndecl) == FUNCTION_DECL && targetm.calls.promote_prototypes (TREE_TYPE (fndecl))) { tree args = DECL_ARGUMENTS (fndecl); for (; args; args = TREE_CHAIN (args)) { tree type = TREE_TYPE (args); if (INTEGRAL_TYPE_P (type) && TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node)) DECL_ARG_TYPE (args) = integer_type_node; } } if (DECL_INITIAL (fndecl) && DECL_INITIAL (fndecl) != error_mark_node) BLOCK_SUPERCONTEXT (DECL_INITIAL (fndecl)) = fndecl; /* Must mark the RESULT_DECL as being in this function. */ if (DECL_RESULT (fndecl) && DECL_RESULT (fndecl) != error_mark_node) DECL_CONTEXT (DECL_RESULT (fndecl)) = fndecl; if (MAIN_NAME_P (DECL_NAME (fndecl)) && flag_hosted) { if (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (fndecl))) != integer_type_node) { /* If warn_main is 1 (-Wmain) or 2 (-Wall), we have already warned. If warn_main is -1 (-Wno-main) we don't want to be warned. */ if (!warn_main) pedwarn ("%Jreturn type of '%D' is not `int'", fndecl, fndecl); } else { #ifdef DEFAULT_MAIN_RETURN /* Make it so that `main' always returns success by default. */ DEFAULT_MAIN_RETURN; #else if (flag_isoc99) c_expand_return (integer_zero_node); #endif } } finish_fname_decls (); /* Tie off the statement tree for this function. */ finish_stmt_tree (&DECL_SAVED_TREE (fndecl)); /* Complain if there's just no return statement. */ if (warn_return_type && TREE_CODE (TREE_TYPE (TREE_TYPE (fndecl))) != VOID_TYPE && !current_function_returns_value && !current_function_returns_null /* Don't complain if we abort. */ && !current_function_returns_abnormally /* Don't warn for main(). */ && !MAIN_NAME_P (DECL_NAME (fndecl)) /* Or if they didn't actually specify a return type. */ && !C_FUNCTION_IMPLICIT_INT (fndecl) /* Normally, with -Wreturn-type, flow will complain. Unless we're an inline function, as we might never be compiled separately. */ && DECL_INLINE (fndecl)) warning ("no return statement in function returning non-void"); /* With just -Wextra, complain only if function returns both with and without a value. */ if (extra_warnings && current_function_returns_value && current_function_returns_null) warning ("this function may return with or without a value"); /* We're leaving the context of this function, so zap cfun. It's still in DECL_SAVED_INSNS, and we'll restore it in tree_rest_of_compilation. */ cfun = NULL; /* ??? Objc emits functions after finalizing the compilation unit. This should be cleaned up later and this conditional removed. */ if (!cgraph_global_info_ready) cgraph_finalize_function (fndecl, false); else c_expand_body (fndecl); current_function_decl = NULL; } /* Generate the RTL for a deferred function FNDECL. */ void c_expand_deferred_function (tree fndecl) { /* DECL_INLINE or DECL_RESULT might got cleared after the inline function was deferred, e.g. in duplicate_decls. */ if (DECL_INLINE (fndecl) && DECL_RESULT (fndecl)) { if (flag_inline_trees) { timevar_push (TV_INTEGRATION); optimize_inline_calls (fndecl); timevar_pop (TV_INTEGRATION); } c_expand_body (fndecl); current_function_decl = NULL; } } /* Generate the RTL for the body of FNDECL. If NESTED_P is nonzero, then we are already in the process of generating RTL for another function. */ static void c_expand_body_1 (tree fndecl, int nested_p) { if (nested_p) - { - /* Make sure that we will evaluate variable-sized types involved - in our function's type. */ - expand_pending_sizes (DECL_LANG_SPECIFIC (fndecl)->pending_sizes); - - /* Squirrel away our current state. */ - push_function_context (); - } + /* Squirrel away our current state. */ + push_function_context (); + /* Make sure that we will evaluate variable-sized types involved + in our function's type. */ + put_pending_sizes (DECL_LANG_SPECIFIC (fndecl)->pending_sizes); tree_rest_of_compilation (fndecl, nested_p); if (nested_p) /* Return to the enclosing function. */ pop_function_context (); if (DECL_STATIC_CONSTRUCTOR (fndecl)) { if (targetm.have_ctors_dtors) (* targetm.asm_out.constructor) (XEXP (DECL_RTL (fndecl), 0), DEFAULT_INIT_PRIORITY); else static_ctors = tree_cons (NULL_TREE, fndecl, static_ctors); } if (DECL_STATIC_DESTRUCTOR (fndecl)) { if (targetm.have_ctors_dtors) (* targetm.asm_out.destructor) (XEXP (DECL_RTL (fndecl), 0), DEFAULT_INIT_PRIORITY); else static_dtors = tree_cons (NULL_TREE, fndecl, static_dtors); } } /* Like c_expand_body_1 but only for unnested functions. */ void c_expand_body (tree fndecl) { if (DECL_INITIAL (fndecl) && DECL_INITIAL (fndecl) != error_mark_node) c_expand_body_1 (fndecl, 0); } /* Check the declarations given in a for-loop for satisfying the C99 constraints. */ void check_for_loop_decls (void) { tree t; if (!flag_isoc99) { /* If we get here, declarations have been used in a for loop without the C99 for loop scope. This doesn't make much sense, so don't allow it. */ error ("'for' loop initial declaration used outside C99 mode"); return; } /* C99 subclause 6.8.5 paragraph 3: [#3] The declaration part of a for statement shall only declare identifiers for objects having storage class auto or register. It isn't clear whether, in this sentence, "identifiers" binds to "shall only declare" or to "objects" - that is, whether all identifiers declared must be identifiers for objects, or whether the restriction only applies to those that are. (A question on this in comp.std.c in November 2000 received no answer.) We implement the strictest interpretation, to avoid creating an extension which later causes problems. */ for (t = current_scope->tags; t; t = TREE_CHAIN (t)) { if (TREE_PURPOSE (t) != 0) { enum tree_code code = TREE_CODE (TREE_VALUE (t)); if (code == RECORD_TYPE) error ("'struct %s' declared in 'for' loop initial declaration", IDENTIFIER_POINTER (TREE_PURPOSE (t))); else if (code == UNION_TYPE) error ("'union %s' declared in 'for' loop initial declaration", IDENTIFIER_POINTER (TREE_PURPOSE (t))); else error ("'enum %s' declared in 'for' loop initial declaration", IDENTIFIER_POINTER (TREE_PURPOSE (t))); } } for (t = getdecls (); t; t = TREE_CHAIN (t)) { if (TREE_CODE (t) != VAR_DECL && DECL_NAME (t)) error ("%Jdeclaration of non-variable '%D' in 'for' loop " "initial declaration", t, t); else if (TREE_STATIC (t)) error ("%Jdeclaration of static variable '%D' in 'for' loop " "initial declaration", t, t); else if (DECL_EXTERNAL (t)) error ("%Jdeclaration of 'extern' variable '%D' in 'for' loop " "initial declaration", t, t); } } /* Save and reinitialize the variables used during compilation of a C function. */ void c_push_function_context (struct function *f) { struct language_function *p; p = ggc_alloc (sizeof (struct language_function)); f->language = p; p->base.x_stmt_tree = c_stmt_tree; p->base.x_scope_stmt_stack = c_scope_stmt_stack; p->x_in_iteration_stmt = c_in_iteration_stmt; p->x_in_case_stmt = c_in_case_stmt; p->returns_value = current_function_returns_value; p->returns_null = current_function_returns_null; p->returns_abnormally = current_function_returns_abnormally; p->warn_about_return_type = warn_about_return_type; p->extern_inline = current_extern_inline; } /* Restore the variables used during compilation of a C function. */ void c_pop_function_context (struct function *f) { struct language_function *p = f->language; if (DECL_SAVED_INSNS (current_function_decl) == 0 && DECL_SAVED_TREE (current_function_decl) == NULL_TREE) { /* Stop pointing to the local nodes about to be freed. */ /* But DECL_INITIAL must remain nonzero so we know this was an actual function definition. */ DECL_INITIAL (current_function_decl) = error_mark_node; DECL_ARGUMENTS (current_function_decl) = 0; } c_stmt_tree = p->base.x_stmt_tree; c_scope_stmt_stack = p->base.x_scope_stmt_stack; c_in_iteration_stmt = p->x_in_iteration_stmt; c_in_case_stmt = p->x_in_case_stmt; current_function_returns_value = p->returns_value; current_function_returns_null = p->returns_null; current_function_returns_abnormally = p->returns_abnormally; warn_about_return_type = p->warn_about_return_type; current_extern_inline = p->extern_inline; f->language = NULL; } /* Copy the DECL_LANG_SPECIFIC data associated with DECL. */ void c_dup_lang_specific_decl (tree decl) { struct lang_decl *ld; if (!DECL_LANG_SPECIFIC (decl)) return; ld = ggc_alloc (sizeof (struct lang_decl)); memcpy (ld, DECL_LANG_SPECIFIC (decl), sizeof (struct lang_decl)); DECL_LANG_SPECIFIC (decl) = ld; } /* The functions below are required for functionality of doing function at once processing in the C front end. Currently these functions are not called from anywhere in the C front end, but as these changes continue, that will change. */ /* Returns nonzero if the current statement is a full expression, i.e. temporaries created during that statement should be destroyed at the end of the statement. */ int stmts_are_full_exprs_p (void) { return 0; } /* Returns the stmt_tree (if any) to which statements are currently being added. If there is no active statement-tree, NULL is returned. */ stmt_tree current_stmt_tree (void) { return &c_stmt_tree; } /* Returns the stack of SCOPE_STMTs for the current function. */ tree * current_scope_stmt_stack (void) { return &c_scope_stmt_stack; } /* Nonzero if TYPE is an anonymous union or struct type. Always 0 in C. */ int anon_aggr_type_p (tree node ATTRIBUTE_UNUSED) { return 0; } /* Dummy function in place of callback used by C++. */ void extract_interface_info (void) { } /* Return a new COMPOUND_STMT, after adding it to the current statement tree. */ tree c_begin_compound_stmt (void) { tree stmt; /* Create the COMPOUND_STMT. */ - stmt = add_stmt (build_stmt (COMPOUND_STMT, NULL_TREE)); + stmt = add_stmt (build_stmt (COMPOUND_STMT, error_mark_node)); return stmt; } /* Expand T (a DECL_STMT) if it declares an entity not handled by the common code. */ void c_expand_decl_stmt (tree t) { tree decl = DECL_STMT_DECL (t); /* Expand nested functions. */ if (TREE_CODE (decl) == FUNCTION_DECL && DECL_CONTEXT (decl) == current_function_decl && DECL_SAVED_TREE (decl)) c_expand_body_1 (decl, 1); } /* Return the global value of T as a symbol. */ tree identifier_global_value (tree t) { tree decl = IDENTIFIER_SYMBOL_VALUE (t); if (decl == 0 || DECL_FILE_SCOPE_P (decl)) return decl; /* Shadowed by something else; find the true global value. */ for (decl = global_scope->names; decl; decl = TREE_CHAIN (decl)) if (DECL_NAME (decl) == t) return decl; /* Only local values for this decl. */ return 0; } /* Record a builtin type for C. If NAME is non-NULL, it is the name used; otherwise the name is found in ridpointers from RID_INDEX. */ void record_builtin_type (enum rid rid_index, const char *name, tree type) { tree id; if (name == 0) id = ridpointers[(int) rid_index]; else id = get_identifier (name); pushdecl (build_decl (TYPE_DECL, id, type)); } /* Build the void_list_node (void_type_node having been created). */ tree build_void_list_node (void) { tree t = build_tree_list (NULL_TREE, void_type_node); return t; } /* Return something to represent absolute declarators containing a *. TARGET is the absolute declarator that the * contains. TYPE_QUALS_ATTRS is a list of modifiers such as const or volatile to apply to the pointer type, represented as identifiers, possible mixed with attributes. We return an INDIRECT_REF whose "contents" are TARGET (inside a TREE_LIST, if attributes are present) and whose type is the modifier list. */ tree make_pointer_declarator (tree type_quals_attrs, tree target) { tree quals, attrs; tree itarget = target; split_specs_attrs (type_quals_attrs, &quals, &attrs); if (attrs != NULL_TREE) itarget = tree_cons (attrs, target, NULL_TREE); return build1 (INDIRECT_REF, quals, itarget); } /* A wrapper around lhd_set_decl_assembler_name that gives static variables their C names if they are at file scope and only one translation unit is being compiled, for backwards compatibility with certain bizarre assembler hacks (like crtstuff.c). */ void c_static_assembler_name (tree decl) { if (num_in_fnames == 1 && !TREE_PUBLIC (decl) && DECL_CONTEXT (decl) && TREE_CODE (DECL_CONTEXT (decl)) == TRANSLATION_UNIT_DECL) SET_DECL_ASSEMBLER_NAME (decl, DECL_NAME (decl)); else lhd_set_decl_assembler_name (decl); } /* Hash and equality functions for link_hash_table: key off DECL_ASSEMBLER_NAME. */ static hashval_t link_hash_hash (const void *x_p) { tree x = (tree)x_p; return (hashval_t) (long)DECL_ASSEMBLER_NAME (x); } static int link_hash_eq (const void *x1_p, const void *x2_p) { tree x1 = (tree)x1_p; tree x2 = (tree)x2_p; return DECL_ASSEMBLER_NAME (x1) == DECL_ASSEMBLER_NAME (x2); } /* Propagate information between definitions and uses between multiple translation units in TU_LIST based on linkage rules. */ void merge_translation_unit_decls (void) { const tree tu_list = current_file_decl; tree tu; tree decl; htab_t link_hash_table; tree block; /* Create the BLOCK that poplevel would have created, but don't actually call poplevel since that's expensive. */ block = make_node (BLOCK); BLOCK_VARS (block) = current_scope->names; TREE_USED (block) = 1; DECL_INITIAL (current_file_decl) = block; /* If only one translation unit seen, no copying necessary. */ if (TREE_CHAIN (tu_list) == NULL_TREE) return; link_hash_table = htab_create (1021, link_hash_hash, link_hash_eq, NULL); /* Enter any actual definitions into the hash table. */ for (tu = tu_list; tu; tu = TREE_CHAIN (tu)) for (decl = BLOCK_VARS (DECL_INITIAL (tu)); decl; decl = TREE_CHAIN (decl)) if (TREE_PUBLIC (decl) && ! DECL_EXTERNAL (decl)) { PTR *slot; slot = htab_find_slot (link_hash_table, decl, INSERT); /* If we've already got a definition, work out which one is the real one, put it into the hash table, and make the other one DECL_EXTERNAL. This is important to avoid putting out two definitions of the same symbol in the assembly output. */ if (*slot != NULL) { tree old_decl = (tree) *slot; /* If this is weak or common or whatever, suppress it in favor of the other definition. */ if (DECL_WEAK (decl)) DECL_EXTERNAL (decl) = 1; else if (DECL_WEAK (old_decl) && ! DECL_WEAK (decl)) DECL_EXTERNAL (old_decl) = 1; else if (DECL_COMMON (decl) || DECL_ONE_ONLY (decl)) DECL_EXTERNAL (decl) = 1; else if (DECL_COMMON (old_decl) || DECL_ONE_ONLY (old_decl)) DECL_EXTERNAL (old_decl) = 1; if (DECL_EXTERNAL (decl)) { DECL_INITIAL (decl) = NULL_TREE; DECL_COMMON (decl) = 0; DECL_ONE_ONLY (decl) = 0; DECL_WEAK (decl) = 0; } else if (DECL_EXTERNAL (old_decl)) { DECL_INITIAL (old_decl) = NULL_TREE; DECL_COMMON (old_decl) = 0; DECL_ONE_ONLY (old_decl) = 0; DECL_WEAK (old_decl) = 0; *slot = decl; } else { error ("%Jredefinition of global '%D'", decl, decl); error ("%J'%D' previously defined here", old_decl, old_decl); } } else *slot = decl; } /* Now insert the desired information from all the definitions into any plain declarations. */ for (tu = tu_list; tu; tu = TREE_CHAIN (tu)) for (decl = BLOCK_VARS (DECL_INITIAL (tu)); decl; decl = TREE_CHAIN (decl)) if (TREE_PUBLIC (decl) && DECL_EXTERNAL (decl)) { tree global_decl; global_decl = htab_find (link_hash_table, decl); if (! global_decl) continue; /* Print any appropriate error messages, and partially merge the decls. */ (void) duplicate_decls (decl, global_decl); } htab_delete (link_hash_table); } /* Perform final processing on file-scope data. */ void c_write_global_declarations(void) { tree link; for (link = current_file_decl; link; link = TREE_CHAIN (link)) { tree globals = BLOCK_VARS (DECL_INITIAL (link)); int len = list_length (globals); tree *vec = xmalloc (sizeof (tree) * len); int i; tree decl; /* Process the decls in the order they were written. */ for (i = 0, decl = globals; i < len; i++, decl = TREE_CHAIN (decl)) vec[i] = decl; wrapup_global_declarations (vec, len); check_global_declarations (vec, len); /* Clean up. */ free (vec); } } /* Reset the parser's state in preparation for a new file. */ void c_reset_state (void) { tree link; tree file_scope_decl; /* Pop the global scope. */ if (current_scope != global_scope) current_scope = global_scope; file_scope_decl = current_file_decl; DECL_INITIAL (file_scope_decl) = poplevel (1, 0, 0); BLOCK_SUPERCONTEXT (DECL_INITIAL (file_scope_decl)) = file_scope_decl; truly_local_externals = NULL_TREE; /* Start a new global binding level. */ pushlevel (0); global_scope = current_scope; current_file_decl = build_decl (TRANSLATION_UNIT_DECL, NULL, NULL); TREE_CHAIN (current_file_decl) = file_scope_decl; /* Reintroduce the builtin declarations. */ for (link = first_builtin_decl; link != TREE_CHAIN (last_builtin_decl); link = TREE_CHAIN (link)) pushdecl (copy_node (link)); } #include "gt-c-decl.h" diff --git a/contrib/gcc/config/alpha/alpha.c b/contrib/gcc/config/alpha/alpha.c index fbaeabe5049a..f9aef999f12b 100644 --- a/contrib/gcc/config/alpha/alpha.c +++ b/contrib/gcc/config/alpha/alpha.c @@ -1,10287 +1,10308 @@ /* Subroutines used for code generation on the DEC Alpha. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu) This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include "system.h" #include "coretypes.h" #include "tm.h" #include "rtl.h" #include "tree.h" #include "regs.h" #include "hard-reg-set.h" #include "real.h" #include "insn-config.h" #include "conditions.h" #include "output.h" #include "insn-attr.h" #include "flags.h" #include "recog.h" #include "expr.h" #include "optabs.h" #include "reload.h" #include "obstack.h" #include "except.h" #include "function.h" #include "toplev.h" #include "ggc.h" #include "integrate.h" #include "tm_p.h" #include "target.h" #include "target-def.h" #include "debug.h" #include "langhooks.h" #include #include "cfglayout.h" /* Specify which cpu to schedule for. */ enum processor_type alpha_cpu; static const char * const alpha_cpu_name[] = { "ev4", "ev5", "ev6" }; /* Specify how accurate floating-point traps need to be. */ enum alpha_trap_precision alpha_tp; /* Specify the floating-point rounding mode. */ enum alpha_fp_rounding_mode alpha_fprm; /* Specify which things cause traps. */ enum alpha_fp_trap_mode alpha_fptm; /* Specify bit size of immediate TLS offsets. */ int alpha_tls_size = 32; /* Strings decoded into the above options. */ const char *alpha_cpu_string; /* -mcpu= */ const char *alpha_tune_string; /* -mtune= */ const char *alpha_tp_string; /* -mtrap-precision=[p|s|i] */ const char *alpha_fprm_string; /* -mfp-rounding-mode=[n|m|c|d] */ const char *alpha_fptm_string; /* -mfp-trap-mode=[n|u|su|sui] */ const char *alpha_mlat_string; /* -mmemory-latency= */ const char *alpha_tls_size_string; /* -mtls-size=[16|32|64] */ /* Save information from a "cmpxx" operation until the branch or scc is emitted. */ struct alpha_compare alpha_compare; /* Nonzero if inside of a function, because the Alpha asm can't handle .files inside of functions. */ static int inside_function = FALSE; /* The number of cycles of latency we should assume on memory reads. */ int alpha_memory_latency = 3; /* Whether the function needs the GP. */ static int alpha_function_needs_gp; /* The alias set for prologue/epilogue register save/restore. */ static GTY(()) int alpha_sr_alias_set; /* The assembler name of the current function. */ static const char *alpha_fnname; /* The next explicit relocation sequence number. */ extern GTY(()) int alpha_next_sequence_number; int alpha_next_sequence_number = 1; /* The literal and gpdisp sequence numbers for this insn, as printed by %# and %* respectively. */ extern GTY(()) int alpha_this_literal_sequence_number; extern GTY(()) int alpha_this_gpdisp_sequence_number; int alpha_this_literal_sequence_number; int alpha_this_gpdisp_sequence_number; /* Costs of various operations on the different architectures. */ struct alpha_rtx_cost_data { unsigned char fp_add; unsigned char fp_mult; unsigned char fp_div_sf; unsigned char fp_div_df; unsigned char int_mult_si; unsigned char int_mult_di; unsigned char int_shift; unsigned char int_cmov; }; static struct alpha_rtx_cost_data const alpha_rtx_cost_data[PROCESSOR_MAX] = { { /* EV4 */ COSTS_N_INSNS (6), /* fp_add */ COSTS_N_INSNS (6), /* fp_mult */ COSTS_N_INSNS (34), /* fp_div_sf */ COSTS_N_INSNS (63), /* fp_div_df */ COSTS_N_INSNS (23), /* int_mult_si */ COSTS_N_INSNS (23), /* int_mult_di */ COSTS_N_INSNS (2), /* int_shift */ COSTS_N_INSNS (2), /* int_cmov */ }, { /* EV5 */ COSTS_N_INSNS (4), /* fp_add */ COSTS_N_INSNS (4), /* fp_mult */ COSTS_N_INSNS (15), /* fp_div_sf */ COSTS_N_INSNS (22), /* fp_div_df */ COSTS_N_INSNS (8), /* int_mult_si */ COSTS_N_INSNS (12), /* int_mult_di */ COSTS_N_INSNS (1) + 1, /* int_shift */ COSTS_N_INSNS (1), /* int_cmov */ }, { /* EV6 */ COSTS_N_INSNS (4), /* fp_add */ COSTS_N_INSNS (4), /* fp_mult */ COSTS_N_INSNS (12), /* fp_div_sf */ COSTS_N_INSNS (15), /* fp_div_df */ COSTS_N_INSNS (7), /* int_mult_si */ COSTS_N_INSNS (7), /* int_mult_di */ COSTS_N_INSNS (1), /* int_shift */ COSTS_N_INSNS (2), /* int_cmov */ }, }; /* Get the number of args of a function in one of two ways. */ #if TARGET_ABI_OPEN_VMS || TARGET_ABI_UNICOSMK #define NUM_ARGS current_function_args_info.num_args #else #define NUM_ARGS current_function_args_info #endif #define REG_PV 27 #define REG_RA 26 /* Declarations of static functions. */ static struct machine_function *alpha_init_machine_status (void); static rtx alpha_emit_xfloating_compare (enum rtx_code, rtx, rtx); #if TARGET_ABI_OPEN_VMS static void alpha_write_linkage (FILE *, const char *, tree); #endif static void unicosmk_output_deferred_case_vectors (FILE *); static void unicosmk_gen_dsib (unsigned long *); static void unicosmk_output_ssib (FILE *, const char *); static int unicosmk_need_dex (rtx); /* Parse target option strings. */ void override_options (void) { int i; static const struct cpu_table { const char *const name; const enum processor_type processor; const int flags; } cpu_table[] = { #define EV5_MASK (MASK_CPU_EV5) #define EV6_MASK (MASK_CPU_EV6|MASK_BWX|MASK_MAX|MASK_FIX) { "ev4", PROCESSOR_EV4, 0 }, { "ev45", PROCESSOR_EV4, 0 }, { "21064", PROCESSOR_EV4, 0 }, { "ev5", PROCESSOR_EV5, EV5_MASK }, { "21164", PROCESSOR_EV5, EV5_MASK }, { "ev56", PROCESSOR_EV5, EV5_MASK|MASK_BWX }, { "21164a", PROCESSOR_EV5, EV5_MASK|MASK_BWX }, { "pca56", PROCESSOR_EV5, EV5_MASK|MASK_BWX|MASK_MAX }, { "21164PC",PROCESSOR_EV5, EV5_MASK|MASK_BWX|MASK_MAX }, { "21164pc",PROCESSOR_EV5, EV5_MASK|MASK_BWX|MASK_MAX }, { "ev6", PROCESSOR_EV6, EV6_MASK }, { "21264", PROCESSOR_EV6, EV6_MASK }, { "ev67", PROCESSOR_EV6, EV6_MASK|MASK_CIX }, { "21264a", PROCESSOR_EV6, EV6_MASK|MASK_CIX }, { 0, 0, 0 } }; /* Unicos/Mk doesn't have shared libraries. */ if (TARGET_ABI_UNICOSMK && flag_pic) { warning ("-f%s ignored for Unicos/Mk (not supported)", (flag_pic > 1) ? "PIC" : "pic"); flag_pic = 0; } /* On Unicos/Mk, the native compiler consistently generates /d suffices for floating-point instructions. Make that the default for this target. */ if (TARGET_ABI_UNICOSMK) alpha_fprm = ALPHA_FPRM_DYN; else alpha_fprm = ALPHA_FPRM_NORM; alpha_tp = ALPHA_TP_PROG; alpha_fptm = ALPHA_FPTM_N; /* We cannot use su and sui qualifiers for conversion instructions on Unicos/Mk. I'm not sure if this is due to assembler or hardware limitations. Right now, we issue a warning if -mieee is specified and then ignore it; eventually, we should either get it right or disable the option altogether. */ if (TARGET_IEEE) { if (TARGET_ABI_UNICOSMK) warning ("-mieee not supported on Unicos/Mk"); else { alpha_tp = ALPHA_TP_INSN; alpha_fptm = ALPHA_FPTM_SU; } } if (TARGET_IEEE_WITH_INEXACT) { if (TARGET_ABI_UNICOSMK) warning ("-mieee-with-inexact not supported on Unicos/Mk"); else { alpha_tp = ALPHA_TP_INSN; alpha_fptm = ALPHA_FPTM_SUI; } } if (alpha_tp_string) { if (! strcmp (alpha_tp_string, "p")) alpha_tp = ALPHA_TP_PROG; else if (! strcmp (alpha_tp_string, "f")) alpha_tp = ALPHA_TP_FUNC; else if (! strcmp (alpha_tp_string, "i")) alpha_tp = ALPHA_TP_INSN; else error ("bad value `%s' for -mtrap-precision switch", alpha_tp_string); } if (alpha_fprm_string) { if (! strcmp (alpha_fprm_string, "n")) alpha_fprm = ALPHA_FPRM_NORM; else if (! strcmp (alpha_fprm_string, "m")) alpha_fprm = ALPHA_FPRM_MINF; else if (! strcmp (alpha_fprm_string, "c")) alpha_fprm = ALPHA_FPRM_CHOP; else if (! strcmp (alpha_fprm_string,"d")) alpha_fprm = ALPHA_FPRM_DYN; else error ("bad value `%s' for -mfp-rounding-mode switch", alpha_fprm_string); } if (alpha_fptm_string) { if (strcmp (alpha_fptm_string, "n") == 0) alpha_fptm = ALPHA_FPTM_N; else if (strcmp (alpha_fptm_string, "u") == 0) alpha_fptm = ALPHA_FPTM_U; else if (strcmp (alpha_fptm_string, "su") == 0) alpha_fptm = ALPHA_FPTM_SU; else if (strcmp (alpha_fptm_string, "sui") == 0) alpha_fptm = ALPHA_FPTM_SUI; else error ("bad value `%s' for -mfp-trap-mode switch", alpha_fptm_string); } if (alpha_tls_size_string) { if (strcmp (alpha_tls_size_string, "16") == 0) alpha_tls_size = 16; else if (strcmp (alpha_tls_size_string, "32") == 0) alpha_tls_size = 32; else if (strcmp (alpha_tls_size_string, "64") == 0) alpha_tls_size = 64; else error ("bad value `%s' for -mtls-size switch", alpha_tls_size_string); } alpha_cpu = TARGET_CPU_DEFAULT & MASK_CPU_EV6 ? PROCESSOR_EV6 : (TARGET_CPU_DEFAULT & MASK_CPU_EV5 ? PROCESSOR_EV5 : PROCESSOR_EV4); if (alpha_cpu_string) { for (i = 0; cpu_table [i].name; i++) if (! strcmp (alpha_cpu_string, cpu_table [i].name)) { alpha_cpu = cpu_table [i].processor; target_flags &= ~ (MASK_BWX | MASK_MAX | MASK_FIX | MASK_CIX | MASK_CPU_EV5 | MASK_CPU_EV6); target_flags |= cpu_table [i].flags; break; } if (! cpu_table [i].name) error ("bad value `%s' for -mcpu switch", alpha_cpu_string); } if (alpha_tune_string) { for (i = 0; cpu_table [i].name; i++) if (! strcmp (alpha_tune_string, cpu_table [i].name)) { alpha_cpu = cpu_table [i].processor; break; } if (! cpu_table [i].name) error ("bad value `%s' for -mcpu switch", alpha_tune_string); } /* Do some sanity checks on the above options. */ if (TARGET_ABI_UNICOSMK && alpha_fptm != ALPHA_FPTM_N) { warning ("trap mode not supported on Unicos/Mk"); alpha_fptm = ALPHA_FPTM_N; } if ((alpha_fptm == ALPHA_FPTM_SU || alpha_fptm == ALPHA_FPTM_SUI) && alpha_tp != ALPHA_TP_INSN && ! TARGET_CPU_EV6) { warning ("fp software completion requires -mtrap-precision=i"); alpha_tp = ALPHA_TP_INSN; } if (TARGET_CPU_EV6) { /* Except for EV6 pass 1 (not released), we always have precise arithmetic traps. Which means we can do software completion without minding trap shadows. */ alpha_tp = ALPHA_TP_PROG; } if (TARGET_FLOAT_VAX) { if (alpha_fprm == ALPHA_FPRM_MINF || alpha_fprm == ALPHA_FPRM_DYN) { warning ("rounding mode not supported for VAX floats"); alpha_fprm = ALPHA_FPRM_NORM; } if (alpha_fptm == ALPHA_FPTM_SUI) { warning ("trap mode not supported for VAX floats"); alpha_fptm = ALPHA_FPTM_SU; } if (target_flags_explicit & MASK_LONG_DOUBLE_128) warning ("128-bit long double not supported for VAX floats"); target_flags &= ~MASK_LONG_DOUBLE_128; } { char *end; int lat; if (!alpha_mlat_string) alpha_mlat_string = "L1"; if (ISDIGIT ((unsigned char)alpha_mlat_string[0]) && (lat = strtol (alpha_mlat_string, &end, 10), *end == '\0')) ; else if ((alpha_mlat_string[0] == 'L' || alpha_mlat_string[0] == 'l') && ISDIGIT ((unsigned char)alpha_mlat_string[1]) && alpha_mlat_string[2] == '\0') { static int const cache_latency[][4] = { { 3, 30, -1 }, /* ev4 -- Bcache is a guess */ { 2, 12, 38 }, /* ev5 -- Bcache from PC164 LMbench numbers */ { 3, 12, 30 }, /* ev6 -- Bcache from DS20 LMbench. */ }; lat = alpha_mlat_string[1] - '0'; if (lat <= 0 || lat > 3 || cache_latency[alpha_cpu][lat-1] == -1) { warning ("L%d cache latency unknown for %s", lat, alpha_cpu_name[alpha_cpu]); lat = 3; } else lat = cache_latency[alpha_cpu][lat-1]; } else if (! strcmp (alpha_mlat_string, "main")) { /* Most current memories have about 370ns latency. This is a reasonable guess for a fast cpu. */ lat = 150; } else { warning ("bad value `%s' for -mmemory-latency", alpha_mlat_string); lat = 3; } alpha_memory_latency = lat; } /* Default the definition of "small data" to 8 bytes. */ if (!g_switch_set) g_switch_value = 8; /* Infer TARGET_SMALL_DATA from -fpic/-fPIC. */ if (flag_pic == 1) target_flags |= MASK_SMALL_DATA; else if (flag_pic == 2) target_flags &= ~MASK_SMALL_DATA; /* Align labels and loops for optimal branching. */ /* ??? Kludge these by not doing anything if we don't optimize and also if we are writing ECOFF symbols to work around a bug in DEC's assembler. */ if (optimize > 0 && write_symbols != SDB_DEBUG) { if (align_loops <= 0) align_loops = 16; if (align_jumps <= 0) align_jumps = 16; } if (align_functions <= 0) align_functions = 16; /* Acquire a unique set number for our register saves and restores. */ alpha_sr_alias_set = new_alias_set (); /* Register variables and functions with the garbage collector. */ /* Set up function hooks. */ init_machine_status = alpha_init_machine_status; /* Tell the compiler when we're using VAX floating point. */ if (TARGET_FLOAT_VAX) { REAL_MODE_FORMAT (SFmode) = &vax_f_format; REAL_MODE_FORMAT (DFmode) = &vax_g_format; REAL_MODE_FORMAT (TFmode) = NULL; } } /* Returns 1 if VALUE is a mask that contains full bytes of zero or ones. */ int zap_mask (HOST_WIDE_INT value) { int i; for (i = 0; i < HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR; i++, value >>= 8) if ((value & 0xff) != 0 && (value & 0xff) != 0xff) return 0; return 1; } /* Returns 1 if OP is either the constant zero or a register. If a register, it must be in the proper mode unless MODE is VOIDmode. */ int reg_or_0_operand (rtx op, enum machine_mode mode) { return op == CONST0_RTX (mode) || register_operand (op, mode); } /* Return 1 if OP is a constant in the range of 0-63 (for a shift) or any register. */ int reg_or_6bit_operand (rtx op, enum machine_mode mode) { return ((GET_CODE (op) == CONST_INT && (unsigned HOST_WIDE_INT) INTVAL (op) < 64) || register_operand (op, mode)); } /* Return 1 if OP is an 8-bit constant or any register. */ int reg_or_8bit_operand (rtx op, enum machine_mode mode) { return ((GET_CODE (op) == CONST_INT && (unsigned HOST_WIDE_INT) INTVAL (op) < 0x100) || register_operand (op, mode)); } /* Return 1 if OP is a constant or any register. */ int reg_or_const_int_operand (rtx op, enum machine_mode mode) { return GET_CODE (op) == CONST_INT || register_operand (op, mode); } /* Return 1 if OP is an 8-bit constant. */ int cint8_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return ((GET_CODE (op) == CONST_INT && (unsigned HOST_WIDE_INT) INTVAL (op) < 0x100)); } /* Return 1 if the operand is a valid second operand to an add insn. */ int add_operand (rtx op, enum machine_mode mode) { if (GET_CODE (op) == CONST_INT) /* Constraints I, J, O and P are covered by K. */ return (CONST_OK_FOR_LETTER_P (INTVAL (op), 'K') || CONST_OK_FOR_LETTER_P (INTVAL (op), 'L')); return register_operand (op, mode); } /* Return 1 if the operand is a valid second operand to a sign-extending add insn. */ int sext_add_operand (rtx op, enum machine_mode mode) { if (GET_CODE (op) == CONST_INT) return (CONST_OK_FOR_LETTER_P (INTVAL (op), 'I') || CONST_OK_FOR_LETTER_P (INTVAL (op), 'O')); return reg_not_elim_operand (op, mode); } /* Return 1 if OP is the constant 4 or 8. */ int const48_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return (GET_CODE (op) == CONST_INT && (INTVAL (op) == 4 || INTVAL (op) == 8)); } /* Return 1 if OP is a valid first operand to an AND insn. */ int and_operand (rtx op, enum machine_mode mode) { if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == VOIDmode) return (zap_mask (CONST_DOUBLE_LOW (op)) && zap_mask (CONST_DOUBLE_HIGH (op))); if (GET_CODE (op) == CONST_INT) return ((unsigned HOST_WIDE_INT) INTVAL (op) < 0x100 || (unsigned HOST_WIDE_INT) ~ INTVAL (op) < 0x100 || zap_mask (INTVAL (op))); return register_operand (op, mode); } /* Return 1 if OP is a valid first operand to an IOR or XOR insn. */ int or_operand (rtx op, enum machine_mode mode) { if (GET_CODE (op) == CONST_INT) return ((unsigned HOST_WIDE_INT) INTVAL (op) < 0x100 || (unsigned HOST_WIDE_INT) ~ INTVAL (op) < 0x100); return register_operand (op, mode); } /* Return 1 if OP is a constant that is the width, in bits, of an integral mode smaller than DImode. */ int mode_width_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return (GET_CODE (op) == CONST_INT && (INTVAL (op) == 8 || INTVAL (op) == 16 || INTVAL (op) == 32 || INTVAL (op) == 64)); } /* Return 1 if OP is a constant that is the width of an integral machine mode smaller than an integer. */ int mode_mask_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { if (GET_CODE (op) == CONST_INT) { HOST_WIDE_INT value = INTVAL (op); if (value == 0xff) return 1; if (value == 0xffff) return 1; if (value == 0xffffffff) return 1; if (value == -1) return 1; } else if (HOST_BITS_PER_WIDE_INT == 32 && GET_CODE (op) == CONST_DOUBLE) { if (CONST_DOUBLE_LOW (op) == 0xffffffff && CONST_DOUBLE_HIGH (op) == 0) return 1; } return 0; } /* Return 1 if OP is a multiple of 8 less than 64. */ int mul8_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return (GET_CODE (op) == CONST_INT && (unsigned HOST_WIDE_INT) INTVAL (op) < 64 && (INTVAL (op) & 7) == 0); } /* Return 1 if OP is the zero constant for MODE. */ int const0_operand (rtx op, enum machine_mode mode) { return op == CONST0_RTX (mode); } /* Return 1 if OP is a hard floating-point register. */ int hard_fp_register_operand (rtx op, enum machine_mode mode) { if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op)) return 0; if (GET_CODE (op) == SUBREG) op = SUBREG_REG (op); return GET_CODE (op) == REG && REGNO_REG_CLASS (REGNO (op)) == FLOAT_REGS; } /* Return 1 if OP is a hard general register. */ int hard_int_register_operand (rtx op, enum machine_mode mode) { if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op)) return 0; if (GET_CODE (op) == SUBREG) op = SUBREG_REG (op); return GET_CODE (op) == REG && REGNO_REG_CLASS (REGNO (op)) == GENERAL_REGS; } /* Return 1 if OP is a register or a constant integer. */ int reg_or_cint_operand (rtx op, enum machine_mode mode) { return (GET_CODE (op) == CONST_INT || register_operand (op, mode)); } /* Return 1 if OP is something that can be reloaded into a register; if it is a MEM, it need not be valid. */ int some_operand (rtx op, enum machine_mode mode) { if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op)) return 0; switch (GET_CODE (op)) { case REG: case MEM: case CONST_INT: case CONST_DOUBLE: case CONST_VECTOR: case LABEL_REF: case SYMBOL_REF: case CONST: case HIGH: return 1; case SUBREG: return some_operand (SUBREG_REG (op), VOIDmode); default: break; } return 0; } /* Likewise, but don't accept constants. */ int some_ni_operand (rtx op, enum machine_mode mode) { if (GET_MODE (op) != mode && mode != VOIDmode) return 0; if (GET_CODE (op) == SUBREG) op = SUBREG_REG (op); return (GET_CODE (op) == REG || GET_CODE (op) == MEM); } /* Return 1 if OP is a valid operand for the source of a move insn. */ int input_operand (rtx op, enum machine_mode mode) { if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op)) return 0; if (GET_MODE_CLASS (mode) == MODE_FLOAT && GET_MODE (op) != mode) return 0; switch (GET_CODE (op)) { case LABEL_REF: case SYMBOL_REF: case CONST: if (TARGET_EXPLICIT_RELOCS) { /* We don't split symbolic operands into something unintelligable until after reload, but we do not wish non-small, non-global symbolic operands to be reconstructed from their high/lo_sum form. */ return (small_symbolic_operand (op, mode) || global_symbolic_operand (op, mode) || gotdtp_symbolic_operand (op, mode) || gottp_symbolic_operand (op, mode)); } /* This handles both the Windows/NT and OSF cases. */ return mode == ptr_mode || mode == DImode; case HIGH: return (TARGET_EXPLICIT_RELOCS && local_symbolic_operand (XEXP (op, 0), mode)); case REG: case ADDRESSOF: return 1; case SUBREG: if (register_operand (op, mode)) return 1; /* ... fall through ... */ case MEM: return ((TARGET_BWX || (mode != HImode && mode != QImode)) && general_operand (op, mode)); case CONST_DOUBLE: case CONST_VECTOR: return op == CONST0_RTX (mode); case CONST_INT: return mode == QImode || mode == HImode || add_operand (op, mode); case CONSTANT_P_RTX: return 1; default: break; } return 0; } /* Return 1 if OP is a SYMBOL_REF for a function known to be in this file, and in the same section as the current function. */ int samegp_function_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { if (GET_CODE (op) != SYMBOL_REF) return false; /* Easy test for recursion. */ if (op == XEXP (DECL_RTL (current_function_decl), 0)) return true; /* Functions that are not local can be overridden, and thus may not share the same gp. */ if (! SYMBOL_REF_LOCAL_P (op)) return false; /* If -msmall-data is in effect, assume that there is only one GP for the module, and so any local symbol has this property. We need explicit relocations to be able to enforce this for symbols not defined in this unit of translation, however. */ if (TARGET_EXPLICIT_RELOCS && TARGET_SMALL_DATA) return true; /* Functions that are not external are defined in this UoT, and thus must share the same gp. */ return ! SYMBOL_REF_EXTERNAL_P (op); } /* Return 1 if OP is a SYMBOL_REF for which we can make a call via bsr. */ int direct_call_operand (rtx op, enum machine_mode mode) { tree op_decl, cfun_sec, op_sec; /* Must share the same GP. */ if (!samegp_function_operand (op, mode)) return false; /* If profiling is implemented via linker tricks, we can't jump to the nogp alternate entry point. Note that current_function_profile would not be correct, since that doesn't indicate if the target function uses profiling. */ /* ??? TARGET_PROFILING_NEEDS_GP isn't really the right test, but is approximately correct for the OSF ABIs. Don't know what to do for VMS, NT, or UMK. */ if (!TARGET_PROFILING_NEEDS_GP && profile_flag) return false; /* Must be a function. In some cases folks create thunks in static data structures and then make calls to them. If we allow the direct call, we'll get an error from the linker about !samegp reloc against a symbol without a .prologue directive. */ if (!SYMBOL_REF_FUNCTION_P (op)) return false; /* Must be "near" so that the branch is assumed to reach. With -msmall-text, this is assumed true of all local symbols. Since we've already checked samegp, locality is already assured. */ if (TARGET_SMALL_TEXT) return true; /* Otherwise, a decl is "near" if it is defined in the same section. */ if (flag_function_sections) return false; op_decl = SYMBOL_REF_DECL (op); if (DECL_ONE_ONLY (current_function_decl) || (op_decl && DECL_ONE_ONLY (op_decl))) return false; cfun_sec = DECL_SECTION_NAME (current_function_decl); op_sec = op_decl ? DECL_SECTION_NAME (op_decl) : NULL; return ((!cfun_sec && !op_sec) || (cfun_sec && op_sec && strcmp (TREE_STRING_POINTER (cfun_sec), TREE_STRING_POINTER (op_sec)) == 0)); } /* Return true if OP is a LABEL_REF, or SYMBOL_REF or CONST referencing a (non-tls) variable known to be defined in this file. */ int local_symbolic_operand (rtx op, enum machine_mode mode) { if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op)) return 0; if (GET_CODE (op) == LABEL_REF) return 1; if (GET_CODE (op) == CONST && GET_CODE (XEXP (op, 0)) == PLUS && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT) op = XEXP (XEXP (op, 0), 0); if (GET_CODE (op) != SYMBOL_REF) return 0; return SYMBOL_REF_LOCAL_P (op) && !SYMBOL_REF_TLS_MODEL (op); } /* Return true if OP is a SYMBOL_REF or CONST referencing a variable known to be defined in this file in the small data area. */ int small_symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { if (! TARGET_SMALL_DATA) return 0; if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op)) return 0; if (GET_CODE (op) == CONST && GET_CODE (XEXP (op, 0)) == PLUS && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT) op = XEXP (XEXP (op, 0), 0); if (GET_CODE (op) != SYMBOL_REF) return 0; /* ??? There's no encode_section_info equivalent for the rtl constant pool, so SYMBOL_FLAG_SMALL never gets set. */ if (CONSTANT_POOL_ADDRESS_P (op)) return GET_MODE_SIZE (get_pool_mode (op)) <= g_switch_value; return (SYMBOL_REF_LOCAL_P (op) && SYMBOL_REF_SMALL_P (op) && SYMBOL_REF_TLS_MODEL (op) == 0); } /* Return true if OP is a SYMBOL_REF or CONST referencing a variable not known (or known not) to be defined in this file. */ int global_symbolic_operand (rtx op, enum machine_mode mode) { if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op)) return 0; if (GET_CODE (op) == CONST && GET_CODE (XEXP (op, 0)) == PLUS && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT) op = XEXP (XEXP (op, 0), 0); if (GET_CODE (op) != SYMBOL_REF) return 0; return !SYMBOL_REF_LOCAL_P (op) && !SYMBOL_REF_TLS_MODEL (op); } /* Return 1 if OP is a valid operand for the MEM of a CALL insn. */ int call_operand (rtx op, enum machine_mode mode) { if (mode != Pmode) return 0; if (GET_CODE (op) == REG) { if (TARGET_ABI_OSF) { /* Disallow virtual registers to cope with pathological test cases such as compile/930117-1.c in which the virtual reg decomposes to the frame pointer. Which is a hard reg that is not $27. */ return (REGNO (op) == 27 || REGNO (op) > LAST_VIRTUAL_REGISTER); } else return 1; } if (TARGET_ABI_UNICOSMK) return 0; if (GET_CODE (op) == SYMBOL_REF) return 1; return 0; } /* Returns 1 if OP is a symbolic operand, i.e. a symbol_ref or a label_ref, possibly with an offset. */ int symbolic_operand (rtx op, enum machine_mode mode) { if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op)) return 0; if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF) return 1; if (GET_CODE (op) == CONST && GET_CODE (XEXP (op,0)) == PLUS && GET_CODE (XEXP (XEXP (op,0), 0)) == SYMBOL_REF && GET_CODE (XEXP (XEXP (op,0), 1)) == CONST_INT) return 1; return 0; } /* Return true if OP is valid for a particular TLS relocation. */ static int tls_symbolic_operand_1 (rtx op, enum machine_mode mode, int size, int unspec) { if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op)) return 0; if (GET_CODE (op) != CONST) return 0; op = XEXP (op, 0); if (GET_CODE (op) != UNSPEC || XINT (op, 1) != unspec) return 0; op = XVECEXP (op, 0, 0); if (GET_CODE (op) != SYMBOL_REF) return 0; if (SYMBOL_REF_LOCAL_P (op)) { if (alpha_tls_size > size) return 0; } else { if (size != 64) return 0; } switch (SYMBOL_REF_TLS_MODEL (op)) { case TLS_MODEL_LOCAL_DYNAMIC: return unspec == UNSPEC_DTPREL; case TLS_MODEL_INITIAL_EXEC: return unspec == UNSPEC_TPREL && size == 64; case TLS_MODEL_LOCAL_EXEC: return unspec == UNSPEC_TPREL; default: abort (); } } /* Return true if OP is valid for 16-bit DTP relative relocations. */ int dtp16_symbolic_operand (rtx op, enum machine_mode mode) { return tls_symbolic_operand_1 (op, mode, 16, UNSPEC_DTPREL); } /* Return true if OP is valid for 32-bit DTP relative relocations. */ int dtp32_symbolic_operand (rtx op, enum machine_mode mode) { return tls_symbolic_operand_1 (op, mode, 32, UNSPEC_DTPREL); } /* Return true if OP is valid for 64-bit DTP relative relocations. */ int gotdtp_symbolic_operand (rtx op, enum machine_mode mode) { return tls_symbolic_operand_1 (op, mode, 64, UNSPEC_DTPREL); } /* Return true if OP is valid for 16-bit TP relative relocations. */ int tp16_symbolic_operand (rtx op, enum machine_mode mode) { return tls_symbolic_operand_1 (op, mode, 16, UNSPEC_TPREL); } /* Return true if OP is valid for 32-bit TP relative relocations. */ int tp32_symbolic_operand (rtx op, enum machine_mode mode) { return tls_symbolic_operand_1 (op, mode, 32, UNSPEC_TPREL); } /* Return true if OP is valid for 64-bit TP relative relocations. */ int gottp_symbolic_operand (rtx op, enum machine_mode mode) { return tls_symbolic_operand_1 (op, mode, 64, UNSPEC_TPREL); } /* Return 1 if OP is a valid Alpha comparison operator. Here we know which comparisons are valid in which insn. */ int alpha_comparison_operator (rtx op, enum machine_mode mode) { enum rtx_code code = GET_CODE (op); if (mode != GET_MODE (op) && mode != VOIDmode) return 0; return (code == EQ || code == LE || code == LT || code == LEU || code == LTU); } /* Return 1 if OP is a valid Alpha comparison operator against zero. Here we know which comparisons are valid in which insn. */ int alpha_zero_comparison_operator (rtx op, enum machine_mode mode) { enum rtx_code code = GET_CODE (op); if (mode != GET_MODE (op) && mode != VOIDmode) return 0; return (code == EQ || code == NE || code == LE || code == LT || code == LEU || code == LTU); } /* Return 1 if OP is a valid Alpha swapped comparison operator. */ int alpha_swapped_comparison_operator (rtx op, enum machine_mode mode) { enum rtx_code code = GET_CODE (op); if ((mode != GET_MODE (op) && mode != VOIDmode) || GET_RTX_CLASS (code) != '<') return 0; code = swap_condition (code); return (code == EQ || code == LE || code == LT || code == LEU || code == LTU); } /* Return 1 if OP is a signed comparison operation. */ int signed_comparison_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { enum rtx_code code = GET_CODE (op); if (mode != GET_MODE (op) && mode != VOIDmode) return 0; return (code == EQ || code == NE || code == LE || code == LT || code == GE || code == GT); } /* Return 1 if OP is a valid Alpha floating point comparison operator. Here we know which comparisons are valid in which insn. */ int alpha_fp_comparison_operator (rtx op, enum machine_mode mode) { enum rtx_code code = GET_CODE (op); if (mode != GET_MODE (op) && mode != VOIDmode) return 0; return (code == EQ || code == LE || code == LT || code == UNORDERED); } /* Return 1 if this is a divide or modulus operator. */ int divmod_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { enum rtx_code code = GET_CODE (op); return (code == DIV || code == MOD || code == UDIV || code == UMOD); } /* Return 1 if this is a float->int conversion operator. */ int fix_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { enum rtx_code code = GET_CODE (op); return (code == FIX || code == UNSIGNED_FIX); } /* Return 1 if this memory address is a known aligned register plus a constant. It must be a valid address. This means that we can do this as an aligned reference plus some offset. Take into account what reload will do. */ int aligned_memory_operand (rtx op, enum machine_mode mode) { rtx base; if (reload_in_progress) { rtx tmp = op; if (GET_CODE (tmp) == SUBREG) tmp = SUBREG_REG (tmp); if (GET_CODE (tmp) == REG && REGNO (tmp) >= FIRST_PSEUDO_REGISTER) { op = reg_equiv_memory_loc[REGNO (tmp)]; if (op == 0) return 0; } } if (GET_CODE (op) != MEM) return 0; if (MEM_ALIGN (op) >= 32) return 1; op = XEXP (op, 0); /* LEGITIMIZE_RELOAD_ADDRESS creates (plus (plus reg const_hi) const_lo) sorts of constructs. Dig for the real base register. */ if (reload_in_progress && GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 0)) == PLUS) base = XEXP (XEXP (op, 0), 0); else { if (! memory_address_p (mode, op)) return 0; base = (GET_CODE (op) == PLUS ? XEXP (op, 0) : op); } return (GET_CODE (base) == REG && REGNO_POINTER_ALIGN (REGNO (base)) >= 32); } /* Similar, but return 1 if OP is a MEM which is not alignable. */ int unaligned_memory_operand (rtx op, enum machine_mode mode) { rtx base; if (reload_in_progress) { rtx tmp = op; if (GET_CODE (tmp) == SUBREG) tmp = SUBREG_REG (tmp); if (GET_CODE (tmp) == REG && REGNO (tmp) >= FIRST_PSEUDO_REGISTER) { op = reg_equiv_memory_loc[REGNO (tmp)]; if (op == 0) return 0; } } if (GET_CODE (op) != MEM) return 0; if (MEM_ALIGN (op) >= 32) return 0; op = XEXP (op, 0); /* LEGITIMIZE_RELOAD_ADDRESS creates (plus (plus reg const_hi) const_lo) sorts of constructs. Dig for the real base register. */ if (reload_in_progress && GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 0)) == PLUS) base = XEXP (XEXP (op, 0), 0); else { if (! memory_address_p (mode, op)) return 0; base = (GET_CODE (op) == PLUS ? XEXP (op, 0) : op); } return (GET_CODE (base) == REG && REGNO_POINTER_ALIGN (REGNO (base)) < 32); } /* Return 1 if OP is either a register or an unaligned memory location. */ int reg_or_unaligned_mem_operand (rtx op, enum machine_mode mode) { return register_operand (op, mode) || unaligned_memory_operand (op, mode); } /* Return 1 if OP is any memory location. During reload a pseudo matches. */ int any_memory_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return (GET_CODE (op) == MEM || (GET_CODE (op) == SUBREG && GET_CODE (SUBREG_REG (op)) == REG) || (reload_in_progress && GET_CODE (op) == REG && REGNO (op) >= FIRST_PSEUDO_REGISTER) || (reload_in_progress && GET_CODE (op) == SUBREG && GET_CODE (SUBREG_REG (op)) == REG && REGNO (SUBREG_REG (op)) >= FIRST_PSEUDO_REGISTER)); } /* Returns 1 if OP is not an eliminable register. This exists to cure a pathological abort in the s8addq (et al) patterns, long foo () { long t; bar(); return (long) &t * 26107; } which run afoul of a hack in reload to cure a (presumably) similar problem with lea-type instructions on other targets. But there is one of us and many of them, so work around the problem by selectively preventing combine from making the optimization. */ int reg_not_elim_operand (rtx op, enum machine_mode mode) { rtx inner = op; if (GET_CODE (op) == SUBREG) inner = SUBREG_REG (op); if (inner == frame_pointer_rtx || inner == arg_pointer_rtx) return 0; return register_operand (op, mode); } /* Return 1 is OP is a memory location that is not a reference (using an AND) to an unaligned location. Take into account what reload will do. */ int normal_memory_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { if (reload_in_progress) { rtx tmp = op; if (GET_CODE (tmp) == SUBREG) tmp = SUBREG_REG (tmp); if (GET_CODE (tmp) == REG && REGNO (tmp) >= FIRST_PSEUDO_REGISTER) { op = reg_equiv_memory_loc[REGNO (tmp)]; /* This may not have been assigned an equivalent address if it will be eliminated. In that case, it doesn't matter what we do. */ if (op == 0) return 1; } } return GET_CODE (op) == MEM && GET_CODE (XEXP (op, 0)) != AND; } /* Accept a register, but not a subreg of any kind. This allows us to avoid pathological cases in reload wrt data movement common in int->fp conversion. */ int reg_no_subreg_operand (rtx op, enum machine_mode mode) { if (GET_CODE (op) != REG) return 0; return register_operand (op, mode); } /* Recognize an addition operation that includes a constant. Used to convince reload to canonize (plus (plus reg c1) c2) during register elimination. */ int addition_operation (rtx op, enum machine_mode mode) { if (GET_MODE (op) != mode && mode != VOIDmode) return 0; if (GET_CODE (op) == PLUS && register_operand (XEXP (op, 0), mode) && GET_CODE (XEXP (op, 1)) == CONST_INT && CONST_OK_FOR_LETTER_P (INTVAL (XEXP (op, 1)), 'K')) return 1; return 0; } /* Implements CONST_OK_FOR_LETTER_P. Return true if the value matches the range defined for C in [I-P]. */ bool alpha_const_ok_for_letter_p (HOST_WIDE_INT value, int c) { switch (c) { case 'I': /* An unsigned 8 bit constant. */ return (unsigned HOST_WIDE_INT) value < 0x100; case 'J': /* The constant zero. */ return value == 0; case 'K': /* A signed 16 bit constant. */ return (unsigned HOST_WIDE_INT) (value + 0x8000) < 0x10000; case 'L': /* A shifted signed 16 bit constant appropriate for LDAH. */ return ((value & 0xffff) == 0 && ((value) >> 31 == -1 || value >> 31 == 0)); case 'M': /* A constant that can be AND'ed with using a ZAP insn. */ return zap_mask (value); case 'N': /* A complemented unsigned 8 bit constant. */ return (unsigned HOST_WIDE_INT) (~ value) < 0x100; case 'O': /* A negated unsigned 8 bit constant. */ return (unsigned HOST_WIDE_INT) (- value) < 0x100; case 'P': /* The constant 1, 2 or 3. */ return value == 1 || value == 2 || value == 3; default: return false; } } /* Implements CONST_DOUBLE_OK_FOR_LETTER_P. Return true if VALUE matches for C in [GH]. */ bool alpha_const_double_ok_for_letter_p (rtx value, int c) { switch (c) { case 'G': /* The floating point zero constant. */ return (GET_MODE_CLASS (GET_MODE (value)) == MODE_FLOAT && value == CONST0_RTX (GET_MODE (value))); case 'H': /* A valid operand of a ZAP insn. */ return (GET_MODE (value) == VOIDmode && zap_mask (CONST_DOUBLE_LOW (value)) && zap_mask (CONST_DOUBLE_HIGH (value))); default: return false; } } /* Implements CONST_DOUBLE_OK_FOR_LETTER_P. Return true if VALUE matches for C. */ bool alpha_extra_constraint (rtx value, int c) { switch (c) { case 'Q': return normal_memory_operand (value, VOIDmode); case 'R': return direct_call_operand (value, Pmode); case 'S': return (GET_CODE (value) == CONST_INT && (unsigned HOST_WIDE_INT) INTVAL (value) < 64); case 'T': return GET_CODE (value) == HIGH; case 'U': return TARGET_ABI_UNICOSMK && symbolic_operand (value, VOIDmode); case 'W': return (GET_CODE (value) == CONST_VECTOR && value == CONST0_RTX (GET_MODE (value))); default: return false; } } /* Return 1 if this function can directly return via $26. */ int direct_return (void) { return (! TARGET_ABI_OPEN_VMS && ! TARGET_ABI_UNICOSMK && reload_completed && alpha_sa_size () == 0 && get_frame_size () == 0 && current_function_outgoing_args_size == 0 && current_function_pretend_args_size == 0); } /* Return the ADDR_VEC associated with a tablejump insn. */ rtx alpha_tablejump_addr_vec (rtx insn) { rtx tmp; tmp = JUMP_LABEL (insn); if (!tmp) return NULL_RTX; tmp = NEXT_INSN (tmp); if (!tmp) return NULL_RTX; if (GET_CODE (tmp) == JUMP_INSN && GET_CODE (PATTERN (tmp)) == ADDR_DIFF_VEC) return PATTERN (tmp); return NULL_RTX; } /* Return the label of the predicted edge, or CONST0_RTX if we don't know. */ rtx alpha_tablejump_best_label (rtx insn) { rtx jump_table = alpha_tablejump_addr_vec (insn); rtx best_label = NULL_RTX; /* ??? Once the CFG doesn't keep getting completely rebuilt, look there for edge frequency counts from profile data. */ if (jump_table) { int n_labels = XVECLEN (jump_table, 1); int best_count = -1; int i, j; for (i = 0; i < n_labels; i++) { int count = 1; for (j = i + 1; j < n_labels; j++) if (XEXP (XVECEXP (jump_table, 1, i), 0) == XEXP (XVECEXP (jump_table, 1, j), 0)) count++; if (count > best_count) best_count = count, best_label = XVECEXP (jump_table, 1, i); } } return best_label ? best_label : const0_rtx; } /* Return the TLS model to use for SYMBOL. */ static enum tls_model tls_symbolic_operand_type (rtx symbol) { enum tls_model model; if (GET_CODE (symbol) != SYMBOL_REF) return 0; model = SYMBOL_REF_TLS_MODEL (symbol); /* Local-exec with a 64-bit size is the same code as initial-exec. */ if (model == TLS_MODEL_LOCAL_EXEC && alpha_tls_size == 64) model = TLS_MODEL_INITIAL_EXEC; return model; } /* Return true if the function DECL will share the same GP as any function in the current unit of translation. */ static bool decl_has_samegp (tree decl) { /* Functions that are not local can be overridden, and thus may not share the same gp. */ if (!(*targetm.binds_local_p) (decl)) return false; /* If -msmall-data is in effect, assume that there is only one GP for the module, and so any local symbol has this property. We need explicit relocations to be able to enforce this for symbols not defined in this unit of translation, however. */ if (TARGET_EXPLICIT_RELOCS && TARGET_SMALL_DATA) return true; /* Functions that are not external are defined in this UoT. */ /* ??? Irritatingly, static functions not yet emitted are still marked "external". Apply this to non-static functions only. */ return !TREE_PUBLIC (decl) || !DECL_EXTERNAL (decl); } /* Return true if EXP should be placed in the small data section. */ static bool alpha_in_small_data_p (tree exp) { /* We want to merge strings, so we never consider them small data. */ if (TREE_CODE (exp) == STRING_CST) return false; /* Functions are never in the small data area. Duh. */ if (TREE_CODE (exp) == FUNCTION_DECL) return false; if (TREE_CODE (exp) == VAR_DECL && DECL_SECTION_NAME (exp)) { const char *section = TREE_STRING_POINTER (DECL_SECTION_NAME (exp)); if (strcmp (section, ".sdata") == 0 || strcmp (section, ".sbss") == 0) return true; } else { HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (exp)); /* If this is an incomplete type with size 0, then we can't put it in sdata because it might be too big when completed. */ if (size > 0 && (unsigned HOST_WIDE_INT) size <= g_switch_value) return true; } return false; } #if TARGET_ABI_OPEN_VMS static bool alpha_linkage_symbol_p (const char *symname) { int symlen = strlen (symname); if (symlen > 4) return strcmp (&symname [symlen - 4], "..lk") == 0; return false; } #define LINKAGE_SYMBOL_REF_P(X) \ ((GET_CODE (X) == SYMBOL_REF \ && alpha_linkage_symbol_p (XSTR (X, 0))) \ || (GET_CODE (X) == CONST \ && GET_CODE (XEXP (X, 0)) == PLUS \ && GET_CODE (XEXP (XEXP (X, 0), 0)) == SYMBOL_REF \ && alpha_linkage_symbol_p (XSTR (XEXP (XEXP (X, 0), 0), 0)))) #endif /* legitimate_address_p 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. For Alpha, we have either a constant address or the sum of a register and a constant address, or just a register. For DImode, any of those forms can be surrounded with an AND that clear the low-order three bits; this is an "unaligned" access. */ bool alpha_legitimate_address_p (enum machine_mode mode, rtx x, int strict) { /* If this is an ldq_u type address, discard the outer AND. */ if (mode == DImode && GET_CODE (x) == AND && GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) == -8) x = XEXP (x, 0); /* Discard non-paradoxical subregs. */ if (GET_CODE (x) == SUBREG && (GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))) x = SUBREG_REG (x); /* Unadorned general registers are valid. */ if (REG_P (x) && (strict ? STRICT_REG_OK_FOR_BASE_P (x) : NONSTRICT_REG_OK_FOR_BASE_P (x))) return true; /* Constant addresses (i.e. +/- 32k) are valid. */ if (CONSTANT_ADDRESS_P (x)) return true; #if TARGET_ABI_OPEN_VMS if (LINKAGE_SYMBOL_REF_P (x)) return true; #endif /* Register plus a small constant offset is valid. */ if (GET_CODE (x) == PLUS) { rtx ofs = XEXP (x, 1); x = XEXP (x, 0); /* Discard non-paradoxical subregs. */ if (GET_CODE (x) == SUBREG && (GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))) x = SUBREG_REG (x); if (REG_P (x)) { if (! strict && NONSTRICT_REG_OK_FP_BASE_P (x) && GET_CODE (ofs) == CONST_INT) return true; if ((strict ? STRICT_REG_OK_FOR_BASE_P (x) : NONSTRICT_REG_OK_FOR_BASE_P (x)) && CONSTANT_ADDRESS_P (ofs)) return true; } else if (GET_CODE (x) == ADDRESSOF && GET_CODE (ofs) == CONST_INT) return true; } /* If we're managing explicit relocations, LO_SUM is valid, as are small data symbols. */ else if (TARGET_EXPLICIT_RELOCS) { if (small_symbolic_operand (x, Pmode)) return true; if (GET_CODE (x) == LO_SUM) { rtx ofs = XEXP (x, 1); x = XEXP (x, 0); /* Discard non-paradoxical subregs. */ if (GET_CODE (x) == SUBREG && (GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))) x = SUBREG_REG (x); /* Must have a valid base register. */ if (! (REG_P (x) && (strict ? STRICT_REG_OK_FOR_BASE_P (x) : NONSTRICT_REG_OK_FOR_BASE_P (x)))) return false; /* The symbol must be local. */ if (local_symbolic_operand (ofs, Pmode) || dtp32_symbolic_operand (ofs, Pmode) || tp32_symbolic_operand (ofs, Pmode)) return true; } } return false; } /* Build the SYMBOL_REF for __tls_get_addr. */ static GTY(()) rtx tls_get_addr_libfunc; static rtx get_tls_get_addr (void) { if (!tls_get_addr_libfunc) tls_get_addr_libfunc = init_one_libfunc ("__tls_get_addr"); return tls_get_addr_libfunc; } /* Try machine-dependent ways of modifying an illegitimate address to be legitimate. If we find one, return the new, valid address. */ rtx alpha_legitimize_address (rtx x, rtx scratch, enum machine_mode mode ATTRIBUTE_UNUSED) { HOST_WIDE_INT addend; /* If the address is (plus reg const_int) and the CONST_INT is not a valid offset, compute the high part of the constant and add it to the register. Then our address is (plus temp low-part-const). */ if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == REG && GET_CODE (XEXP (x, 1)) == CONST_INT && ! CONSTANT_ADDRESS_P (XEXP (x, 1))) { addend = INTVAL (XEXP (x, 1)); x = XEXP (x, 0); goto split_addend; } /* If the address is (const (plus FOO const_int)), find the low-order part of the CONST_INT. Then load FOO plus any high-order part of the CONST_INT into a register. Our address is (plus reg low-part-const). This is done to reduce the number of GOT entries. */ if (!no_new_pseudos && GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT) { addend = INTVAL (XEXP (XEXP (x, 0), 1)); x = force_reg (Pmode, XEXP (XEXP (x, 0), 0)); goto split_addend; } /* If we have a (plus reg const), emit the load as in (2), then add the two registers, and finally generate (plus reg low-part-const) as our address. */ if (!no_new_pseudos && GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == REG && GET_CODE (XEXP (x, 1)) == CONST && GET_CODE (XEXP (XEXP (x, 1), 0)) == PLUS && GET_CODE (XEXP (XEXP (XEXP (x, 1), 0), 1)) == CONST_INT) { addend = INTVAL (XEXP (XEXP (XEXP (x, 1), 0), 1)); x = expand_simple_binop (Pmode, PLUS, XEXP (x, 0), XEXP (XEXP (XEXP (x, 1), 0), 0), NULL_RTX, 1, OPTAB_LIB_WIDEN); goto split_addend; } /* If this is a local symbol, split the address into HIGH/LO_SUM parts. */ if (TARGET_EXPLICIT_RELOCS && symbolic_operand (x, Pmode)) { rtx r0, r16, eqv, tga, tp, insn, dest, seq; switch (tls_symbolic_operand_type (x)) { case TLS_MODEL_GLOBAL_DYNAMIC: start_sequence (); r0 = gen_rtx_REG (Pmode, 0); r16 = gen_rtx_REG (Pmode, 16); tga = get_tls_get_addr (); dest = gen_reg_rtx (Pmode); seq = GEN_INT (alpha_next_sequence_number++); emit_insn (gen_movdi_er_tlsgd (r16, pic_offset_table_rtx, x, seq)); insn = gen_call_value_osf_tlsgd (r0, tga, seq); insn = emit_call_insn (insn); CONST_OR_PURE_CALL_P (insn) = 1; use_reg (&CALL_INSN_FUNCTION_USAGE (insn), r16); insn = get_insns (); end_sequence (); emit_libcall_block (insn, dest, r0, x); return dest; case TLS_MODEL_LOCAL_DYNAMIC: start_sequence (); r0 = gen_rtx_REG (Pmode, 0); r16 = gen_rtx_REG (Pmode, 16); tga = get_tls_get_addr (); scratch = gen_reg_rtx (Pmode); seq = GEN_INT (alpha_next_sequence_number++); emit_insn (gen_movdi_er_tlsldm (r16, pic_offset_table_rtx, seq)); insn = gen_call_value_osf_tlsldm (r0, tga, seq); insn = emit_call_insn (insn); CONST_OR_PURE_CALL_P (insn) = 1; use_reg (&CALL_INSN_FUNCTION_USAGE (insn), r16); insn = get_insns (); end_sequence (); eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TLSLDM_CALL); emit_libcall_block (insn, scratch, r0, eqv); eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), UNSPEC_DTPREL); eqv = gen_rtx_CONST (Pmode, eqv); if (alpha_tls_size == 64) { dest = gen_reg_rtx (Pmode); emit_insn (gen_rtx_SET (VOIDmode, dest, eqv)); emit_insn (gen_adddi3 (dest, dest, scratch)); return dest; } if (alpha_tls_size == 32) { insn = gen_rtx_HIGH (Pmode, eqv); insn = gen_rtx_PLUS (Pmode, scratch, insn); scratch = gen_reg_rtx (Pmode); emit_insn (gen_rtx_SET (VOIDmode, scratch, insn)); } return gen_rtx_LO_SUM (Pmode, scratch, eqv); case TLS_MODEL_INITIAL_EXEC: eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), UNSPEC_TPREL); eqv = gen_rtx_CONST (Pmode, eqv); tp = gen_reg_rtx (Pmode); scratch = gen_reg_rtx (Pmode); dest = gen_reg_rtx (Pmode); emit_insn (gen_load_tp (tp)); emit_insn (gen_rtx_SET (VOIDmode, scratch, eqv)); emit_insn (gen_adddi3 (dest, tp, scratch)); return dest; case TLS_MODEL_LOCAL_EXEC: eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), UNSPEC_TPREL); eqv = gen_rtx_CONST (Pmode, eqv); tp = gen_reg_rtx (Pmode); emit_insn (gen_load_tp (tp)); if (alpha_tls_size == 32) { insn = gen_rtx_HIGH (Pmode, eqv); insn = gen_rtx_PLUS (Pmode, tp, insn); tp = gen_reg_rtx (Pmode); emit_insn (gen_rtx_SET (VOIDmode, tp, insn)); } return gen_rtx_LO_SUM (Pmode, tp, eqv); } if (local_symbolic_operand (x, Pmode)) { if (small_symbolic_operand (x, Pmode)) return x; else { if (!no_new_pseudos) scratch = gen_reg_rtx (Pmode); emit_insn (gen_rtx_SET (VOIDmode, scratch, gen_rtx_HIGH (Pmode, x))); return gen_rtx_LO_SUM (Pmode, scratch, x); } } } return NULL; split_addend: { HOST_WIDE_INT low, high; low = ((addend & 0xffff) ^ 0x8000) - 0x8000; addend -= low; high = ((addend & 0xffffffff) ^ 0x80000000) - 0x80000000; addend -= high; if (addend) x = expand_simple_binop (Pmode, PLUS, x, GEN_INT (addend), (no_new_pseudos ? scratch : NULL_RTX), 1, OPTAB_LIB_WIDEN); if (high) x = expand_simple_binop (Pmode, PLUS, x, GEN_INT (high), (no_new_pseudos ? scratch : NULL_RTX), 1, OPTAB_LIB_WIDEN); return plus_constant (x, low); } } /* Primarily this is required for TLS symbols, but given that our move patterns *ought* to be able to handle any symbol at any time, we should never be spilling symbolic operands to the constant pool, ever. */ static bool alpha_cannot_force_const_mem (rtx x) { enum rtx_code code = GET_CODE (x); return code == SYMBOL_REF || code == LABEL_REF || code == CONST; } /* We do not allow indirect calls to be optimized into sibling calls, nor can we allow a call to a function with a different GP to be optimized into a sibcall. */ static bool alpha_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED) { /* Can't do indirect tail calls, since we don't know if the target uses the same GP. */ if (!decl) return false; /* Otherwise, we can make a tail call if the target function shares the same GP. */ return decl_has_samegp (decl); } /* For TARGET_EXPLICIT_RELOCS, we don't obfuscate a SYMBOL_REF to a small symbolic operand until after reload. At which point we need to replace (mem (symbol_ref)) with (mem (lo_sum $29 symbol_ref)) so that sched2 has the proper dependency information. */ static int some_small_symbolic_operand_1 (rtx *px, void *data ATTRIBUTE_UNUSED) { rtx x = *px; /* Don't re-split. */ if (GET_CODE (x) == LO_SUM) return -1; return small_symbolic_operand (x, Pmode) != 0; } int some_small_symbolic_operand (rtx x, enum machine_mode mode ATTRIBUTE_UNUSED) { return for_each_rtx (&x, some_small_symbolic_operand_1, NULL); } static int split_small_symbolic_operand_1 (rtx *px, void *data ATTRIBUTE_UNUSED) { rtx x = *px; /* Don't re-split. */ if (GET_CODE (x) == LO_SUM) return -1; if (small_symbolic_operand (x, Pmode)) { x = gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, x); *px = x; return -1; } return 0; } rtx split_small_symbolic_operand (rtx x) { x = copy_insn (x); for_each_rtx (&x, split_small_symbolic_operand_1, NULL); return x; } /* Indicate that INSN cannot be duplicated. This is true for any insn that we've marked with gpdisp relocs, since those have to stay in 1-1 correspondence with one another. Technically we could copy them if we could set up a mapping from one sequence number to another, across the set of insns to be duplicated. This seems overly complicated and error-prone since interblock motion from sched-ebb could move one of the pair of insns to a different block. Also cannot allow jsr insns to be duplicated. If they throw exceptions, then they'll be in a different block from their ldgp. Which could lead the bb reorder code to think that it would be ok to copy just the block containing the call and branch to the block containing the ldgp. */ static bool alpha_cannot_copy_insn_p (rtx insn) { if (!reload_completed || !TARGET_EXPLICIT_RELOCS) return false; if (recog_memoized (insn) >= 0) return get_attr_cannot_copy (insn); else return false; } /* Try a machine-dependent way of reloading an illegitimate address operand. If we find one, push the reload and return the new rtx. */ rtx alpha_legitimize_reload_address (rtx x, enum machine_mode mode ATTRIBUTE_UNUSED, int opnum, int type, int ind_levels ATTRIBUTE_UNUSED) { /* We must recognize output that we have already generated ourselves. */ if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == PLUS && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT && GET_CODE (XEXP (x, 1)) == CONST_INT) { push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL, BASE_REG_CLASS, GET_MODE (x), VOIDmode, 0, 0, opnum, type); return x; } /* We wish to handle large displacements off a base register by splitting the addend across an ldah and the mem insn. This cuts number of extra insns needed from 3 to 1. */ if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == REG && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER && REGNO_OK_FOR_BASE_P (REGNO (XEXP (x, 0))) && GET_CODE (XEXP (x, 1)) == CONST_INT) { HOST_WIDE_INT val = INTVAL (XEXP (x, 1)); HOST_WIDE_INT low = ((val & 0xffff) ^ 0x8000) - 0x8000; HOST_WIDE_INT high = (((val - low) & 0xffffffff) ^ 0x80000000) - 0x80000000; /* Check for 32-bit overflow. */ if (high + low != val) return NULL_RTX; /* Reload the high part into a base reg; leave the low part in the mem directly. */ x = gen_rtx_PLUS (GET_MODE (x), gen_rtx_PLUS (GET_MODE (x), XEXP (x, 0), GEN_INT (high)), GEN_INT (low)); push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL, BASE_REG_CLASS, GET_MODE (x), VOIDmode, 0, 0, opnum, type); return x; } return NULL_RTX; } /* Compute a (partial) cost for rtx X. Return true if the complete cost has been computed, and false if subexpressions should be scanned. In either case, *TOTAL contains the cost result. */ static bool alpha_rtx_costs (rtx x, int code, int outer_code, int *total) { enum machine_mode mode = GET_MODE (x); bool float_mode_p = FLOAT_MODE_P (mode); switch (code) { /* If this is an 8-bit constant, return zero since it can be used nearly anywhere with no cost. If it is a valid operand for an ADD or AND, likewise return 0 if we know it will be used in that context. Otherwise, return 2 since it might be used there later. All other constants take at least two insns. */ case CONST_INT: if (INTVAL (x) >= 0 && INTVAL (x) < 256) { *total = 0; return true; } /* FALLTHRU */ case CONST_DOUBLE: if (x == CONST0_RTX (mode)) *total = 0; else if ((outer_code == PLUS && add_operand (x, VOIDmode)) || (outer_code == AND && and_operand (x, VOIDmode))) *total = 0; else if (add_operand (x, VOIDmode) || and_operand (x, VOIDmode)) *total = 2; else *total = COSTS_N_INSNS (2); return true; case CONST: case SYMBOL_REF: case LABEL_REF: if (TARGET_EXPLICIT_RELOCS && small_symbolic_operand (x, VOIDmode)) *total = COSTS_N_INSNS (outer_code != MEM); else if (TARGET_EXPLICIT_RELOCS && local_symbolic_operand (x, VOIDmode)) *total = COSTS_N_INSNS (1 + (outer_code != MEM)); else if (tls_symbolic_operand_type (x)) /* Estimate of cost for call_pal rduniq. */ *total = COSTS_N_INSNS (15); else /* Otherwise we do a load from the GOT. */ *total = COSTS_N_INSNS (alpha_memory_latency); return true; case PLUS: case MINUS: if (float_mode_p) *total = alpha_rtx_cost_data[alpha_cpu].fp_add; else if (GET_CODE (XEXP (x, 0)) == MULT && const48_operand (XEXP (XEXP (x, 0), 1), VOIDmode)) { *total = (rtx_cost (XEXP (XEXP (x, 0), 0), outer_code) + rtx_cost (XEXP (x, 1), outer_code) + 2); return true; } return false; case MULT: if (float_mode_p) *total = alpha_rtx_cost_data[alpha_cpu].fp_mult; else if (mode == DImode) *total = alpha_rtx_cost_data[alpha_cpu].int_mult_di; else *total = alpha_rtx_cost_data[alpha_cpu].int_mult_si; return false; case ASHIFT: if (GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) <= 3) { *total = COSTS_N_INSNS (1); return false; } /* FALLTHRU */ case ASHIFTRT: case LSHIFTRT: *total = alpha_rtx_cost_data[alpha_cpu].int_shift; return false; case IF_THEN_ELSE: if (float_mode_p) *total = alpha_rtx_cost_data[alpha_cpu].fp_add; else *total = alpha_rtx_cost_data[alpha_cpu].int_cmov; return false; case DIV: case UDIV: case MOD: case UMOD: if (!float_mode_p) *total = COSTS_N_INSNS (70); /* ??? */ else if (mode == SFmode) *total = alpha_rtx_cost_data[alpha_cpu].fp_div_sf; else *total = alpha_rtx_cost_data[alpha_cpu].fp_div_df; return false; case MEM: *total = COSTS_N_INSNS (alpha_memory_latency); return true; case NEG: if (! float_mode_p) { *total = COSTS_N_INSNS (1); return false; } /* FALLTHRU */ case ABS: if (! float_mode_p) { *total = COSTS_N_INSNS (1) + alpha_rtx_cost_data[alpha_cpu].int_cmov; return false; } /* FALLTHRU */ case FLOAT: case UNSIGNED_FLOAT: case FIX: case UNSIGNED_FIX: case FLOAT_EXTEND: case FLOAT_TRUNCATE: *total = alpha_rtx_cost_data[alpha_cpu].fp_add; return false; default: return false; } } /* REF is an alignable memory location. Place an aligned SImode reference into *PALIGNED_MEM and the number of bits to shift into *PBITNUM. SCRATCH is a free register for use in reloading out of range stack slots. */ void get_aligned_mem (rtx ref, rtx *paligned_mem, rtx *pbitnum) { rtx base; HOST_WIDE_INT offset = 0; if (GET_CODE (ref) != MEM) abort (); if (reload_in_progress && ! memory_address_p (GET_MODE (ref), XEXP (ref, 0))) { base = find_replacement (&XEXP (ref, 0)); if (! memory_address_p (GET_MODE (ref), base)) abort (); } else { base = XEXP (ref, 0); } if (GET_CODE (base) == PLUS) offset += INTVAL (XEXP (base, 1)), base = XEXP (base, 0); *paligned_mem = widen_memory_access (ref, SImode, (offset & ~3) - offset); if (WORDS_BIG_ENDIAN) *pbitnum = GEN_INT (32 - (GET_MODE_BITSIZE (GET_MODE (ref)) + (offset & 3) * 8)); else *pbitnum = GEN_INT ((offset & 3) * 8); } /* Similar, but just get the address. Handle the two reload cases. Add EXTRA_OFFSET to the address we return. */ rtx get_unaligned_address (rtx ref, int extra_offset) { rtx base; HOST_WIDE_INT offset = 0; if (GET_CODE (ref) != MEM) abort (); if (reload_in_progress && ! memory_address_p (GET_MODE (ref), XEXP (ref, 0))) { base = find_replacement (&XEXP (ref, 0)); if (! memory_address_p (GET_MODE (ref), base)) abort (); } else { base = XEXP (ref, 0); } if (GET_CODE (base) == PLUS) offset += INTVAL (XEXP (base, 1)), base = XEXP (base, 0); return plus_constant (base, offset + extra_offset); } /* On the Alpha, all (non-symbolic) constants except zero go into a floating-point register via memory. Note that we cannot return anything that is not a subset of CLASS, and that some symbolic constants cannot be dropped to memory. */ enum reg_class alpha_preferred_reload_class(rtx x, enum reg_class class) { /* Zero is present in any register class. */ if (x == CONST0_RTX (GET_MODE (x))) return class; /* These sorts of constants we can easily drop to memory. */ if (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE) { if (class == FLOAT_REGS) return NO_REGS; if (class == ALL_REGS) return GENERAL_REGS; return class; } /* All other kinds of constants should not (and in the case of HIGH cannot) be dropped to memory -- instead we use a GENERAL_REGS secondary reload. */ if (CONSTANT_P (x)) return (class == ALL_REGS ? GENERAL_REGS : class); return class; } /* Loading and storing HImode or QImode values to and from memory usually requires a scratch register. The exceptions are loading QImode and HImode from an aligned address to a general register unless byte instructions are permitted. We also cannot load an unaligned address or a paradoxical SUBREG into an FP register. We also cannot do integral arithmetic into FP regs, as might result from register elimination into a DImode fp register. */ enum reg_class secondary_reload_class (enum reg_class class, enum machine_mode mode, rtx x, int in) { if ((mode == QImode || mode == HImode) && ! TARGET_BWX) { if (GET_CODE (x) == MEM || (GET_CODE (x) == REG && REGNO (x) >= FIRST_PSEUDO_REGISTER) || (GET_CODE (x) == SUBREG && (GET_CODE (SUBREG_REG (x)) == MEM || (GET_CODE (SUBREG_REG (x)) == REG && REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER)))) { if (!in || !aligned_memory_operand(x, mode)) return GENERAL_REGS; } } if (class == FLOAT_REGS) { if (GET_CODE (x) == MEM && GET_CODE (XEXP (x, 0)) == AND) return GENERAL_REGS; if (GET_CODE (x) == SUBREG && (GET_MODE_SIZE (GET_MODE (x)) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))) return GENERAL_REGS; if (in && INTEGRAL_MODE_P (mode) && ! (memory_operand (x, mode) || x == const0_rtx)) return GENERAL_REGS; } return NO_REGS; } /* Subfunction of the following function. Update the flags of any MEM found in part of X. */ static void alpha_set_memflags_1 (rtx x, int in_struct_p, int volatile_p, int unchanging_p) { int i; switch (GET_CODE (x)) { case SEQUENCE: abort (); case PARALLEL: for (i = XVECLEN (x, 0) - 1; i >= 0; i--) alpha_set_memflags_1 (XVECEXP (x, 0, i), in_struct_p, volatile_p, unchanging_p); break; case INSN: alpha_set_memflags_1 (PATTERN (x), in_struct_p, volatile_p, unchanging_p); break; case SET: alpha_set_memflags_1 (SET_DEST (x), in_struct_p, volatile_p, unchanging_p); alpha_set_memflags_1 (SET_SRC (x), in_struct_p, volatile_p, unchanging_p); break; case MEM: MEM_IN_STRUCT_P (x) = in_struct_p; MEM_VOLATILE_P (x) = volatile_p; RTX_UNCHANGING_P (x) = unchanging_p; /* Sadly, we cannot use alias sets because the extra aliasing produced by the AND interferes. Given that two-byte quantities are the only thing we would be able to differentiate anyway, there does not seem to be any point in convoluting the early out of the alias check. */ break; default: break; } } /* Given INSN, which is an INSN list or the PATTERN of a single insn generated to perform a memory operation, look for any MEMs in either a SET_DEST or a SET_SRC and copy the in-struct, unchanging, and volatile flags from REF into each of the MEMs found. If REF is not a MEM, don't do anything. */ void alpha_set_memflags (rtx insn, rtx ref) { int in_struct_p, volatile_p, unchanging_p; if (GET_CODE (ref) != MEM) return; in_struct_p = MEM_IN_STRUCT_P (ref); volatile_p = MEM_VOLATILE_P (ref); unchanging_p = RTX_UNCHANGING_P (ref); /* This is only called from alpha.md, after having had something generated from one of the insn patterns. So if everything is zero, the pattern is already up-to-date. */ if (! in_struct_p && ! volatile_p && ! unchanging_p) return; alpha_set_memflags_1 (insn, in_struct_p, volatile_p, unchanging_p); } /* Internal routine for alpha_emit_set_const to check for N or below insns. */ static rtx alpha_emit_set_const_1 (rtx target, enum machine_mode mode, HOST_WIDE_INT c, int n) { HOST_WIDE_INT new; int i, bits; /* Use a pseudo if highly optimizing and still generating RTL. */ rtx subtarget = (flag_expensive_optimizations && !no_new_pseudos ? 0 : target); rtx temp, insn; /* If this is a sign-extended 32-bit constant, we can do this in at most three insns, so do it if we have enough insns left. We always have a sign-extended 32-bit constant when compiling on a narrow machine. */ if (HOST_BITS_PER_WIDE_INT != 64 || c >> 31 == -1 || c >> 31 == 0) { HOST_WIDE_INT low = ((c & 0xffff) ^ 0x8000) - 0x8000; HOST_WIDE_INT tmp1 = c - low; HOST_WIDE_INT high = (((tmp1 >> 16) & 0xffff) ^ 0x8000) - 0x8000; HOST_WIDE_INT extra = 0; /* If HIGH will be interpreted as negative but the constant is positive, we must adjust it to do two ldha insns. */ if ((high & 0x8000) != 0 && c >= 0) { extra = 0x4000; tmp1 -= 0x40000000; high = ((tmp1 >> 16) & 0xffff) - 2 * ((tmp1 >> 16) & 0x8000); } if (c == low || (low == 0 && extra == 0)) { /* We used to use copy_to_suggested_reg (GEN_INT (c), target, mode) but that meant that we can't handle INT_MIN on 32-bit machines (like NT/Alpha), because we recurse indefinitely through emit_move_insn to gen_movdi. So instead, since we know exactly what we want, create it explicitly. */ if (target == NULL) target = gen_reg_rtx (mode); emit_insn (gen_rtx_SET (VOIDmode, target, GEN_INT (c))); return target; } else if (n >= 2 + (extra != 0)) { if (no_new_pseudos) { emit_insn (gen_rtx_SET (VOIDmode, target, GEN_INT (high << 16))); temp = target; } else temp = copy_to_suggested_reg (GEN_INT (high << 16), subtarget, mode); /* As of 2002-02-23, addsi3 is only available when not optimizing. This means that if we go through expand_binop, we'll try to generate extensions, etc, which will require new pseudos, which will fail during some split phases. The SImode add patterns still exist, but are not named. So build the insns by hand. */ if (extra != 0) { if (! subtarget) subtarget = gen_reg_rtx (mode); insn = gen_rtx_PLUS (mode, temp, GEN_INT (extra << 16)); insn = gen_rtx_SET (VOIDmode, subtarget, insn); emit_insn (insn); temp = subtarget; } if (target == NULL) target = gen_reg_rtx (mode); insn = gen_rtx_PLUS (mode, temp, GEN_INT (low)); insn = gen_rtx_SET (VOIDmode, target, insn); emit_insn (insn); return target; } } /* If we couldn't do it that way, try some other methods. But if we have no instructions left, don't bother. Likewise, if this is SImode and we can't make pseudos, we can't do anything since the expand_binop and expand_unop calls will widen and try to make pseudos. */ if (n == 1 || (mode == SImode && no_new_pseudos)) return 0; /* Next, see if we can load a related constant and then shift and possibly negate it to get the constant we want. Try this once each increasing numbers of insns. */ for (i = 1; i < n; i++) { /* First, see if minus some low bits, we've an easy load of high bits. */ new = ((c & 0xffff) ^ 0x8000) - 0x8000; if (new != 0 && (temp = alpha_emit_set_const (subtarget, mode, c - new, i)) != 0) return expand_binop (mode, add_optab, temp, GEN_INT (new), target, 0, OPTAB_WIDEN); /* Next try complementing. */ if ((temp = alpha_emit_set_const (subtarget, mode, ~ c, i)) != 0) return expand_unop (mode, one_cmpl_optab, temp, target, 0); /* Next try to form a constant and do a left shift. We can do this if some low-order bits are zero; the exact_log2 call below tells us that information. The bits we are shifting out could be any value, but here we'll just try the 0- and sign-extended forms of the constant. To try to increase the chance of having the same constant in more than one insn, start at the highest number of bits to shift, but try all possibilities in case a ZAPNOT will be useful. */ if ((bits = exact_log2 (c & - c)) > 0) for (; bits > 0; bits--) if ((temp = (alpha_emit_set_const (subtarget, mode, c >> bits, i))) != 0 || ((temp = (alpha_emit_set_const (subtarget, mode, ((unsigned HOST_WIDE_INT) c) >> bits, i))) != 0)) return expand_binop (mode, ashl_optab, temp, GEN_INT (bits), target, 0, OPTAB_WIDEN); /* Now try high-order zero bits. Here we try the shifted-in bits as all zero and all ones. Be careful to avoid shifting outside the mode and to avoid shifting outside the host wide int size. */ /* On narrow hosts, don't shift a 1 into the high bit, since we'll confuse the recursive call and set all of the high 32 bits. */ if ((bits = (MIN (HOST_BITS_PER_WIDE_INT, GET_MODE_SIZE (mode) * 8) - floor_log2 (c) - 1 - (HOST_BITS_PER_WIDE_INT < 64))) > 0) for (; bits > 0; bits--) if ((temp = alpha_emit_set_const (subtarget, mode, c << bits, i)) != 0 || ((temp = (alpha_emit_set_const (subtarget, mode, ((c << bits) | (((HOST_WIDE_INT) 1 << bits) - 1)), i))) != 0)) return expand_binop (mode, lshr_optab, temp, GEN_INT (bits), target, 1, OPTAB_WIDEN); /* Now try high-order 1 bits. We get that with a sign-extension. But one bit isn't enough here. Be careful to avoid shifting outside the mode and to avoid shifting outside the host wide int size. */ if ((bits = (MIN (HOST_BITS_PER_WIDE_INT, GET_MODE_SIZE (mode) * 8) - floor_log2 (~ c) - 2)) > 0) for (; bits > 0; bits--) if ((temp = alpha_emit_set_const (subtarget, mode, c << bits, i)) != 0 || ((temp = (alpha_emit_set_const (subtarget, mode, ((c << bits) | (((HOST_WIDE_INT) 1 << bits) - 1)), i))) != 0)) return expand_binop (mode, ashr_optab, temp, GEN_INT (bits), target, 0, OPTAB_WIDEN); } #if HOST_BITS_PER_WIDE_INT == 64 /* Finally, see if can load a value into the target that is the same as the constant except that all bytes that are 0 are changed to be 0xff. If we can, then we can do a ZAPNOT to obtain the desired constant. */ new = c; for (i = 0; i < 64; i += 8) if ((new & ((HOST_WIDE_INT) 0xff << i)) == 0) new |= (HOST_WIDE_INT) 0xff << i; /* We are only called for SImode and DImode. If this is SImode, ensure that we are sign extended to a full word. */ if (mode == SImode) new = ((new & 0xffffffff) ^ 0x80000000) - 0x80000000; if (new != c && new != -1 && (temp = alpha_emit_set_const (subtarget, mode, new, n - 1)) != 0) return expand_binop (mode, and_optab, temp, GEN_INT (c | ~ new), target, 0, OPTAB_WIDEN); #endif return 0; } /* Try to output insns to set TARGET equal to the constant C if it can be done in less than N insns. Do all computations in MODE. Returns the place where the output has been placed if it can be done and the insns have been emitted. If it would take more than N insns, zero is returned and no insns and emitted. */ rtx alpha_emit_set_const (rtx target, enum machine_mode mode, HOST_WIDE_INT c, int n) { rtx result = 0; rtx orig_target = target; int i; /* If we can't make any pseudos, TARGET is an SImode hard register, we can't load this constant in one insn, do this in DImode. */ if (no_new_pseudos && mode == SImode && GET_CODE (target) == REG && REGNO (target) < FIRST_PSEUDO_REGISTER && (result = alpha_emit_set_const_1 (target, mode, c, 1)) == 0) { target = gen_lowpart (DImode, target); mode = DImode; } /* Try 1 insn, then 2, then up to N. */ for (i = 1; i <= n; i++) { result = alpha_emit_set_const_1 (target, mode, c, i); if (result) { rtx insn = get_last_insn (); rtx set = single_set (insn); if (! CONSTANT_P (SET_SRC (set))) set_unique_reg_note (get_last_insn (), REG_EQUAL, GEN_INT (c)); break; } } /* Allow for the case where we changed the mode of TARGET. */ if (result == target) result = orig_target; return result; } /* Having failed to find a 3 insn sequence in alpha_emit_set_const, fall back to a straight forward decomposition. We do this to avoid exponential run times encountered when looking for longer sequences with alpha_emit_set_const. */ rtx alpha_emit_set_long_const (rtx target, HOST_WIDE_INT c1, HOST_WIDE_INT c2) { HOST_WIDE_INT d1, d2, d3, d4; /* Decompose the entire word */ #if HOST_BITS_PER_WIDE_INT >= 64 if (c2 != -(c1 < 0)) abort (); d1 = ((c1 & 0xffff) ^ 0x8000) - 0x8000; c1 -= d1; d2 = ((c1 & 0xffffffff) ^ 0x80000000) - 0x80000000; c1 = (c1 - d2) >> 32; d3 = ((c1 & 0xffff) ^ 0x8000) - 0x8000; c1 -= d3; d4 = ((c1 & 0xffffffff) ^ 0x80000000) - 0x80000000; if (c1 != d4) abort (); #else d1 = ((c1 & 0xffff) ^ 0x8000) - 0x8000; c1 -= d1; d2 = ((c1 & 0xffffffff) ^ 0x80000000) - 0x80000000; if (c1 != d2) abort (); c2 += (d2 < 0); d3 = ((c2 & 0xffff) ^ 0x8000) - 0x8000; c2 -= d3; d4 = ((c2 & 0xffffffff) ^ 0x80000000) - 0x80000000; if (c2 != d4) abort (); #endif /* Construct the high word */ if (d4) { emit_move_insn (target, GEN_INT (d4)); if (d3) emit_move_insn (target, gen_rtx_PLUS (DImode, target, GEN_INT (d3))); } else emit_move_insn (target, GEN_INT (d3)); /* Shift it into place */ emit_move_insn (target, gen_rtx_ASHIFT (DImode, target, GEN_INT (32))); /* Add in the low bits. */ if (d2) emit_move_insn (target, gen_rtx_PLUS (DImode, target, GEN_INT (d2))); if (d1) emit_move_insn (target, gen_rtx_PLUS (DImode, target, GEN_INT (d1))); return target; } /* Expand a move instruction; return true if all work is done. We don't handle non-bwx subword loads here. */ bool alpha_expand_mov (enum machine_mode mode, rtx *operands) { /* If the output is not a register, the input must be. */ if (GET_CODE (operands[0]) == MEM && ! reg_or_0_operand (operands[1], mode)) operands[1] = force_reg (mode, operands[1]); /* Allow legitimize_address to perform some simplifications. */ if (mode == Pmode && symbolic_operand (operands[1], mode)) { rtx tmp; /* With RTL inlining, at -O3, rtl is generated, stored, then actually compiled at the end of compilation. In the meantime, someone can re-encode-section-info on some symbol changing it e.g. from global to local-not-small. If this happens, we'd have emitted a plain load rather than a high+losum load and not recognize the insn. So if rtl inlining is in effect, we delay the global/not-global decision until rest_of_compilation by wrapping it in an UNSPEC_SYMBOL. */ if (TARGET_EXPLICIT_RELOCS && flag_inline_functions && rtx_equal_function_value_matters && global_symbolic_operand (operands[1], mode)) { emit_insn (gen_movdi_er_maybe_g (operands[0], operands[1])); return true; } tmp = alpha_legitimize_address (operands[1], operands[0], mode); if (tmp) { if (tmp == operands[0]) return true; operands[1] = tmp; return false; } } /* Early out for non-constants and valid constants. */ if (! CONSTANT_P (operands[1]) || input_operand (operands[1], mode)) return false; /* Split large integers. */ if (GET_CODE (operands[1]) == CONST_INT || GET_CODE (operands[1]) == CONST_DOUBLE) { HOST_WIDE_INT i0, i1; rtx temp = NULL_RTX; if (GET_CODE (operands[1]) == CONST_INT) { i0 = INTVAL (operands[1]); i1 = -(i0 < 0); } else if (HOST_BITS_PER_WIDE_INT >= 64) { i0 = CONST_DOUBLE_LOW (operands[1]); i1 = -(i0 < 0); } else { i0 = CONST_DOUBLE_LOW (operands[1]); i1 = CONST_DOUBLE_HIGH (operands[1]); } if (HOST_BITS_PER_WIDE_INT >= 64 || i1 == -(i0 < 0)) temp = alpha_emit_set_const (operands[0], mode, i0, 3); if (!temp && TARGET_BUILD_CONSTANTS) temp = alpha_emit_set_long_const (operands[0], i0, i1); if (temp) { if (rtx_equal_p (operands[0], temp)) return true; operands[1] = temp; return false; } } /* Otherwise we've nothing left but to drop the thing to memory. */ operands[1] = force_const_mem (mode, operands[1]); if (reload_in_progress) { emit_move_insn (operands[0], XEXP (operands[1], 0)); operands[1] = copy_rtx (operands[1]); XEXP (operands[1], 0) = operands[0]; } else operands[1] = validize_mem (operands[1]); return false; } /* Expand a non-bwx QImode or HImode move instruction; return true if all work is done. */ bool alpha_expand_mov_nobwx (enum machine_mode mode, rtx *operands) { /* If the output is not a register, the input must be. */ if (GET_CODE (operands[0]) == MEM) operands[1] = force_reg (mode, operands[1]); /* Handle four memory cases, unaligned and aligned for either the input or the output. The only case where we can be called during reload is for aligned loads; all other cases require temporaries. */ if (GET_CODE (operands[1]) == MEM || (GET_CODE (operands[1]) == SUBREG && GET_CODE (SUBREG_REG (operands[1])) == MEM) || (reload_in_progress && GET_CODE (operands[1]) == REG && REGNO (operands[1]) >= FIRST_PSEUDO_REGISTER) || (reload_in_progress && GET_CODE (operands[1]) == SUBREG && GET_CODE (SUBREG_REG (operands[1])) == REG && REGNO (SUBREG_REG (operands[1])) >= FIRST_PSEUDO_REGISTER)) { if (aligned_memory_operand (operands[1], mode)) { if (reload_in_progress) { emit_insn ((mode == QImode ? gen_reload_inqi_help : gen_reload_inhi_help) (operands[0], operands[1], gen_rtx_REG (SImode, REGNO (operands[0])))); } else { rtx aligned_mem, bitnum; rtx scratch = gen_reg_rtx (SImode); rtx subtarget; bool copyout; get_aligned_mem (operands[1], &aligned_mem, &bitnum); subtarget = operands[0]; if (GET_CODE (subtarget) == REG) subtarget = gen_lowpart (DImode, subtarget), copyout = false; else subtarget = gen_reg_rtx (DImode), copyout = true; emit_insn ((mode == QImode ? gen_aligned_loadqi : gen_aligned_loadhi) (subtarget, aligned_mem, bitnum, scratch)); if (copyout) emit_move_insn (operands[0], gen_lowpart (mode, subtarget)); } } else { /* Don't pass these as parameters since that makes the generated code depend on parameter evaluation order which will cause bootstrap failures. */ rtx temp1, temp2, seq, subtarget; bool copyout; temp1 = gen_reg_rtx (DImode); temp2 = gen_reg_rtx (DImode); subtarget = operands[0]; if (GET_CODE (subtarget) == REG) subtarget = gen_lowpart (DImode, subtarget), copyout = false; else subtarget = gen_reg_rtx (DImode), copyout = true; seq = ((mode == QImode ? gen_unaligned_loadqi : gen_unaligned_loadhi) (subtarget, get_unaligned_address (operands[1], 0), temp1, temp2)); alpha_set_memflags (seq, operands[1]); emit_insn (seq); if (copyout) emit_move_insn (operands[0], gen_lowpart (mode, subtarget)); } return true; } if (GET_CODE (operands[0]) == MEM || (GET_CODE (operands[0]) == SUBREG && GET_CODE (SUBREG_REG (operands[0])) == MEM) || (reload_in_progress && GET_CODE (operands[0]) == REG && REGNO (operands[0]) >= FIRST_PSEUDO_REGISTER) || (reload_in_progress && GET_CODE (operands[0]) == SUBREG && GET_CODE (SUBREG_REG (operands[0])) == REG && REGNO (operands[0]) >= FIRST_PSEUDO_REGISTER)) { if (aligned_memory_operand (operands[0], mode)) { rtx aligned_mem, bitnum; rtx temp1 = gen_reg_rtx (SImode); rtx temp2 = gen_reg_rtx (SImode); get_aligned_mem (operands[0], &aligned_mem, &bitnum); emit_insn (gen_aligned_store (aligned_mem, operands[1], bitnum, temp1, temp2)); } else { rtx temp1 = gen_reg_rtx (DImode); rtx temp2 = gen_reg_rtx (DImode); rtx temp3 = gen_reg_rtx (DImode); rtx seq = ((mode == QImode ? gen_unaligned_storeqi : gen_unaligned_storehi) (get_unaligned_address (operands[0], 0), operands[1], temp1, temp2, temp3)); alpha_set_memflags (seq, operands[0]); emit_insn (seq); } return true; } return false; } /* Generate an unsigned DImode to FP conversion. This is the same code optabs would emit if we didn't have TFmode patterns. For SFmode, this is the only construction I've found that can pass gcc.c-torture/execute/ieee/rbug.c. No scenario that uses DFmode intermediates will work, because you'll get intermediate rounding that ruins the end result. Some of this could be fixed by turning on round-to-positive-infinity, but that requires diddling the fpsr, which kills performance. I tried turning this around and converting to a negative number, so that I could turn on /m, but either I did it wrong or there's something else cause I wound up with the exact same single-bit error. There is a branch-less form of this same code: srl $16,1,$1 and $16,1,$2 cmplt $16,0,$3 or $1,$2,$2 cmovge $16,$16,$2 itoft $3,$f10 itoft $2,$f11 cvtqs $f11,$f11 adds $f11,$f11,$f0 fcmoveq $f10,$f11,$f0 I'm not using it because it's the same number of instructions as this branch-full form, and it has more serialized long latency instructions on the critical path. For DFmode, we can avoid rounding errors by breaking up the word into two pieces, converting them separately, and adding them back: LC0: .long 0,0x5f800000 itoft $16,$f11 lda $2,LC0 cmplt $16,0,$1 cpyse $f11,$f31,$f10 cpyse $f31,$f11,$f11 s4addq $1,$2,$1 lds $f12,0($1) cvtqt $f10,$f10 cvtqt $f11,$f11 addt $f12,$f10,$f0 addt $f0,$f11,$f0 This doesn't seem to be a clear-cut win over the optabs form. It probably all depends on the distribution of numbers being converted -- in the optabs form, all but high-bit-set has a much lower minimum execution time. */ void alpha_emit_floatuns (rtx operands[2]) { rtx neglab, donelab, i0, i1, f0, in, out; enum machine_mode mode; out = operands[0]; in = force_reg (DImode, operands[1]); mode = GET_MODE (out); neglab = gen_label_rtx (); donelab = gen_label_rtx (); i0 = gen_reg_rtx (DImode); i1 = gen_reg_rtx (DImode); f0 = gen_reg_rtx (mode); emit_cmp_and_jump_insns (in, const0_rtx, LT, const0_rtx, DImode, 0, neglab); emit_insn (gen_rtx_SET (VOIDmode, out, gen_rtx_FLOAT (mode, in))); emit_jump_insn (gen_jump (donelab)); emit_barrier (); emit_label (neglab); emit_insn (gen_lshrdi3 (i0, in, const1_rtx)); emit_insn (gen_anddi3 (i1, in, const1_rtx)); emit_insn (gen_iordi3 (i0, i0, i1)); emit_insn (gen_rtx_SET (VOIDmode, f0, gen_rtx_FLOAT (mode, i0))); emit_insn (gen_rtx_SET (VOIDmode, out, gen_rtx_PLUS (mode, f0, f0))); emit_label (donelab); } /* Generate the comparison for a conditional branch. */ rtx alpha_emit_conditional_branch (enum rtx_code code) { enum rtx_code cmp_code, branch_code; enum machine_mode cmp_mode, branch_mode = VOIDmode; rtx op0 = alpha_compare.op0, op1 = alpha_compare.op1; rtx tem; if (alpha_compare.fp_p && GET_MODE (op0) == TFmode) { if (! TARGET_HAS_XFLOATING_LIBS) abort (); /* X_floating library comparison functions return -1 unordered 0 false 1 true Convert the compare against the raw return value. */ switch (code) { case UNORDERED: cmp_code = EQ; code = LT; break; case ORDERED: cmp_code = EQ; code = GE; break; case NE: cmp_code = NE; code = NE; break; default: cmp_code = code; code = GT; break; } op0 = alpha_emit_xfloating_compare (cmp_code, op0, op1); op1 = const0_rtx; alpha_compare.fp_p = 0; } /* The general case: fold the comparison code to the types of compares that we have, choosing the branch as necessary. */ switch (code) { case EQ: case LE: case LT: case LEU: case LTU: case UNORDERED: /* We have these compares: */ cmp_code = code, branch_code = NE; break; case NE: case ORDERED: /* These must be reversed. */ cmp_code = reverse_condition (code), branch_code = EQ; break; case GE: case GT: case GEU: case GTU: /* For FP, we swap them, for INT, we reverse them. */ if (alpha_compare.fp_p) { cmp_code = swap_condition (code); branch_code = NE; tem = op0, op0 = op1, op1 = tem; } else { cmp_code = reverse_condition (code); branch_code = EQ; } break; default: abort (); } if (alpha_compare.fp_p) { cmp_mode = DFmode; if (flag_unsafe_math_optimizations) { /* When we are not as concerned about non-finite values, and we are comparing against zero, we can branch directly. */ if (op1 == CONST0_RTX (DFmode)) cmp_code = NIL, branch_code = code; else if (op0 == CONST0_RTX (DFmode)) { /* Undo the swap we probably did just above. */ tem = op0, op0 = op1, op1 = tem; branch_code = swap_condition (cmp_code); cmp_code = NIL; } } else { /* ??? We mark the branch mode to be CCmode to prevent the compare and branch from being combined, since the compare insn follows IEEE rules that the branch does not. */ branch_mode = CCmode; } } else { cmp_mode = DImode; /* The following optimizations are only for signed compares. */ if (code != LEU && code != LTU && code != GEU && code != GTU) { /* Whee. Compare and branch against 0 directly. */ if (op1 == const0_rtx) cmp_code = NIL, branch_code = code; /* If the constants doesn't fit into an immediate, but can be generated by lda/ldah, we adjust the argument and compare against zero, so we can use beq/bne directly. */ /* ??? Don't do this when comparing against symbols, otherwise we'll reduce (&x == 0x1234) to (&x-0x1234 == 0), which will be declared false out of hand (at least for non-weak). */ else if (GET_CODE (op1) == CONST_INT && (code == EQ || code == NE) && !(symbolic_operand (op0, VOIDmode) || (GET_CODE (op0) == REG && REG_POINTER (op0)))) { HOST_WIDE_INT v = INTVAL (op1), n = -v; if (! CONST_OK_FOR_LETTER_P (v, 'I') && (CONST_OK_FOR_LETTER_P (n, 'K') || CONST_OK_FOR_LETTER_P (n, 'L'))) { cmp_code = PLUS, branch_code = code; op1 = GEN_INT (n); } } } if (!reg_or_0_operand (op0, DImode)) op0 = force_reg (DImode, op0); if (cmp_code != PLUS && !reg_or_8bit_operand (op1, DImode)) op1 = force_reg (DImode, op1); } /* Emit an initial compare instruction, if necessary. */ tem = op0; if (cmp_code != NIL) { tem = gen_reg_rtx (cmp_mode); emit_move_insn (tem, gen_rtx_fmt_ee (cmp_code, cmp_mode, op0, op1)); } /* Zero the operands. */ memset (&alpha_compare, 0, sizeof (alpha_compare)); /* Return the branch comparison. */ return gen_rtx_fmt_ee (branch_code, branch_mode, tem, CONST0_RTX (cmp_mode)); } /* Certain simplifications can be done to make invalid setcc operations valid. Return the final comparison, or NULL if we can't work. */ rtx alpha_emit_setcc (enum rtx_code code) { enum rtx_code cmp_code; rtx op0 = alpha_compare.op0, op1 = alpha_compare.op1; int fp_p = alpha_compare.fp_p; rtx tmp; /* Zero the operands. */ memset (&alpha_compare, 0, sizeof (alpha_compare)); if (fp_p && GET_MODE (op0) == TFmode) { if (! TARGET_HAS_XFLOATING_LIBS) abort (); /* X_floating library comparison functions return -1 unordered 0 false 1 true Convert the compare against the raw return value. */ if (code == UNORDERED || code == ORDERED) cmp_code = EQ; else cmp_code = code; op0 = alpha_emit_xfloating_compare (cmp_code, op0, op1); op1 = const0_rtx; fp_p = 0; if (code == UNORDERED) code = LT; else if (code == ORDERED) code = GE; else code = GT; } if (fp_p && !TARGET_FIX) return NULL_RTX; /* The general case: fold the comparison code to the types of compares that we have, choosing the branch as necessary. */ cmp_code = NIL; switch (code) { case EQ: case LE: case LT: case LEU: case LTU: case UNORDERED: /* We have these compares. */ if (fp_p) cmp_code = code, code = NE; break; case NE: if (!fp_p && op1 == const0_rtx) break; /* FALLTHRU */ case ORDERED: cmp_code = reverse_condition (code); code = EQ; break; case GE: case GT: case GEU: case GTU: /* These normally need swapping, but for integer zero we have special patterns that recognize swapped operands. */ if (!fp_p && op1 == const0_rtx) break; code = swap_condition (code); if (fp_p) cmp_code = code, code = NE; tmp = op0, op0 = op1, op1 = tmp; break; default: abort (); } if (!fp_p) { if (!register_operand (op0, DImode)) op0 = force_reg (DImode, op0); if (!reg_or_8bit_operand (op1, DImode)) op1 = force_reg (DImode, op1); } /* Emit an initial compare instruction, if necessary. */ if (cmp_code != NIL) { enum machine_mode mode = fp_p ? DFmode : DImode; tmp = gen_reg_rtx (mode); emit_insn (gen_rtx_SET (VOIDmode, tmp, gen_rtx_fmt_ee (cmp_code, mode, op0, op1))); op0 = fp_p ? gen_lowpart (DImode, tmp) : tmp; op1 = const0_rtx; } /* Return the setcc comparison. */ return gen_rtx_fmt_ee (code, DImode, op0, op1); } /* Rewrite a comparison against zero CMP of the form (CODE (cc0) (const_int 0)) so it can be written validly in a conditional move (if_then_else CMP ...). If both of the operands that set cc0 are nonzero we must emit an insn to perform the compare (it can't be done within the conditional move). */ rtx alpha_emit_conditional_move (rtx cmp, enum machine_mode mode) { enum rtx_code code = GET_CODE (cmp); enum rtx_code cmov_code = NE; rtx op0 = alpha_compare.op0; rtx op1 = alpha_compare.op1; int fp_p = alpha_compare.fp_p; enum machine_mode cmp_mode = (GET_MODE (op0) == VOIDmode ? DImode : GET_MODE (op0)); enum machine_mode cmp_op_mode = fp_p ? DFmode : DImode; enum machine_mode cmov_mode = VOIDmode; int local_fast_math = flag_unsafe_math_optimizations; rtx tem; /* Zero the operands. */ memset (&alpha_compare, 0, sizeof (alpha_compare)); if (fp_p != FLOAT_MODE_P (mode)) { enum rtx_code cmp_code; if (! TARGET_FIX) return 0; /* If we have fp<->int register move instructions, do a cmov by performing the comparison in fp registers, and move the zero/nonzero value to integer registers, where we can then use a normal cmov, or vice-versa. */ switch (code) { case EQ: case LE: case LT: case LEU: case LTU: /* We have these compares. */ cmp_code = code, code = NE; break; case NE: /* This must be reversed. */ cmp_code = EQ, code = EQ; break; case GE: case GT: case GEU: case GTU: /* These normally need swapping, but for integer zero we have special patterns that recognize swapped operands. */ if (!fp_p && op1 == const0_rtx) cmp_code = code, code = NE; else { cmp_code = swap_condition (code); code = NE; tem = op0, op0 = op1, op1 = tem; } break; default: abort (); } tem = gen_reg_rtx (cmp_op_mode); emit_insn (gen_rtx_SET (VOIDmode, tem, gen_rtx_fmt_ee (cmp_code, cmp_op_mode, op0, op1))); cmp_mode = cmp_op_mode = fp_p ? DImode : DFmode; op0 = gen_lowpart (cmp_op_mode, tem); op1 = CONST0_RTX (cmp_op_mode); fp_p = !fp_p; local_fast_math = 1; } /* We may be able to use a conditional move directly. This avoids emitting spurious compares. */ if (signed_comparison_operator (cmp, VOIDmode) && (!fp_p || local_fast_math) && (op0 == CONST0_RTX (cmp_mode) || op1 == CONST0_RTX (cmp_mode))) return gen_rtx_fmt_ee (code, VOIDmode, op0, op1); /* We can't put the comparison inside the conditional move; emit a compare instruction and put that inside the conditional move. Make sure we emit only comparisons we have; swap or reverse as necessary. */ if (no_new_pseudos) return NULL_RTX; switch (code) { case EQ: case LE: case LT: case LEU: case LTU: /* We have these compares: */ break; case NE: /* This must be reversed. */ code = reverse_condition (code); cmov_code = EQ; break; case GE: case GT: case GEU: case GTU: /* These must be swapped. */ if (op1 != CONST0_RTX (cmp_mode)) { code = swap_condition (code); tem = op0, op0 = op1, op1 = tem; } break; default: abort (); } if (!fp_p) { if (!reg_or_0_operand (op0, DImode)) op0 = force_reg (DImode, op0); if (!reg_or_8bit_operand (op1, DImode)) op1 = force_reg (DImode, op1); } /* ??? We mark the branch mode to be CCmode to prevent the compare and cmov from being combined, since the compare insn follows IEEE rules that the cmov does not. */ if (fp_p && !local_fast_math) cmov_mode = CCmode; tem = gen_reg_rtx (cmp_op_mode); emit_move_insn (tem, gen_rtx_fmt_ee (code, cmp_op_mode, op0, op1)); return gen_rtx_fmt_ee (cmov_code, cmov_mode, tem, CONST0_RTX (cmp_op_mode)); } /* Simplify a conditional move of two constants into a setcc with arithmetic. This is done with a splitter since combine would just undo the work if done during code generation. It also catches cases we wouldn't have before cse. */ int alpha_split_conditional_move (enum rtx_code code, rtx dest, rtx cond, rtx t_rtx, rtx f_rtx) { HOST_WIDE_INT t, f, diff; enum machine_mode mode; rtx target, subtarget, tmp; mode = GET_MODE (dest); t = INTVAL (t_rtx); f = INTVAL (f_rtx); diff = t - f; if (((code == NE || code == EQ) && diff < 0) || (code == GE || code == GT)) { code = reverse_condition (code); diff = t, t = f, f = diff; diff = t - f; } subtarget = target = dest; if (mode != DImode) { target = gen_lowpart (DImode, dest); if (! no_new_pseudos) subtarget = gen_reg_rtx (DImode); else subtarget = target; } /* Below, we must be careful to use copy_rtx on target and subtarget in intermediate insns, as they may be a subreg rtx, which may not be shared. */ if (f == 0 && exact_log2 (diff) > 0 /* On EV6, we've got enough shifters to make non-arithmetic shifts viable over a longer latency cmove. On EV5, the E0 slot is a scarce resource, and on EV4 shift has the same latency as a cmove. */ && (diff <= 8 || alpha_cpu == PROCESSOR_EV6)) { tmp = gen_rtx_fmt_ee (code, DImode, cond, const0_rtx); emit_insn (gen_rtx_SET (VOIDmode, copy_rtx (subtarget), tmp)); tmp = gen_rtx_ASHIFT (DImode, copy_rtx (subtarget), GEN_INT (exact_log2 (t))); emit_insn (gen_rtx_SET (VOIDmode, target, tmp)); } else if (f == 0 && t == -1) { tmp = gen_rtx_fmt_ee (code, DImode, cond, const0_rtx); emit_insn (gen_rtx_SET (VOIDmode, copy_rtx (subtarget), tmp)); emit_insn (gen_negdi2 (target, copy_rtx (subtarget))); } else if (diff == 1 || diff == 4 || diff == 8) { rtx add_op; tmp = gen_rtx_fmt_ee (code, DImode, cond, const0_rtx); emit_insn (gen_rtx_SET (VOIDmode, copy_rtx (subtarget), tmp)); if (diff == 1) emit_insn (gen_adddi3 (target, copy_rtx (subtarget), GEN_INT (f))); else { add_op = GEN_INT (f); if (sext_add_operand (add_op, mode)) { tmp = gen_rtx_MULT (DImode, copy_rtx (subtarget), GEN_INT (diff)); tmp = gen_rtx_PLUS (DImode, tmp, add_op); emit_insn (gen_rtx_SET (VOIDmode, target, tmp)); } else return 0; } } else return 0; return 1; } /* Look up the function X_floating library function name for the given operation. */ static const char * alpha_lookup_xfloating_lib_func (enum rtx_code code) { struct xfloating_op { const enum rtx_code code; const char *const func; }; static const struct xfloating_op vms_xfloating_ops[] = { { PLUS, "OTS$ADD_X" }, { MINUS, "OTS$SUB_X" }, { MULT, "OTS$MUL_X" }, { DIV, "OTS$DIV_X" }, { EQ, "OTS$EQL_X" }, { NE, "OTS$NEQ_X" }, { LT, "OTS$LSS_X" }, { LE, "OTS$LEQ_X" }, { GT, "OTS$GTR_X" }, { GE, "OTS$GEQ_X" }, { FIX, "OTS$CVTXQ" }, { FLOAT, "OTS$CVTQX" }, { UNSIGNED_FLOAT, "OTS$CVTQUX" }, { FLOAT_EXTEND, "OTS$CVT_FLOAT_T_X" }, { FLOAT_TRUNCATE, "OTS$CVT_FLOAT_X_T" }, }; static const struct xfloating_op osf_xfloating_ops[] = { { PLUS, "_OtsAddX" }, { MINUS, "_OtsSubX" }, { MULT, "_OtsMulX" }, { DIV, "_OtsDivX" }, { EQ, "_OtsEqlX" }, { NE, "_OtsNeqX" }, { LT, "_OtsLssX" }, { LE, "_OtsLeqX" }, { GT, "_OtsGtrX" }, { GE, "_OtsGeqX" }, { FIX, "_OtsCvtXQ" }, { FLOAT, "_OtsCvtQX" }, { UNSIGNED_FLOAT, "_OtsCvtQUX" }, { FLOAT_EXTEND, "_OtsConvertFloatTX" }, { FLOAT_TRUNCATE, "_OtsConvertFloatXT" }, }; const struct xfloating_op *ops; const long n = ARRAY_SIZE (osf_xfloating_ops); long i; /* How irritating. Nothing to key off for the table. Hardcode knowledge of the G_floating routines. */ if (TARGET_FLOAT_VAX) { if (TARGET_ABI_OPEN_VMS) { if (code == FLOAT_EXTEND) return "OTS$CVT_FLOAT_G_X"; if (code == FLOAT_TRUNCATE) return "OTS$CVT_FLOAT_X_G"; } else { if (code == FLOAT_EXTEND) return "_OtsConvertFloatGX"; if (code == FLOAT_TRUNCATE) return "_OtsConvertFloatXG"; } } if (TARGET_ABI_OPEN_VMS) ops = vms_xfloating_ops; else ops = osf_xfloating_ops; for (i = 0; i < n; ++i) if (ops[i].code == code) return ops[i].func; abort(); } /* Most X_floating operations take the rounding mode as an argument. Compute that here. */ static int alpha_compute_xfloating_mode_arg (enum rtx_code code, enum alpha_fp_rounding_mode round) { int mode; switch (round) { case ALPHA_FPRM_NORM: mode = 2; break; case ALPHA_FPRM_MINF: mode = 1; break; case ALPHA_FPRM_CHOP: mode = 0; break; case ALPHA_FPRM_DYN: mode = 4; break; default: abort (); /* XXX For reference, round to +inf is mode = 3. */ } if (code == FLOAT_TRUNCATE && alpha_fptm == ALPHA_FPTM_N) mode |= 0x10000; return mode; } /* Emit an X_floating library function call. Note that these functions do not follow normal calling conventions: TFmode arguments are passed in two integer registers (as opposed to indirect); TFmode return values appear in R16+R17. FUNC is the function name to call. TARGET is where the output belongs. OPERANDS are the inputs. NOPERANDS is the count of inputs. EQUIV is the expression equivalent for the function. */ static void alpha_emit_xfloating_libcall (const char *func, rtx target, rtx operands[], int noperands, rtx equiv) { rtx usage = NULL_RTX, tmp, reg; int regno = 16, i; start_sequence (); for (i = 0; i < noperands; ++i) { switch (GET_MODE (operands[i])) { case TFmode: reg = gen_rtx_REG (TFmode, regno); regno += 2; break; case DFmode: reg = gen_rtx_REG (DFmode, regno + 32); regno += 1; break; case VOIDmode: if (GET_CODE (operands[i]) != CONST_INT) abort (); /* FALLTHRU */ case DImode: reg = gen_rtx_REG (DImode, regno); regno += 1; break; default: abort (); } emit_move_insn (reg, operands[i]); usage = alloc_EXPR_LIST (0, gen_rtx_USE (VOIDmode, reg), usage); } switch (GET_MODE (target)) { case TFmode: reg = gen_rtx_REG (TFmode, 16); break; case DFmode: reg = gen_rtx_REG (DFmode, 32); break; case DImode: reg = gen_rtx_REG (DImode, 0); break; default: abort (); } tmp = gen_rtx_MEM (QImode, init_one_libfunc (func)); tmp = emit_call_insn (GEN_CALL_VALUE (reg, tmp, const0_rtx, const0_rtx, const0_rtx)); CALL_INSN_FUNCTION_USAGE (tmp) = usage; tmp = get_insns (); end_sequence (); emit_libcall_block (tmp, target, reg, equiv); } /* Emit an X_floating library function call for arithmetic (+,-,*,/). */ void alpha_emit_xfloating_arith (enum rtx_code code, rtx operands[]) { const char *func; int mode; rtx out_operands[3]; func = alpha_lookup_xfloating_lib_func (code); mode = alpha_compute_xfloating_mode_arg (code, alpha_fprm); out_operands[0] = operands[1]; out_operands[1] = operands[2]; out_operands[2] = GEN_INT (mode); alpha_emit_xfloating_libcall (func, operands[0], out_operands, 3, gen_rtx_fmt_ee (code, TFmode, operands[1], operands[2])); } /* Emit an X_floating library function call for a comparison. */ static rtx alpha_emit_xfloating_compare (enum rtx_code code, rtx op0, rtx op1) { const char *func; rtx out, operands[2]; func = alpha_lookup_xfloating_lib_func (code); operands[0] = op0; operands[1] = op1; out = gen_reg_rtx (DImode); /* ??? Strange mode for equiv because what's actually returned is -1,0,1, not a proper boolean value. */ alpha_emit_xfloating_libcall (func, out, operands, 2, gen_rtx_fmt_ee (code, CCmode, op0, op1)); return out; } /* Emit an X_floating library function call for a conversion. */ void alpha_emit_xfloating_cvt (enum rtx_code orig_code, rtx operands[]) { int noperands = 1, mode; rtx out_operands[2]; const char *func; enum rtx_code code = orig_code; if (code == UNSIGNED_FIX) code = FIX; func = alpha_lookup_xfloating_lib_func (code); out_operands[0] = operands[1]; switch (code) { case FIX: mode = alpha_compute_xfloating_mode_arg (code, ALPHA_FPRM_CHOP); out_operands[1] = GEN_INT (mode); noperands = 2; break; case FLOAT_TRUNCATE: mode = alpha_compute_xfloating_mode_arg (code, alpha_fprm); out_operands[1] = GEN_INT (mode); noperands = 2; break; default: break; } alpha_emit_xfloating_libcall (func, operands[0], out_operands, noperands, gen_rtx_fmt_e (orig_code, GET_MODE (operands[0]), operands[1])); } /* Split a TFmode OP[1] into DImode OP[2,3] and likewise for OP[0] into OP[0,1]. Naturally, output operand ordering is little-endian. */ void alpha_split_tfmode_pair (rtx operands[4]) { if (GET_CODE (operands[1]) == REG) { operands[3] = gen_rtx_REG (DImode, REGNO (operands[1]) + 1); operands[2] = gen_rtx_REG (DImode, REGNO (operands[1])); } else if (GET_CODE (operands[1]) == MEM) { operands[3] = adjust_address (operands[1], DImode, 8); operands[2] = adjust_address (operands[1], DImode, 0); } else if (operands[1] == CONST0_RTX (TFmode)) operands[2] = operands[3] = const0_rtx; else abort (); if (GET_CODE (operands[0]) == REG) { operands[1] = gen_rtx_REG (DImode, REGNO (operands[0]) + 1); operands[0] = gen_rtx_REG (DImode, REGNO (operands[0])); } else if (GET_CODE (operands[0]) == MEM) { operands[1] = adjust_address (operands[0], DImode, 8); operands[0] = adjust_address (operands[0], DImode, 0); } else abort (); } /* Implement negtf2 or abstf2. Op0 is destination, op1 is source, op2 is a register containing the sign bit, operation is the logical operation to be performed. */ void alpha_split_tfmode_frobsign (rtx operands[3], rtx (*operation) (rtx, rtx, rtx)) { rtx high_bit = operands[2]; rtx scratch; int move; alpha_split_tfmode_pair (operands); /* Detect three flavors of operand overlap. */ move = 1; if (rtx_equal_p (operands[0], operands[2])) move = 0; else if (rtx_equal_p (operands[1], operands[2])) { if (rtx_equal_p (operands[0], high_bit)) move = 2; else move = -1; } if (move < 0) emit_move_insn (operands[0], operands[2]); /* ??? If the destination overlaps both source tf and high_bit, then assume source tf is dead in its entirety and use the other half for a scratch register. Otherwise "scratch" is just the proper destination register. */ scratch = operands[move < 2 ? 1 : 3]; emit_insn ((*operation) (scratch, high_bit, operands[3])); if (move > 0) { emit_move_insn (operands[0], operands[2]); if (move > 1) emit_move_insn (operands[1], scratch); } } /* Use ext[wlq][lh] as the Architecture Handbook describes for extracting unaligned data: unsigned: signed: word: ldq_u r1,X(r11) ldq_u r1,X(r11) ldq_u r2,X+1(r11) ldq_u r2,X+1(r11) lda r3,X(r11) lda r3,X+2(r11) extwl r1,r3,r1 extql r1,r3,r1 extwh r2,r3,r2 extqh r2,r3,r2 or r1.r2.r1 or r1,r2,r1 sra r1,48,r1 long: ldq_u r1,X(r11) ldq_u r1,X(r11) ldq_u r2,X+3(r11) ldq_u r2,X+3(r11) lda r3,X(r11) lda r3,X(r11) extll r1,r3,r1 extll r1,r3,r1 extlh r2,r3,r2 extlh r2,r3,r2 or r1.r2.r1 addl r1,r2,r1 quad: ldq_u r1,X(r11) ldq_u r2,X+7(r11) lda r3,X(r11) extql r1,r3,r1 extqh r2,r3,r2 or r1.r2.r1 */ void alpha_expand_unaligned_load (rtx tgt, rtx mem, HOST_WIDE_INT size, HOST_WIDE_INT ofs, int sign) { rtx meml, memh, addr, extl, exth, tmp, mema; enum machine_mode mode; meml = gen_reg_rtx (DImode); memh = gen_reg_rtx (DImode); addr = gen_reg_rtx (DImode); extl = gen_reg_rtx (DImode); exth = gen_reg_rtx (DImode); mema = XEXP (mem, 0); if (GET_CODE (mema) == LO_SUM) mema = force_reg (Pmode, mema); /* AND addresses cannot be in any alias set, since they may implicitly alias surrounding code. Ideally we'd have some alias set that covered all types except those with alignment 8 or higher. */ tmp = change_address (mem, DImode, gen_rtx_AND (DImode, plus_constant (mema, ofs), GEN_INT (-8))); set_mem_alias_set (tmp, 0); emit_move_insn (meml, tmp); tmp = change_address (mem, DImode, gen_rtx_AND (DImode, plus_constant (mema, ofs + size - 1), GEN_INT (-8))); set_mem_alias_set (tmp, 0); emit_move_insn (memh, tmp); if (WORDS_BIG_ENDIAN && sign && (size == 2 || size == 4)) { emit_move_insn (addr, plus_constant (mema, -1)); emit_insn (gen_extqh_be (extl, meml, addr)); emit_insn (gen_extxl_be (exth, memh, GEN_INT (64), addr)); addr = expand_binop (DImode, ior_optab, extl, exth, tgt, 1, OPTAB_WIDEN); addr = expand_binop (DImode, ashr_optab, addr, GEN_INT (64 - size*8), addr, 1, OPTAB_WIDEN); } else if (sign && size == 2) { emit_move_insn (addr, plus_constant (mema, ofs+2)); emit_insn (gen_extxl_le (extl, meml, GEN_INT (64), addr)); emit_insn (gen_extqh_le (exth, memh, addr)); /* We must use tgt here for the target. Alpha-vms port fails if we use addr for the target, because addr is marked as a pointer and combine knows that pointers are always sign-extended 32 bit values. */ addr = expand_binop (DImode, ior_optab, extl, exth, tgt, 1, OPTAB_WIDEN); addr = expand_binop (DImode, ashr_optab, addr, GEN_INT (48), addr, 1, OPTAB_WIDEN); } else { if (WORDS_BIG_ENDIAN) { emit_move_insn (addr, plus_constant (mema, ofs+size-1)); switch ((int) size) { case 2: emit_insn (gen_extwh_be (extl, meml, addr)); mode = HImode; break; case 4: emit_insn (gen_extlh_be (extl, meml, addr)); mode = SImode; break; case 8: emit_insn (gen_extqh_be (extl, meml, addr)); mode = DImode; break; default: abort (); } emit_insn (gen_extxl_be (exth, memh, GEN_INT (size*8), addr)); } else { emit_move_insn (addr, plus_constant (mema, ofs)); emit_insn (gen_extxl_le (extl, meml, GEN_INT (size*8), addr)); switch ((int) size) { case 2: emit_insn (gen_extwh_le (exth, memh, addr)); mode = HImode; break; case 4: emit_insn (gen_extlh_le (exth, memh, addr)); mode = SImode; break; case 8: emit_insn (gen_extqh_le (exth, memh, addr)); mode = DImode; break; default: abort(); } } addr = expand_binop (mode, ior_optab, gen_lowpart (mode, extl), gen_lowpart (mode, exth), gen_lowpart (mode, tgt), sign, OPTAB_WIDEN); } if (addr != tgt) emit_move_insn (tgt, gen_lowpart(GET_MODE (tgt), addr)); } /* Similarly, use ins and msk instructions to perform unaligned stores. */ void alpha_expand_unaligned_store (rtx dst, rtx src, HOST_WIDE_INT size, HOST_WIDE_INT ofs) { rtx dstl, dsth, addr, insl, insh, meml, memh, dsta; dstl = gen_reg_rtx (DImode); dsth = gen_reg_rtx (DImode); insl = gen_reg_rtx (DImode); insh = gen_reg_rtx (DImode); dsta = XEXP (dst, 0); if (GET_CODE (dsta) == LO_SUM) dsta = force_reg (Pmode, dsta); /* AND addresses cannot be in any alias set, since they may implicitly alias surrounding code. Ideally we'd have some alias set that covered all types except those with alignment 8 or higher. */ meml = change_address (dst, DImode, gen_rtx_AND (DImode, plus_constant (dsta, ofs), GEN_INT (-8))); set_mem_alias_set (meml, 0); memh = change_address (dst, DImode, gen_rtx_AND (DImode, plus_constant (dsta, ofs + size - 1), GEN_INT (-8))); set_mem_alias_set (memh, 0); emit_move_insn (dsth, memh); emit_move_insn (dstl, meml); if (WORDS_BIG_ENDIAN) { addr = copy_addr_to_reg (plus_constant (dsta, ofs+size-1)); if (src != const0_rtx) { switch ((int) size) { case 2: emit_insn (gen_inswl_be (insh, gen_lowpart (HImode,src), addr)); break; case 4: emit_insn (gen_insll_be (insh, gen_lowpart (SImode,src), addr)); break; case 8: emit_insn (gen_insql_be (insh, gen_lowpart (DImode,src), addr)); break; } emit_insn (gen_insxh (insl, gen_lowpart (DImode, src), GEN_INT (size*8), addr)); } switch ((int) size) { case 2: emit_insn (gen_mskxl_be (dsth, dsth, GEN_INT (0xffff), addr)); break; case 4: { rtx msk = immed_double_const (0xffffffff, 0, DImode); emit_insn (gen_mskxl_be (dsth, dsth, msk, addr)); break; } case 8: emit_insn (gen_mskxl_be (dsth, dsth, constm1_rtx, addr)); break; } emit_insn (gen_mskxh (dstl, dstl, GEN_INT (size*8), addr)); } else { addr = copy_addr_to_reg (plus_constant (dsta, ofs)); if (src != const0_rtx) { emit_insn (gen_insxh (insh, gen_lowpart (DImode, src), GEN_INT (size*8), addr)); switch ((int) size) { case 2: emit_insn (gen_inswl_le (insl, gen_lowpart (HImode, src), addr)); break; case 4: emit_insn (gen_insll_le (insl, gen_lowpart (SImode, src), addr)); break; case 8: emit_insn (gen_insql_le (insl, src, addr)); break; } } emit_insn (gen_mskxh (dsth, dsth, GEN_INT (size*8), addr)); switch ((int) size) { case 2: emit_insn (gen_mskxl_le (dstl, dstl, GEN_INT (0xffff), addr)); break; case 4: { rtx msk = immed_double_const (0xffffffff, 0, DImode); emit_insn (gen_mskxl_le (dstl, dstl, msk, addr)); break; } case 8: emit_insn (gen_mskxl_le (dstl, dstl, constm1_rtx, addr)); break; } } if (src != const0_rtx) { dsth = expand_binop (DImode, ior_optab, insh, dsth, dsth, 0, OPTAB_WIDEN); dstl = expand_binop (DImode, ior_optab, insl, dstl, dstl, 0, OPTAB_WIDEN); } if (WORDS_BIG_ENDIAN) { emit_move_insn (meml, dstl); emit_move_insn (memh, dsth); } else { /* Must store high before low for degenerate case of aligned. */ emit_move_insn (memh, dsth); emit_move_insn (meml, dstl); } } /* The block move code tries to maximize speed by separating loads and stores at the expense of register pressure: we load all of the data before we store it back out. There are two secondary effects worth mentioning, that this speeds copying to/from aligned and unaligned buffers, and that it makes the code significantly easier to write. */ #define MAX_MOVE_WORDS 8 /* Load an integral number of consecutive unaligned quadwords. */ static void alpha_expand_unaligned_load_words (rtx *out_regs, rtx smem, HOST_WIDE_INT words, HOST_WIDE_INT ofs) { rtx const im8 = GEN_INT (-8); rtx const i64 = GEN_INT (64); rtx ext_tmps[MAX_MOVE_WORDS], data_regs[MAX_MOVE_WORDS+1]; rtx sreg, areg, tmp, smema; HOST_WIDE_INT i; smema = XEXP (smem, 0); if (GET_CODE (smema) == LO_SUM) smema = force_reg (Pmode, smema); /* Generate all the tmp registers we need. */ for (i = 0; i < words; ++i) { data_regs[i] = out_regs[i]; ext_tmps[i] = gen_reg_rtx (DImode); } data_regs[words] = gen_reg_rtx (DImode); if (ofs != 0) smem = adjust_address (smem, GET_MODE (smem), ofs); /* Load up all of the source data. */ for (i = 0; i < words; ++i) { tmp = change_address (smem, DImode, gen_rtx_AND (DImode, plus_constant (smema, 8*i), im8)); set_mem_alias_set (tmp, 0); emit_move_insn (data_regs[i], tmp); } tmp = change_address (smem, DImode, gen_rtx_AND (DImode, plus_constant (smema, 8*words - 1), im8)); set_mem_alias_set (tmp, 0); emit_move_insn (data_regs[words], tmp); /* Extract the half-word fragments. Unfortunately DEC decided to make extxh with offset zero a noop instead of zeroing the register, so we must take care of that edge condition ourselves with cmov. */ sreg = copy_addr_to_reg (smema); areg = expand_binop (DImode, and_optab, sreg, GEN_INT (7), NULL, 1, OPTAB_WIDEN); if (WORDS_BIG_ENDIAN) emit_move_insn (sreg, plus_constant (sreg, 7)); for (i = 0; i < words; ++i) { if (WORDS_BIG_ENDIAN) { emit_insn (gen_extqh_be (data_regs[i], data_regs[i], sreg)); emit_insn (gen_extxl_be (ext_tmps[i], data_regs[i+1], i64, sreg)); } else { emit_insn (gen_extxl_le (data_regs[i], data_regs[i], i64, sreg)); emit_insn (gen_extqh_le (ext_tmps[i], data_regs[i+1], sreg)); } emit_insn (gen_rtx_SET (VOIDmode, ext_tmps[i], gen_rtx_IF_THEN_ELSE (DImode, gen_rtx_EQ (DImode, areg, const0_rtx), const0_rtx, ext_tmps[i]))); } /* Merge the half-words into whole words. */ for (i = 0; i < words; ++i) { out_regs[i] = expand_binop (DImode, ior_optab, data_regs[i], ext_tmps[i], data_regs[i], 1, OPTAB_WIDEN); } } /* Store an integral number of consecutive unaligned quadwords. DATA_REGS may be NULL to store zeros. */ static void alpha_expand_unaligned_store_words (rtx *data_regs, rtx dmem, HOST_WIDE_INT words, HOST_WIDE_INT ofs) { rtx const im8 = GEN_INT (-8); rtx const i64 = GEN_INT (64); rtx ins_tmps[MAX_MOVE_WORDS]; rtx st_tmp_1, st_tmp_2, dreg; rtx st_addr_1, st_addr_2, dmema; HOST_WIDE_INT i; dmema = XEXP (dmem, 0); if (GET_CODE (dmema) == LO_SUM) dmema = force_reg (Pmode, dmema); /* Generate all the tmp registers we need. */ if (data_regs != NULL) for (i = 0; i < words; ++i) ins_tmps[i] = gen_reg_rtx(DImode); st_tmp_1 = gen_reg_rtx(DImode); st_tmp_2 = gen_reg_rtx(DImode); if (ofs != 0) dmem = adjust_address (dmem, GET_MODE (dmem), ofs); st_addr_2 = change_address (dmem, DImode, gen_rtx_AND (DImode, plus_constant (dmema, words*8 - 1), im8)); set_mem_alias_set (st_addr_2, 0); st_addr_1 = change_address (dmem, DImode, gen_rtx_AND (DImode, dmema, im8)); set_mem_alias_set (st_addr_1, 0); /* Load up the destination end bits. */ emit_move_insn (st_tmp_2, st_addr_2); emit_move_insn (st_tmp_1, st_addr_1); /* Shift the input data into place. */ dreg = copy_addr_to_reg (dmema); if (WORDS_BIG_ENDIAN) emit_move_insn (dreg, plus_constant (dreg, 7)); if (data_regs != NULL) { for (i = words-1; i >= 0; --i) { if (WORDS_BIG_ENDIAN) { emit_insn (gen_insql_be (ins_tmps[i], data_regs[i], dreg)); emit_insn (gen_insxh (data_regs[i], data_regs[i], i64, dreg)); } else { emit_insn (gen_insxh (ins_tmps[i], data_regs[i], i64, dreg)); emit_insn (gen_insql_le (data_regs[i], data_regs[i], dreg)); } } for (i = words-1; i > 0; --i) { ins_tmps[i-1] = expand_binop (DImode, ior_optab, data_regs[i], ins_tmps[i-1], ins_tmps[i-1], 1, OPTAB_WIDEN); } } /* Split and merge the ends with the destination data. */ if (WORDS_BIG_ENDIAN) { emit_insn (gen_mskxl_be (st_tmp_2, st_tmp_2, constm1_rtx, dreg)); emit_insn (gen_mskxh (st_tmp_1, st_tmp_1, i64, dreg)); } else { emit_insn (gen_mskxh (st_tmp_2, st_tmp_2, i64, dreg)); emit_insn (gen_mskxl_le (st_tmp_1, st_tmp_1, constm1_rtx, dreg)); } if (data_regs != NULL) { st_tmp_2 = expand_binop (DImode, ior_optab, st_tmp_2, ins_tmps[words-1], st_tmp_2, 1, OPTAB_WIDEN); st_tmp_1 = expand_binop (DImode, ior_optab, st_tmp_1, data_regs[0], st_tmp_1, 1, OPTAB_WIDEN); } /* Store it all. */ if (WORDS_BIG_ENDIAN) emit_move_insn (st_addr_1, st_tmp_1); else emit_move_insn (st_addr_2, st_tmp_2); for (i = words-1; i > 0; --i) { rtx tmp = change_address (dmem, DImode, gen_rtx_AND (DImode, plus_constant(dmema, WORDS_BIG_ENDIAN ? i*8-1 : i*8), im8)); set_mem_alias_set (tmp, 0); emit_move_insn (tmp, data_regs ? ins_tmps[i-1] : const0_rtx); } if (WORDS_BIG_ENDIAN) emit_move_insn (st_addr_2, st_tmp_2); else emit_move_insn (st_addr_1, st_tmp_1); } /* Expand string/block move operations. operands[0] is the pointer to the destination. operands[1] is the pointer to the source. operands[2] is the number of bytes to move. operands[3] is the alignment. */ int alpha_expand_block_move (rtx operands[]) { rtx bytes_rtx = operands[2]; rtx align_rtx = operands[3]; HOST_WIDE_INT orig_bytes = INTVAL (bytes_rtx); HOST_WIDE_INT bytes = orig_bytes; HOST_WIDE_INT src_align = INTVAL (align_rtx) * BITS_PER_UNIT; HOST_WIDE_INT dst_align = src_align; rtx orig_src = operands[1]; rtx orig_dst = operands[0]; rtx data_regs[2 * MAX_MOVE_WORDS + 16]; rtx tmp; unsigned int i, words, ofs, nregs = 0; if (orig_bytes <= 0) return 1; else if (orig_bytes > MAX_MOVE_WORDS * UNITS_PER_WORD) return 0; /* Look for additional alignment information from recorded register info. */ tmp = XEXP (orig_src, 0); if (GET_CODE (tmp) == REG) src_align = MAX (src_align, REGNO_POINTER_ALIGN (REGNO (tmp))); else if (GET_CODE (tmp) == PLUS && GET_CODE (XEXP (tmp, 0)) == REG && GET_CODE (XEXP (tmp, 1)) == CONST_INT) { unsigned HOST_WIDE_INT c = INTVAL (XEXP (tmp, 1)); unsigned int a = REGNO_POINTER_ALIGN (REGNO (XEXP (tmp, 0))); if (a > src_align) { if (a >= 64 && c % 8 == 0) src_align = 64; else if (a >= 32 && c % 4 == 0) src_align = 32; else if (a >= 16 && c % 2 == 0) src_align = 16; } } tmp = XEXP (orig_dst, 0); if (GET_CODE (tmp) == REG) dst_align = MAX (dst_align, REGNO_POINTER_ALIGN (REGNO (tmp))); else if (GET_CODE (tmp) == PLUS && GET_CODE (XEXP (tmp, 0)) == REG && GET_CODE (XEXP (tmp, 1)) == CONST_INT) { unsigned HOST_WIDE_INT c = INTVAL (XEXP (tmp, 1)); unsigned int a = REGNO_POINTER_ALIGN (REGNO (XEXP (tmp, 0))); if (a > dst_align) { if (a >= 64 && c % 8 == 0) dst_align = 64; else if (a >= 32 && c % 4 == 0) dst_align = 32; else if (a >= 16 && c % 2 == 0) dst_align = 16; } } /* Load the entire block into registers. */ if (GET_CODE (XEXP (orig_src, 0)) == ADDRESSOF) { enum machine_mode mode; tmp = XEXP (XEXP (orig_src, 0), 0); /* Don't use the existing register if we're reading more than is held in the register. Nor if there is not a mode that handles the exact size. */ mode = mode_for_size (bytes * BITS_PER_UNIT, MODE_INT, 1); if (GET_CODE (tmp) == REG && mode != BLKmode && GET_MODE_SIZE (GET_MODE (tmp)) >= bytes) { if (mode == TImode) { data_regs[nregs] = gen_lowpart (DImode, tmp); data_regs[nregs + 1] = gen_highpart (DImode, tmp); nregs += 2; } else data_regs[nregs++] = gen_lowpart (mode, tmp); goto src_done; } /* No appropriate mode; fall back on memory. */ orig_src = replace_equiv_address (orig_src, copy_addr_to_reg (XEXP (orig_src, 0))); src_align = GET_MODE_BITSIZE (GET_MODE (tmp)); } ofs = 0; if (src_align >= 64 && bytes >= 8) { words = bytes / 8; for (i = 0; i < words; ++i) data_regs[nregs + i] = gen_reg_rtx (DImode); for (i = 0; i < words; ++i) emit_move_insn (data_regs[nregs + i], adjust_address (orig_src, DImode, ofs + i * 8)); nregs += words; bytes -= words * 8; ofs += words * 8; } if (src_align >= 32 && bytes >= 4) { words = bytes / 4; for (i = 0; i < words; ++i) data_regs[nregs + i] = gen_reg_rtx (SImode); for (i = 0; i < words; ++i) emit_move_insn (data_regs[nregs + i], adjust_address (orig_src, SImode, ofs + i * 4)); nregs += words; bytes -= words * 4; ofs += words * 4; } if (bytes >= 8) { words = bytes / 8; for (i = 0; i < words+1; ++i) data_regs[nregs + i] = gen_reg_rtx (DImode); alpha_expand_unaligned_load_words (data_regs + nregs, orig_src, words, ofs); nregs += words; bytes -= words * 8; ofs += words * 8; } if (! TARGET_BWX && bytes >= 4) { data_regs[nregs++] = tmp = gen_reg_rtx (SImode); alpha_expand_unaligned_load (tmp, orig_src, 4, ofs, 0); bytes -= 4; ofs += 4; } if (bytes >= 2) { if (src_align >= 16) { do { data_regs[nregs++] = tmp = gen_reg_rtx (HImode); emit_move_insn (tmp, adjust_address (orig_src, HImode, ofs)); bytes -= 2; ofs += 2; } while (bytes >= 2); } else if (! TARGET_BWX) { data_regs[nregs++] = tmp = gen_reg_rtx (HImode); alpha_expand_unaligned_load (tmp, orig_src, 2, ofs, 0); bytes -= 2; ofs += 2; } } while (bytes > 0) { data_regs[nregs++] = tmp = gen_reg_rtx (QImode); emit_move_insn (tmp, adjust_address (orig_src, QImode, ofs)); bytes -= 1; ofs += 1; } src_done: if (nregs > ARRAY_SIZE (data_regs)) abort (); /* Now save it back out again. */ i = 0, ofs = 0; if (GET_CODE (XEXP (orig_dst, 0)) == ADDRESSOF) { enum machine_mode mode; tmp = XEXP (XEXP (orig_dst, 0), 0); mode = mode_for_size (orig_bytes * BITS_PER_UNIT, MODE_INT, 1); if (GET_CODE (tmp) == REG && GET_MODE (tmp) == mode) { if (nregs == 1) { emit_move_insn (tmp, data_regs[0]); i = 1; goto dst_done; } else if (nregs == 2 && mode == TImode) { /* Undo the subregging done above when copying between two TImode registers. */ if (GET_CODE (data_regs[0]) == SUBREG && GET_MODE (SUBREG_REG (data_regs[0])) == TImode) emit_move_insn (tmp, SUBREG_REG (data_regs[0])); else { rtx seq; start_sequence (); emit_move_insn (gen_lowpart (DImode, tmp), data_regs[0]); emit_move_insn (gen_highpart (DImode, tmp), data_regs[1]); seq = get_insns (); end_sequence (); emit_no_conflict_block (seq, tmp, data_regs[0], data_regs[1], NULL_RTX); } i = 2; goto dst_done; } } /* ??? If nregs > 1, consider reconstructing the word in regs. */ /* ??? Optimize mode < dst_mode with strict_low_part. */ /* No appropriate mode; fall back on memory. We can speed things up by recognizing extra alignment information. */ orig_dst = replace_equiv_address (orig_dst, copy_addr_to_reg (XEXP (orig_dst, 0))); dst_align = GET_MODE_BITSIZE (GET_MODE (tmp)); } /* Write out the data in whatever chunks reading the source allowed. */ if (dst_align >= 64) { while (i < nregs && GET_MODE (data_regs[i]) == DImode) { emit_move_insn (adjust_address (orig_dst, DImode, ofs), data_regs[i]); ofs += 8; i++; } } if (dst_align >= 32) { /* If the source has remaining DImode regs, write them out in two pieces. */ while (i < nregs && GET_MODE (data_regs[i]) == DImode) { tmp = expand_binop (DImode, lshr_optab, data_regs[i], GEN_INT (32), NULL_RTX, 1, OPTAB_WIDEN); emit_move_insn (adjust_address (orig_dst, SImode, ofs), gen_lowpart (SImode, data_regs[i])); emit_move_insn (adjust_address (orig_dst, SImode, ofs + 4), gen_lowpart (SImode, tmp)); ofs += 8; i++; } while (i < nregs && GET_MODE (data_regs[i]) == SImode) { emit_move_insn (adjust_address (orig_dst, SImode, ofs), data_regs[i]); ofs += 4; i++; } } if (i < nregs && GET_MODE (data_regs[i]) == DImode) { /* Write out a remaining block of words using unaligned methods. */ for (words = 1; i + words < nregs; words++) if (GET_MODE (data_regs[i + words]) != DImode) break; if (words == 1) alpha_expand_unaligned_store (orig_dst, data_regs[i], 8, ofs); else alpha_expand_unaligned_store_words (data_regs + i, orig_dst, words, ofs); i += words; ofs += words * 8; } /* Due to the above, this won't be aligned. */ /* ??? If we have more than one of these, consider constructing full words in registers and using alpha_expand_unaligned_store_words. */ while (i < nregs && GET_MODE (data_regs[i]) == SImode) { alpha_expand_unaligned_store (orig_dst, data_regs[i], 4, ofs); ofs += 4; i++; } if (dst_align >= 16) while (i < nregs && GET_MODE (data_regs[i]) == HImode) { emit_move_insn (adjust_address (orig_dst, HImode, ofs), data_regs[i]); i++; ofs += 2; } else while (i < nregs && GET_MODE (data_regs[i]) == HImode) { alpha_expand_unaligned_store (orig_dst, data_regs[i], 2, ofs); i++; ofs += 2; } while (i < nregs && GET_MODE (data_regs[i]) == QImode) { emit_move_insn (adjust_address (orig_dst, QImode, ofs), data_regs[i]); i++; ofs += 1; } dst_done: if (i != nregs) abort (); return 1; } int alpha_expand_block_clear (rtx operands[]) { rtx bytes_rtx = operands[1]; rtx align_rtx = operands[2]; HOST_WIDE_INT orig_bytes = INTVAL (bytes_rtx); HOST_WIDE_INT bytes = orig_bytes; HOST_WIDE_INT align = INTVAL (align_rtx) * BITS_PER_UNIT; HOST_WIDE_INT alignofs = 0; rtx orig_dst = operands[0]; rtx tmp; int i, words, ofs = 0; if (orig_bytes <= 0) return 1; if (orig_bytes > MAX_MOVE_WORDS * UNITS_PER_WORD) return 0; /* Look for stricter alignment. */ tmp = XEXP (orig_dst, 0); if (GET_CODE (tmp) == REG) align = MAX (align, REGNO_POINTER_ALIGN (REGNO (tmp))); else if (GET_CODE (tmp) == PLUS && GET_CODE (XEXP (tmp, 0)) == REG && GET_CODE (XEXP (tmp, 1)) == CONST_INT) { HOST_WIDE_INT c = INTVAL (XEXP (tmp, 1)); int a = REGNO_POINTER_ALIGN (REGNO (XEXP (tmp, 0))); if (a > align) { if (a >= 64) align = a, alignofs = 8 - c % 8; else if (a >= 32) align = a, alignofs = 4 - c % 4; else if (a >= 16) align = a, alignofs = 2 - c % 2; } } else if (GET_CODE (tmp) == ADDRESSOF) { enum machine_mode mode; mode = mode_for_size (bytes * BITS_PER_UNIT, MODE_INT, 1); if (GET_MODE (XEXP (tmp, 0)) == mode) { emit_move_insn (XEXP (tmp, 0), const0_rtx); return 1; } /* No appropriate mode; fall back on memory. */ orig_dst = replace_equiv_address (orig_dst, copy_addr_to_reg (tmp)); align = GET_MODE_BITSIZE (GET_MODE (XEXP (tmp, 0))); } /* Handle an unaligned prefix first. */ if (alignofs > 0) { #if HOST_BITS_PER_WIDE_INT >= 64 /* Given that alignofs is bounded by align, the only time BWX could generate three stores is for a 7 byte fill. Prefer two individual stores over a load/mask/store sequence. */ if ((!TARGET_BWX || alignofs == 7) && align >= 32 && !(alignofs == 4 && bytes >= 4)) { enum machine_mode mode = (align >= 64 ? DImode : SImode); int inv_alignofs = (align >= 64 ? 8 : 4) - alignofs; rtx mem, tmp; HOST_WIDE_INT mask; mem = adjust_address (orig_dst, mode, ofs - inv_alignofs); set_mem_alias_set (mem, 0); mask = ~(~(HOST_WIDE_INT)0 << (inv_alignofs * 8)); if (bytes < alignofs) { mask |= ~(HOST_WIDE_INT)0 << ((inv_alignofs + bytes) * 8); ofs += bytes; bytes = 0; } else { bytes -= alignofs; ofs += alignofs; } alignofs = 0; tmp = expand_binop (mode, and_optab, mem, GEN_INT (mask), NULL_RTX, 1, OPTAB_WIDEN); emit_move_insn (mem, tmp); } #endif if (TARGET_BWX && (alignofs & 1) && bytes >= 1) { emit_move_insn (adjust_address (orig_dst, QImode, ofs), const0_rtx); bytes -= 1; ofs += 1; alignofs -= 1; } if (TARGET_BWX && align >= 16 && (alignofs & 3) == 2 && bytes >= 2) { emit_move_insn (adjust_address (orig_dst, HImode, ofs), const0_rtx); bytes -= 2; ofs += 2; alignofs -= 2; } if (alignofs == 4 && bytes >= 4) { emit_move_insn (adjust_address (orig_dst, SImode, ofs), const0_rtx); bytes -= 4; ofs += 4; alignofs = 0; } /* If we've not used the extra lead alignment information by now, we won't be able to. Downgrade align to match what's left over. */ if (alignofs > 0) { alignofs = alignofs & -alignofs; align = MIN (align, alignofs * BITS_PER_UNIT); } } /* Handle a block of contiguous long-words. */ if (align >= 64 && bytes >= 8) { words = bytes / 8; for (i = 0; i < words; ++i) emit_move_insn (adjust_address (orig_dst, DImode, ofs + i * 8), const0_rtx); bytes -= words * 8; ofs += words * 8; } /* If the block is large and appropriately aligned, emit a single store followed by a sequence of stq_u insns. */ if (align >= 32 && bytes > 16) { rtx orig_dsta; emit_move_insn (adjust_address (orig_dst, SImode, ofs), const0_rtx); bytes -= 4; ofs += 4; orig_dsta = XEXP (orig_dst, 0); if (GET_CODE (orig_dsta) == LO_SUM) orig_dsta = force_reg (Pmode, orig_dsta); words = bytes / 8; for (i = 0; i < words; ++i) { rtx mem = change_address (orig_dst, DImode, gen_rtx_AND (DImode, plus_constant (orig_dsta, ofs + i*8), GEN_INT (-8))); set_mem_alias_set (mem, 0); emit_move_insn (mem, const0_rtx); } /* Depending on the alignment, the first stq_u may have overlapped with the initial stl, which means that the last stq_u didn't write as much as it would appear. Leave those questionable bytes unaccounted for. */ bytes -= words * 8 - 4; ofs += words * 8 - 4; } /* Handle a smaller block of aligned words. */ if ((align >= 64 && bytes == 4) || (align == 32 && bytes >= 4)) { words = bytes / 4; for (i = 0; i < words; ++i) emit_move_insn (adjust_address (orig_dst, SImode, ofs + i * 4), const0_rtx); bytes -= words * 4; ofs += words * 4; } /* An unaligned block uses stq_u stores for as many as possible. */ if (bytes >= 8) { words = bytes / 8; alpha_expand_unaligned_store_words (NULL, orig_dst, words, ofs); bytes -= words * 8; ofs += words * 8; } /* Next clean up any trailing pieces. */ #if HOST_BITS_PER_WIDE_INT >= 64 /* Count the number of bits in BYTES for which aligned stores could be emitted. */ words = 0; for (i = (TARGET_BWX ? 1 : 4); i * BITS_PER_UNIT <= align ; i <<= 1) if (bytes & i) words += 1; /* If we have appropriate alignment (and it wouldn't take too many instructions otherwise), mask out the bytes we need. */ if (TARGET_BWX ? words > 2 : bytes > 0) { if (align >= 64) { rtx mem, tmp; HOST_WIDE_INT mask; mem = adjust_address (orig_dst, DImode, ofs); set_mem_alias_set (mem, 0); mask = ~(HOST_WIDE_INT)0 << (bytes * 8); tmp = expand_binop (DImode, and_optab, mem, GEN_INT (mask), NULL_RTX, 1, OPTAB_WIDEN); emit_move_insn (mem, tmp); return 1; } else if (align >= 32 && bytes < 4) { rtx mem, tmp; HOST_WIDE_INT mask; mem = adjust_address (orig_dst, SImode, ofs); set_mem_alias_set (mem, 0); mask = ~(HOST_WIDE_INT)0 << (bytes * 8); tmp = expand_binop (SImode, and_optab, mem, GEN_INT (mask), NULL_RTX, 1, OPTAB_WIDEN); emit_move_insn (mem, tmp); return 1; } } #endif if (!TARGET_BWX && bytes >= 4) { alpha_expand_unaligned_store (orig_dst, const0_rtx, 4, ofs); bytes -= 4; ofs += 4; } if (bytes >= 2) { if (align >= 16) { do { emit_move_insn (adjust_address (orig_dst, HImode, ofs), const0_rtx); bytes -= 2; ofs += 2; } while (bytes >= 2); } else if (! TARGET_BWX) { alpha_expand_unaligned_store (orig_dst, const0_rtx, 2, ofs); bytes -= 2; ofs += 2; } } while (bytes > 0) { emit_move_insn (adjust_address (orig_dst, QImode, ofs), const0_rtx); bytes -= 1; ofs += 1; } return 1; } /* Returns a mask so that zap(x, value) == x & mask. */ rtx alpha_expand_zap_mask (HOST_WIDE_INT value) { rtx result; int i; if (HOST_BITS_PER_WIDE_INT >= 64) { HOST_WIDE_INT mask = 0; for (i = 7; i >= 0; --i) { mask <<= 8; if (!((value >> i) & 1)) mask |= 0xff; } result = gen_int_mode (mask, DImode); } else if (HOST_BITS_PER_WIDE_INT == 32) { HOST_WIDE_INT mask_lo = 0, mask_hi = 0; for (i = 7; i >= 4; --i) { mask_hi <<= 8; if (!((value >> i) & 1)) mask_hi |= 0xff; } for (i = 3; i >= 0; --i) { mask_lo <<= 8; if (!((value >> i) & 1)) mask_lo |= 0xff; } result = immed_double_const (mask_lo, mask_hi, DImode); } else abort (); return result; } void alpha_expand_builtin_vector_binop (rtx (*gen) (rtx, rtx, rtx), enum machine_mode mode, rtx op0, rtx op1, rtx op2) { op0 = gen_lowpart (mode, op0); if (op1 == const0_rtx) op1 = CONST0_RTX (mode); else op1 = gen_lowpart (mode, op1); if (op2 == const0_rtx) op2 = CONST0_RTX (mode); else op2 = gen_lowpart (mode, op2); emit_insn ((*gen) (op0, op1, op2)); } /* Adjust the cost of a scheduling dependency. Return the new cost of a dependency LINK or INSN on DEP_INSN. COST is the current cost. */ static int alpha_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost) { enum attr_type insn_type, dep_insn_type; /* If the dependence is an anti-dependence, there is no cost. For an output dependence, there is sometimes a cost, but it doesn't seem worth handling those few cases. */ if (REG_NOTE_KIND (link) != 0) return cost; /* If we can't recognize the insns, we can't really do anything. */ if (recog_memoized (insn) < 0 || recog_memoized (dep_insn) < 0) return cost; insn_type = get_attr_type (insn); dep_insn_type = get_attr_type (dep_insn); /* Bring in the user-defined memory latency. */ if (dep_insn_type == TYPE_ILD || dep_insn_type == TYPE_FLD || dep_insn_type == TYPE_LDSYM) cost += alpha_memory_latency-1; /* Everything else handled in DFA bypasses now. */ return cost; } /* The number of instructions that can be issued per cycle. */ static int alpha_issue_rate (void) { return (alpha_cpu == PROCESSOR_EV4 ? 2 : 4); } static int alpha_use_dfa_pipeline_interface (void) { return true; } /* How many alternative schedules to try. This should be as wide as the scheduling freedom in the DFA, but no wider. Making this value too large results extra work for the scheduler. For EV4, loads can be issued to either IB0 or IB1, thus we have 2 alternative schedules. For EV5, we can choose between E0/E1 and FA/FM. For EV6, an arithmetic insn can be issued to U0/U1/L0/L1. */ static int alpha_multipass_dfa_lookahead (void) { return (alpha_cpu == PROCESSOR_EV6 ? 4 : 2); } /* Machine-specific function data. */ struct machine_function GTY(()) { /* For unicosmk. */ /* List of call information words for calls from this function. */ struct rtx_def *first_ciw; struct rtx_def *last_ciw; int ciw_count; /* List of deferred case vectors. */ struct rtx_def *addr_list; /* For OSF. */ const char *some_ld_name; }; /* How to allocate a 'struct machine_function'. */ static struct machine_function * alpha_init_machine_status (void) { return ((struct machine_function *) ggc_alloc_cleared (sizeof (struct machine_function))); } /* Functions to save and restore alpha_return_addr_rtx. */ /* Start the ball rolling with RETURN_ADDR_RTX. */ rtx alpha_return_addr (int count, rtx frame ATTRIBUTE_UNUSED) { if (count != 0) return const0_rtx; return get_hard_reg_initial_val (Pmode, REG_RA); } /* Return or create a pseudo containing the gp value for the current function. Needed only if TARGET_LD_BUGGY_LDGP. */ rtx alpha_gp_save_rtx (void) { rtx r = get_hard_reg_initial_val (DImode, 29); if (GET_CODE (r) != MEM) r = gen_mem_addressof (r, NULL_TREE, /*rescan=*/true); return r; } static int alpha_ra_ever_killed (void) { rtx top; if (!has_hard_reg_initial_val (Pmode, REG_RA)) return regs_ever_live[REG_RA]; push_topmost_sequence (); top = get_insns (); pop_topmost_sequence (); return reg_set_between_p (gen_rtx_REG (Pmode, REG_RA), top, NULL_RTX); } /* Return the trap mode suffix applicable to the current instruction, or NULL. */ static const char * get_trap_mode_suffix (void) { enum attr_trap_suffix s = get_attr_trap_suffix (current_output_insn); switch (s) { case TRAP_SUFFIX_NONE: return NULL; case TRAP_SUFFIX_SU: if (alpha_fptm >= ALPHA_FPTM_SU) return "su"; return NULL; case TRAP_SUFFIX_SUI: if (alpha_fptm >= ALPHA_FPTM_SUI) return "sui"; return NULL; case TRAP_SUFFIX_V_SV: switch (alpha_fptm) { case ALPHA_FPTM_N: return NULL; case ALPHA_FPTM_U: return "v"; case ALPHA_FPTM_SU: case ALPHA_FPTM_SUI: return "sv"; } break; case TRAP_SUFFIX_V_SV_SVI: switch (alpha_fptm) { case ALPHA_FPTM_N: return NULL; case ALPHA_FPTM_U: return "v"; case ALPHA_FPTM_SU: return "sv"; case ALPHA_FPTM_SUI: return "svi"; } break; case TRAP_SUFFIX_U_SU_SUI: switch (alpha_fptm) { case ALPHA_FPTM_N: return NULL; case ALPHA_FPTM_U: return "u"; case ALPHA_FPTM_SU: return "su"; case ALPHA_FPTM_SUI: return "sui"; } break; } abort (); } /* Return the rounding mode suffix applicable to the current instruction, or NULL. */ static const char * get_round_mode_suffix (void) { enum attr_round_suffix s = get_attr_round_suffix (current_output_insn); switch (s) { case ROUND_SUFFIX_NONE: return NULL; case ROUND_SUFFIX_NORMAL: switch (alpha_fprm) { case ALPHA_FPRM_NORM: return NULL; case ALPHA_FPRM_MINF: return "m"; case ALPHA_FPRM_CHOP: return "c"; case ALPHA_FPRM_DYN: return "d"; } break; case ROUND_SUFFIX_C: return "c"; } abort (); } /* Locate some local-dynamic symbol still in use by this function so that we can print its name in some movdi_er_tlsldm pattern. */ static int get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED) { rtx x = *px; if (GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (x) == TLS_MODEL_LOCAL_DYNAMIC) { cfun->machine->some_ld_name = XSTR (x, 0); return 1; } return 0; } static const char * get_some_local_dynamic_name (void) { rtx insn; if (cfun->machine->some_ld_name) return cfun->machine->some_ld_name; for (insn = get_insns (); insn ; insn = NEXT_INSN (insn)) if (INSN_P (insn) && for_each_rtx (&PATTERN (insn), get_some_local_dynamic_name_1, 0)) return cfun->machine->some_ld_name; abort (); } /* Print an operand. Recognize special options, documented below. */ void print_operand (FILE *file, rtx x, int code) { int i; switch (code) { case '~': /* Print the assembler name of the current function. */ assemble_name (file, alpha_fnname); break; case '&': assemble_name (file, get_some_local_dynamic_name ()); break; case '/': { const char *trap = get_trap_mode_suffix (); const char *round = get_round_mode_suffix (); if (trap || round) fprintf (file, (TARGET_AS_SLASH_BEFORE_SUFFIX ? "/%s%s" : "%s%s"), (trap ? trap : ""), (round ? round : "")); break; } case ',': /* Generates single precision instruction suffix. */ fputc ((TARGET_FLOAT_VAX ? 'f' : 's'), file); break; case '-': /* Generates double precision instruction suffix. */ fputc ((TARGET_FLOAT_VAX ? 'g' : 't'), file); break; case '+': /* Generates a nop after a noreturn call at the very end of the function. */ if (next_real_insn (current_output_insn) == 0) fprintf (file, "\n\tnop"); break; case '#': if (alpha_this_literal_sequence_number == 0) alpha_this_literal_sequence_number = alpha_next_sequence_number++; fprintf (file, "%d", alpha_this_literal_sequence_number); break; case '*': if (alpha_this_gpdisp_sequence_number == 0) alpha_this_gpdisp_sequence_number = alpha_next_sequence_number++; fprintf (file, "%d", alpha_this_gpdisp_sequence_number); break; case 'H': if (GET_CODE (x) == HIGH) output_addr_const (file, XEXP (x, 0)); else output_operand_lossage ("invalid %%H value"); break; case 'J': { const char *lituse; if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSGD_CALL) { x = XVECEXP (x, 0, 0); lituse = "lituse_tlsgd"; } else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSLDM_CALL) { x = XVECEXP (x, 0, 0); lituse = "lituse_tlsldm"; } else if (GET_CODE (x) == CONST_INT) lituse = "lituse_jsr"; else { output_operand_lossage ("invalid %%J value"); break; } if (x != const0_rtx) fprintf (file, "\t\t!%s!%d", lituse, (int) INTVAL (x)); } break; + case 'j': + { + const char *lituse; + +#ifdef HAVE_AS_JSRDIRECT_RELOCS + lituse = "lituse_jsrdirect"; +#else + lituse = "lituse_jsr"; +#endif + + if (INTVAL (x) == 0) + abort (); + fprintf (file, "\t\t!%s!%d", lituse, (int) INTVAL (x)); + } + break; case 'r': /* If this operand is the constant zero, write it as "$31". */ if (GET_CODE (x) == REG) fprintf (file, "%s", reg_names[REGNO (x)]); else if (x == CONST0_RTX (GET_MODE (x))) fprintf (file, "$31"); else output_operand_lossage ("invalid %%r value"); break; case 'R': /* Similar, but for floating-point. */ if (GET_CODE (x) == REG) fprintf (file, "%s", reg_names[REGNO (x)]); else if (x == CONST0_RTX (GET_MODE (x))) fprintf (file, "$f31"); else output_operand_lossage ("invalid %%R value"); break; case 'N': /* Write the 1's complement of a constant. */ if (GET_CODE (x) != CONST_INT) output_operand_lossage ("invalid %%N value"); fprintf (file, HOST_WIDE_INT_PRINT_DEC, ~ INTVAL (x)); break; case 'P': /* Write 1 << C, for a constant C. */ if (GET_CODE (x) != CONST_INT) output_operand_lossage ("invalid %%P value"); fprintf (file, HOST_WIDE_INT_PRINT_DEC, (HOST_WIDE_INT) 1 << INTVAL (x)); break; case 'h': /* Write the high-order 16 bits of a constant, sign-extended. */ if (GET_CODE (x) != CONST_INT) output_operand_lossage ("invalid %%h value"); fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) >> 16); break; case 'L': /* Write the low-order 16 bits of a constant, sign-extended. */ if (GET_CODE (x) != CONST_INT) output_operand_lossage ("invalid %%L value"); fprintf (file, HOST_WIDE_INT_PRINT_DEC, (INTVAL (x) & 0xffff) - 2 * (INTVAL (x) & 0x8000)); break; case 'm': /* Write mask for ZAP insn. */ if (GET_CODE (x) == CONST_DOUBLE) { HOST_WIDE_INT mask = 0; HOST_WIDE_INT value; value = CONST_DOUBLE_LOW (x); for (i = 0; i < HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR; i++, value >>= 8) if (value & 0xff) mask |= (1 << i); value = CONST_DOUBLE_HIGH (x); for (i = 0; i < HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR; i++, value >>= 8) if (value & 0xff) mask |= (1 << (i + sizeof (int))); fprintf (file, HOST_WIDE_INT_PRINT_DEC, mask & 0xff); } else if (GET_CODE (x) == CONST_INT) { HOST_WIDE_INT mask = 0, value = INTVAL (x); for (i = 0; i < 8; i++, value >>= 8) if (value & 0xff) mask |= (1 << i); fprintf (file, HOST_WIDE_INT_PRINT_DEC, mask); } else output_operand_lossage ("invalid %%m value"); break; case 'M': /* 'b', 'w', 'l', or 'q' as the value of the constant. */ if (GET_CODE (x) != CONST_INT || (INTVAL (x) != 8 && INTVAL (x) != 16 && INTVAL (x) != 32 && INTVAL (x) != 64)) output_operand_lossage ("invalid %%M value"); fprintf (file, "%s", (INTVAL (x) == 8 ? "b" : INTVAL (x) == 16 ? "w" : INTVAL (x) == 32 ? "l" : "q")); break; case 'U': /* Similar, except do it from the mask. */ if (GET_CODE (x) == CONST_INT) { HOST_WIDE_INT value = INTVAL (x); if (value == 0xff) { fputc ('b', file); break; } if (value == 0xffff) { fputc ('w', file); break; } if (value == 0xffffffff) { fputc ('l', file); break; } if (value == -1) { fputc ('q', file); break; } } else if (HOST_BITS_PER_WIDE_INT == 32 && GET_CODE (x) == CONST_DOUBLE && CONST_DOUBLE_LOW (x) == 0xffffffff && CONST_DOUBLE_HIGH (x) == 0) { fputc ('l', file); break; } output_operand_lossage ("invalid %%U value"); break; case 's': /* Write the constant value divided by 8 for little-endian mode or (56 - value) / 8 for big-endian mode. */ if (GET_CODE (x) != CONST_INT || (unsigned HOST_WIDE_INT) INTVAL (x) >= (WORDS_BIG_ENDIAN ? 56 : 64) || (INTVAL (x) & 7) != 0) output_operand_lossage ("invalid %%s value"); fprintf (file, HOST_WIDE_INT_PRINT_DEC, WORDS_BIG_ENDIAN ? (56 - INTVAL (x)) / 8 : INTVAL (x) / 8); break; case 'S': /* Same, except compute (64 - c) / 8 */ if (GET_CODE (x) != CONST_INT && (unsigned HOST_WIDE_INT) INTVAL (x) >= 64 && (INTVAL (x) & 7) != 8) output_operand_lossage ("invalid %%s value"); fprintf (file, HOST_WIDE_INT_PRINT_DEC, (64 - INTVAL (x)) / 8); break; case 't': { /* On Unicos/Mk systems: use a DEX expression if the symbol clashes with a register name. */ int dex = unicosmk_need_dex (x); if (dex) fprintf (file, "DEX(%d)", dex); else output_addr_const (file, x); } break; case 'C': case 'D': case 'c': case 'd': /* Write out comparison name. */ { enum rtx_code c = GET_CODE (x); if (GET_RTX_CLASS (c) != '<') output_operand_lossage ("invalid %%C value"); else if (code == 'D') c = reverse_condition (c); else if (code == 'c') c = swap_condition (c); else if (code == 'd') c = swap_condition (reverse_condition (c)); if (c == LEU) fprintf (file, "ule"); else if (c == LTU) fprintf (file, "ult"); else if (c == UNORDERED) fprintf (file, "un"); else fprintf (file, "%s", GET_RTX_NAME (c)); } break; case 'E': /* Write the divide or modulus operator. */ switch (GET_CODE (x)) { case DIV: fprintf (file, "div%s", GET_MODE (x) == SImode ? "l" : "q"); break; case UDIV: fprintf (file, "div%su", GET_MODE (x) == SImode ? "l" : "q"); break; case MOD: fprintf (file, "rem%s", GET_MODE (x) == SImode ? "l" : "q"); break; case UMOD: fprintf (file, "rem%su", GET_MODE (x) == SImode ? "l" : "q"); break; default: output_operand_lossage ("invalid %%E value"); break; } break; case 'A': /* Write "_u" for unaligned access. */ if (GET_CODE (x) == MEM && GET_CODE (XEXP (x, 0)) == AND) fprintf (file, "_u"); break; case 0: if (GET_CODE (x) == REG) fprintf (file, "%s", reg_names[REGNO (x)]); else if (GET_CODE (x) == MEM) output_address (XEXP (x, 0)); else if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == UNSPEC) { switch (XINT (XEXP (x, 0), 1)) { case UNSPEC_DTPREL: case UNSPEC_TPREL: output_addr_const (file, XVECEXP (XEXP (x, 0), 0, 0)); break; default: output_operand_lossage ("unknown relocation unspec"); break; } } else output_addr_const (file, x); break; default: output_operand_lossage ("invalid %%xn code"); } } void print_operand_address (FILE *file, rtx addr) { int basereg = 31; HOST_WIDE_INT offset = 0; if (GET_CODE (addr) == AND) addr = XEXP (addr, 0); if (GET_CODE (addr) == PLUS && GET_CODE (XEXP (addr, 1)) == CONST_INT) { offset = INTVAL (XEXP (addr, 1)); addr = XEXP (addr, 0); } if (GET_CODE (addr) == LO_SUM) { const char *reloc16, *reloclo; rtx op1 = XEXP (addr, 1); if (GET_CODE (op1) == CONST && GET_CODE (XEXP (op1, 0)) == UNSPEC) { op1 = XEXP (op1, 0); switch (XINT (op1, 1)) { case UNSPEC_DTPREL: reloc16 = NULL; reloclo = (alpha_tls_size == 16 ? "dtprel" : "dtprello"); break; case UNSPEC_TPREL: reloc16 = NULL; reloclo = (alpha_tls_size == 16 ? "tprel" : "tprello"); break; default: output_operand_lossage ("unknown relocation unspec"); return; } output_addr_const (file, XVECEXP (op1, 0, 0)); } else { reloc16 = "gprel"; reloclo = "gprellow"; output_addr_const (file, op1); } if (offset) fprintf (file, "+" HOST_WIDE_INT_PRINT_DEC, offset); addr = XEXP (addr, 0); if (GET_CODE (addr) == REG) basereg = REGNO (addr); else if (GET_CODE (addr) == SUBREG && GET_CODE (SUBREG_REG (addr)) == REG) basereg = subreg_regno (addr); else abort (); fprintf (file, "($%d)\t\t!%s", basereg, (basereg == 29 ? reloc16 : reloclo)); return; } if (GET_CODE (addr) == REG) basereg = REGNO (addr); else if (GET_CODE (addr) == SUBREG && GET_CODE (SUBREG_REG (addr)) == REG) basereg = subreg_regno (addr); else if (GET_CODE (addr) == CONST_INT) offset = INTVAL (addr); #if TARGET_ABI_OPEN_VMS else if (GET_CODE (addr) == SYMBOL_REF) { fprintf (file, "%s", XSTR (addr, 0)); return; } else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == PLUS && GET_CODE (XEXP (XEXP (addr, 0), 0)) == SYMBOL_REF) { fprintf (file, "%s+" HOST_WIDE_INT_PRINT_DEC, XSTR (XEXP (XEXP (addr, 0), 0), 0), INTVAL (XEXP (XEXP (addr, 0), 1))); return; } #endif else abort (); fprintf (file, HOST_WIDE_INT_PRINT_DEC "($%d)", offset, basereg); } /* Emit RTL insns to initialize the variable parts of a trampoline at TRAMP. FNADDR is an RTX for the address of the function's pure code. CXT is an RTX for the static chain value for the function. The three offset parameters are for the individual template's layout. A JMPOFS < 0 indicates that the trampoline does not contain instructions at all. We assume here that a function will be called many more times than its address is taken (e.g., it might be passed to qsort), so we take the trouble to initialize the "hint" field in the JMP insn. Note that the hint field is PC (new) + 4 * bits 13:0. */ void alpha_initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt, int fnofs, int cxtofs, int jmpofs) { rtx temp, temp1, addr; /* VMS really uses DImode pointers in memory at this point. */ enum machine_mode mode = TARGET_ABI_OPEN_VMS ? Pmode : ptr_mode; #ifdef POINTERS_EXTEND_UNSIGNED fnaddr = convert_memory_address (mode, fnaddr); cxt = convert_memory_address (mode, cxt); #endif /* Store function address and CXT. */ addr = memory_address (mode, plus_constant (tramp, fnofs)); emit_move_insn (gen_rtx_MEM (mode, addr), fnaddr); addr = memory_address (mode, plus_constant (tramp, cxtofs)); emit_move_insn (gen_rtx_MEM (mode, addr), cxt); /* This has been disabled since the hint only has a 32k range, and in no existing OS is the stack within 32k of the text segment. */ if (0 && jmpofs >= 0) { /* Compute hint value. */ temp = force_operand (plus_constant (tramp, jmpofs+4), NULL_RTX); temp = expand_binop (DImode, sub_optab, fnaddr, temp, temp, 1, OPTAB_WIDEN); temp = expand_shift (RSHIFT_EXPR, Pmode, temp, build_int_2 (2, 0), NULL_RTX, 1); temp = expand_and (SImode, gen_lowpart (SImode, temp), GEN_INT (0x3fff), 0); /* Merge in the hint. */ addr = memory_address (SImode, plus_constant (tramp, jmpofs)); temp1 = force_reg (SImode, gen_rtx_MEM (SImode, addr)); temp1 = expand_and (SImode, temp1, GEN_INT (0xffffc000), NULL_RTX); temp1 = expand_binop (SImode, ior_optab, temp1, temp, temp1, 1, OPTAB_WIDEN); emit_move_insn (gen_rtx_MEM (SImode, addr), temp1); } #ifdef ENABLE_EXECUTE_STACK emit_library_call (init_one_libfunc ("__enable_execute_stack"), 0, VOIDmode, 1, tramp, Pmode); #endif if (jmpofs >= 0) emit_insn (gen_imb ()); } /* Determine where to put an argument 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). On Alpha the first 6 words of args are normally in registers and the rest are pushed. */ rtx function_arg (CUMULATIVE_ARGS cum, enum machine_mode mode, tree type, int named ATTRIBUTE_UNUSED) { int basereg; int num_args; /* Don't get confused and pass small structures in FP registers. */ if (type && AGGREGATE_TYPE_P (type)) basereg = 16; else { #ifdef ENABLE_CHECKING /* With alpha_split_complex_arg, we shouldn't see any raw complex values here. */ if (COMPLEX_MODE_P (mode)) abort (); #endif /* Set up defaults for FP operands passed in FP registers, and integral operands passed in integer registers. */ if (TARGET_FPREGS && GET_MODE_CLASS (mode) == MODE_FLOAT) basereg = 32 + 16; else basereg = 16; } /* ??? Irritatingly, the definition of CUMULATIVE_ARGS is different for the three platforms, so we can't avoid conditional compilation. */ #if TARGET_ABI_OPEN_VMS { if (mode == VOIDmode) return alpha_arg_info_reg_val (cum); num_args = cum.num_args; if (num_args >= 6 || MUST_PASS_IN_STACK (mode, type)) return NULL_RTX; } #elif TARGET_ABI_UNICOSMK { int size; /* If this is the last argument, generate the call info word (CIW). */ /* ??? We don't include the caller's line number in the CIW because I don't know how to determine it if debug infos are turned off. */ if (mode == VOIDmode) { int i; HOST_WIDE_INT lo; HOST_WIDE_INT hi; rtx ciw; lo = 0; for (i = 0; i < cum.num_reg_words && i < 5; i++) if (cum.reg_args_type[i]) lo |= (1 << (7 - i)); if (cum.num_reg_words == 6 && cum.reg_args_type[5]) lo |= 7; else lo |= cum.num_reg_words; #if HOST_BITS_PER_WIDE_INT == 32 hi = (cum.num_args << 20) | cum.num_arg_words; #else lo = lo | ((HOST_WIDE_INT) cum.num_args << 52) | ((HOST_WIDE_INT) cum.num_arg_words << 32); hi = 0; #endif ciw = immed_double_const (lo, hi, DImode); return gen_rtx_UNSPEC (DImode, gen_rtvec (1, ciw), UNSPEC_UMK_LOAD_CIW); } size = ALPHA_ARG_SIZE (mode, type, named); num_args = cum.num_reg_words; if (MUST_PASS_IN_STACK (mode, type) || cum.num_reg_words + size > 6 || cum.force_stack) return NULL_RTX; else if (type && TYPE_MODE (type) == BLKmode) { rtx reg1, reg2; reg1 = gen_rtx_REG (DImode, num_args + 16); reg1 = gen_rtx_EXPR_LIST (DImode, reg1, const0_rtx); /* The argument fits in two registers. Note that we still need to reserve a register for empty structures. */ if (size == 0) return NULL_RTX; else if (size == 1) return gen_rtx_PARALLEL (mode, gen_rtvec (1, reg1)); else { reg2 = gen_rtx_REG (DImode, num_args + 17); reg2 = gen_rtx_EXPR_LIST (DImode, reg2, GEN_INT (8)); return gen_rtx_PARALLEL (mode, gen_rtvec (2, reg1, reg2)); } } } #elif TARGET_ABI_OSF { if (cum >= 6) return NULL_RTX; num_args = cum; /* VOID is passed as a special flag for "last argument". */ if (type == void_type_node) basereg = 16; else if (MUST_PASS_IN_STACK (mode, type)) return NULL_RTX; else if (FUNCTION_ARG_PASS_BY_REFERENCE (cum, mode, type, named)) basereg = 16; } #else #error Unhandled ABI #endif return gen_rtx_REG (mode, num_args + basereg); } /* Return true if TYPE must be returned in memory, instead of in registers. */ static bool alpha_return_in_memory (tree type, tree fndecl ATTRIBUTE_UNUSED) { enum machine_mode mode = VOIDmode; int size; if (type) { mode = TYPE_MODE (type); /* All aggregates are returned in memory. */ if (AGGREGATE_TYPE_P (type)) return true; } size = GET_MODE_SIZE (mode); switch (GET_MODE_CLASS (mode)) { case MODE_VECTOR_FLOAT: /* Pass all float vectors in memory, like an aggregate. */ return true; case MODE_COMPLEX_FLOAT: /* We judge complex floats on the size of their element, not the size of the whole type. */ size = GET_MODE_UNIT_SIZE (mode); break; case MODE_INT: case MODE_FLOAT: case MODE_COMPLEX_INT: case MODE_VECTOR_INT: break; default: /* ??? We get called on all sorts of random stuff from aggregate_value_p. We can't abort, but it's not clear what's safe to return. Pretend it's a struct I guess. */ return true; } /* Otherwise types must fit in one register. */ return size > UNITS_PER_WORD; } /* Define how to find the value returned by a function. VALTYPE is the data type of the value (as a tree). If the precise function being called is known, FUNC is its FUNCTION_DECL; otherwise, FUNC is 0. MODE is set instead of VALTYPE for libcalls. On Alpha the value is found in $0 for integer functions and $f0 for floating-point functions. */ rtx function_value (tree valtype, tree func ATTRIBUTE_UNUSED, enum machine_mode mode) { unsigned int regnum; enum mode_class class; #ifdef ENABLE_CHECKING if (valtype && alpha_return_in_memory (valtype, func)) abort (); #endif if (valtype) mode = TYPE_MODE (valtype); class = GET_MODE_CLASS (mode); switch (class) { case MODE_INT: /* Do the same thing as PROMOTE_MODE. */ mode = DImode; /* FALLTHRU */ case MODE_COMPLEX_INT: case MODE_VECTOR_INT: regnum = 0; break; case MODE_FLOAT: regnum = 32; break; case MODE_COMPLEX_FLOAT: { enum machine_mode cmode = GET_MODE_INNER (mode); return gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (cmode, 32), GEN_INT (0)), gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (cmode, 33), GEN_INT (GET_MODE_SIZE (cmode))))); } default: abort (); } return gen_rtx_REG (mode, regnum); } /* TCmode complex values are passed by invisible reference. We should not split these values. */ static bool alpha_split_complex_arg (tree type) { return TYPE_MODE (type) != TCmode; } static tree alpha_build_builtin_va_list (void) { tree base, ofs, space, record, type_decl; if (TARGET_ABI_OPEN_VMS || TARGET_ABI_UNICOSMK) return ptr_type_node; record = (*lang_hooks.types.make_type) (RECORD_TYPE); type_decl = build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record); TREE_CHAIN (record) = type_decl; TYPE_NAME (record) = type_decl; /* C++? SET_IS_AGGR_TYPE (record, 1); */ /* Dummy field to prevent alignment warnings. */ space = build_decl (FIELD_DECL, NULL_TREE, integer_type_node); DECL_FIELD_CONTEXT (space) = record; DECL_ARTIFICIAL (space) = 1; DECL_IGNORED_P (space) = 1; ofs = build_decl (FIELD_DECL, get_identifier ("__offset"), integer_type_node); DECL_FIELD_CONTEXT (ofs) = record; TREE_CHAIN (ofs) = space; base = build_decl (FIELD_DECL, get_identifier ("__base"), ptr_type_node); DECL_FIELD_CONTEXT (base) = record; TREE_CHAIN (base) = ofs; TYPE_FIELDS (record) = base; layout_type (record); return record; } /* Perform any needed actions needed for a function that is receiving a variable number of arguments. */ static void alpha_setup_incoming_varargs (CUMULATIVE_ARGS *pcum, enum machine_mode mode ATTRIBUTE_UNUSED, tree type ATTRIBUTE_UNUSED, int *pretend_size, int no_rtl) { #if TARGET_ABI_UNICOSMK /* On Unicos/Mk, the standard subroutine __T3E_MISMATCH stores all register arguments on the stack. Unfortunately, it doesn't always store the first one (i.e. the one that arrives in $16 or $f16). This is not a problem with stdargs as we always have at least one named argument there. */ int num_reg_words = pcum->num_reg_words; if (num_reg_words < 6) { if (!no_rtl) { emit_insn (gen_umk_mismatch_args (GEN_INT (num_reg_words + 1))); emit_insn (gen_arg_home_umk ()); } *pretend_size = 0; } #elif TARGET_ABI_OPEN_VMS /* For VMS, we allocate space for all 6 arg registers plus a count. However, if NO registers need to be saved, don't allocate any space. This is not only because we won't need the space, but because AP includes the current_pretend_args_size and we don't want to mess up any ap-relative addresses already made. */ if (pcum->num_args < 6) { if (!no_rtl) { emit_move_insn (gen_rtx_REG (DImode, 1), virtual_incoming_args_rtx); emit_insn (gen_arg_home ()); } *pretend_size = 7 * UNITS_PER_WORD; } #else /* On OSF/1 and friends, we allocate space for all 12 arg registers, but only push those that are remaining. However, if NO registers need to be saved, don't allocate any space. This is not only because we won't need the space, but because AP includes the current_pretend_args_size and we don't want to mess up any ap-relative addresses already made. If we are not to use the floating-point registers, save the integer registers where we would put the floating-point registers. This is not the most efficient way to implement varargs with just one register class, but it isn't worth doing anything more efficient in this rare case. */ CUMULATIVE_ARGS cum = *pcum; if (cum >= 6) return; if (!no_rtl) { int set = get_varargs_alias_set (); rtx tmp; tmp = gen_rtx_MEM (BLKmode, plus_constant (virtual_incoming_args_rtx, (cum + 6) * UNITS_PER_WORD)); set_mem_alias_set (tmp, set); move_block_from_reg (16 + cum, tmp, 6 - cum); tmp = gen_rtx_MEM (BLKmode, plus_constant (virtual_incoming_args_rtx, cum * UNITS_PER_WORD)); set_mem_alias_set (tmp, set); move_block_from_reg (16 + (TARGET_FPREGS ? 32 : 0) + cum, tmp, 6 - cum); } *pretend_size = 12 * UNITS_PER_WORD; #endif } void alpha_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED) { HOST_WIDE_INT offset; tree t, offset_field, base_field; if (TREE_CODE (TREE_TYPE (valist)) == ERROR_MARK) return; if (TARGET_ABI_UNICOSMK) std_expand_builtin_va_start (valist, nextarg); /* For Unix, SETUP_INCOMING_VARARGS moves the starting address base up by 48, storing fp arg registers in the first 48 bytes, and the integer arg registers in the next 48 bytes. This is only done, however, if any integer registers need to be stored. If no integer registers need be stored, then we must subtract 48 in order to account for the integer arg registers which are counted in argsize above, but which are not actually stored on the stack. Must further be careful here about structures straddling the last integer argument register; that futzes with pretend_args_size, which changes the meaning of AP. */ if (NUM_ARGS <= 6) offset = TARGET_ABI_OPEN_VMS ? UNITS_PER_WORD : 6 * UNITS_PER_WORD; else offset = -6 * UNITS_PER_WORD + current_function_pretend_args_size; if (TARGET_ABI_OPEN_VMS) { nextarg = plus_constant (nextarg, offset); nextarg = plus_constant (nextarg, NUM_ARGS * UNITS_PER_WORD); t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, make_tree (ptr_type_node, nextarg)); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); } else { base_field = TYPE_FIELDS (TREE_TYPE (valist)); offset_field = TREE_CHAIN (base_field); base_field = build (COMPONENT_REF, TREE_TYPE (base_field), valist, base_field); offset_field = build (COMPONENT_REF, TREE_TYPE (offset_field), valist, offset_field); t = make_tree (ptr_type_node, virtual_incoming_args_rtx); t = build (PLUS_EXPR, ptr_type_node, t, build_int_2 (offset, 0)); t = build (MODIFY_EXPR, TREE_TYPE (base_field), base_field, t); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); t = build_int_2 (NUM_ARGS * UNITS_PER_WORD, 0); t = build (MODIFY_EXPR, TREE_TYPE (offset_field), offset_field, t); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); } } rtx alpha_va_arg (tree valist, tree type) { rtx addr; tree t, type_size, rounded_size; tree offset_field, base_field, addr_tree, addend; tree wide_type, wide_ofs; int indirect = 0; if (TARGET_ABI_OPEN_VMS || TARGET_ABI_UNICOSMK) return std_expand_builtin_va_arg (valist, type); if (type == error_mark_node || (type_size = TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type))) == NULL || TREE_OVERFLOW (type_size)) rounded_size = size_zero_node; else rounded_size = fold (build (MULT_EXPR, sizetype, fold (build (TRUNC_DIV_EXPR, sizetype, fold (build (PLUS_EXPR, sizetype, type_size, size_int (7))), size_int (8))), size_int (8))); base_field = TYPE_FIELDS (TREE_TYPE (valist)); offset_field = TREE_CHAIN (base_field); base_field = build (COMPONENT_REF, TREE_TYPE (base_field), valist, base_field); offset_field = build (COMPONENT_REF, TREE_TYPE (offset_field), valist, offset_field); /* If the type could not be passed in registers, skip the block reserved for the registers. */ if (MUST_PASS_IN_STACK (TYPE_MODE (type), type)) { t = build (MODIFY_EXPR, TREE_TYPE (offset_field), offset_field, build (MAX_EXPR, TREE_TYPE (offset_field), offset_field, build_int_2 (6*8, 0))); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); } wide_type = make_signed_type (64); wide_ofs = save_expr (build1 (CONVERT_EXPR, wide_type, offset_field)); addend = wide_ofs; if (TYPE_MODE (type) == TFmode || TYPE_MODE (type) == TCmode) { indirect = 1; rounded_size = size_int (UNITS_PER_WORD); } else if (TREE_CODE (type) == COMPLEX_TYPE) { rtx real_part, imag_part, value, tmp; real_part = alpha_va_arg (valist, TREE_TYPE (type)); imag_part = alpha_va_arg (valist, TREE_TYPE (type)); /* ??? Most irritatingly, we're not returning the value here, but the address. Since real_part and imag_part are not necessarily contiguous, we must copy to local storage. */ real_part = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (type)), real_part); imag_part = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (type)), imag_part); value = gen_rtx_CONCAT (TYPE_MODE (type), real_part, imag_part); tmp = assign_temp (type, 0, 1, 0); emit_move_insn (tmp, value); return XEXP (tmp, 0); } else if (TREE_CODE (type) == REAL_TYPE) { tree fpaddend, cond; fpaddend = fold (build (PLUS_EXPR, TREE_TYPE (addend), addend, build_int_2 (-6*8, 0))); cond = fold (build (LT_EXPR, integer_type_node, wide_ofs, build_int_2 (6*8, 0))); addend = fold (build (COND_EXPR, TREE_TYPE (addend), cond, fpaddend, addend)); } addr_tree = build (PLUS_EXPR, TREE_TYPE (base_field), base_field, addend); addr = expand_expr (addr_tree, NULL_RTX, Pmode, EXPAND_NORMAL); addr = copy_to_reg (addr); t = build (MODIFY_EXPR, TREE_TYPE (offset_field), offset_field, build (PLUS_EXPR, TREE_TYPE (offset_field), offset_field, rounded_size)); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); if (indirect) { addr = force_reg (Pmode, addr); addr = gen_rtx_MEM (Pmode, addr); } return addr; } /* Builtins. */ enum alpha_builtin { ALPHA_BUILTIN_CMPBGE, ALPHA_BUILTIN_EXTBL, ALPHA_BUILTIN_EXTWL, ALPHA_BUILTIN_EXTLL, ALPHA_BUILTIN_EXTQL, ALPHA_BUILTIN_EXTWH, ALPHA_BUILTIN_EXTLH, ALPHA_BUILTIN_EXTQH, ALPHA_BUILTIN_INSBL, ALPHA_BUILTIN_INSWL, ALPHA_BUILTIN_INSLL, ALPHA_BUILTIN_INSQL, ALPHA_BUILTIN_INSWH, ALPHA_BUILTIN_INSLH, ALPHA_BUILTIN_INSQH, ALPHA_BUILTIN_MSKBL, ALPHA_BUILTIN_MSKWL, ALPHA_BUILTIN_MSKLL, ALPHA_BUILTIN_MSKQL, ALPHA_BUILTIN_MSKWH, ALPHA_BUILTIN_MSKLH, ALPHA_BUILTIN_MSKQH, ALPHA_BUILTIN_UMULH, ALPHA_BUILTIN_ZAP, ALPHA_BUILTIN_ZAPNOT, ALPHA_BUILTIN_AMASK, ALPHA_BUILTIN_IMPLVER, ALPHA_BUILTIN_RPCC, ALPHA_BUILTIN_THREAD_POINTER, ALPHA_BUILTIN_SET_THREAD_POINTER, /* TARGET_MAX */ ALPHA_BUILTIN_MINUB8, ALPHA_BUILTIN_MINSB8, ALPHA_BUILTIN_MINUW4, ALPHA_BUILTIN_MINSW4, ALPHA_BUILTIN_MAXUB8, ALPHA_BUILTIN_MAXSB8, ALPHA_BUILTIN_MAXUW4, ALPHA_BUILTIN_MAXSW4, ALPHA_BUILTIN_PERR, ALPHA_BUILTIN_PKLB, ALPHA_BUILTIN_PKWB, ALPHA_BUILTIN_UNPKBL, ALPHA_BUILTIN_UNPKBW, /* TARGET_CIX */ ALPHA_BUILTIN_CTTZ, ALPHA_BUILTIN_CTLZ, ALPHA_BUILTIN_CTPOP, ALPHA_BUILTIN_max }; static unsigned int const code_for_builtin[ALPHA_BUILTIN_max] = { CODE_FOR_builtin_cmpbge, CODE_FOR_builtin_extbl, CODE_FOR_builtin_extwl, CODE_FOR_builtin_extll, CODE_FOR_builtin_extql, CODE_FOR_builtin_extwh, CODE_FOR_builtin_extlh, CODE_FOR_builtin_extqh, CODE_FOR_builtin_insbl, CODE_FOR_builtin_inswl, CODE_FOR_builtin_insll, CODE_FOR_builtin_insql, CODE_FOR_builtin_inswh, CODE_FOR_builtin_inslh, CODE_FOR_builtin_insqh, CODE_FOR_builtin_mskbl, CODE_FOR_builtin_mskwl, CODE_FOR_builtin_mskll, CODE_FOR_builtin_mskql, CODE_FOR_builtin_mskwh, CODE_FOR_builtin_msklh, CODE_FOR_builtin_mskqh, CODE_FOR_umuldi3_highpart, CODE_FOR_builtin_zap, CODE_FOR_builtin_zapnot, CODE_FOR_builtin_amask, CODE_FOR_builtin_implver, CODE_FOR_builtin_rpcc, CODE_FOR_load_tp, CODE_FOR_set_tp, /* TARGET_MAX */ CODE_FOR_builtin_minub8, CODE_FOR_builtin_minsb8, CODE_FOR_builtin_minuw4, CODE_FOR_builtin_minsw4, CODE_FOR_builtin_maxub8, CODE_FOR_builtin_maxsb8, CODE_FOR_builtin_maxuw4, CODE_FOR_builtin_maxsw4, CODE_FOR_builtin_perr, CODE_FOR_builtin_pklb, CODE_FOR_builtin_pkwb, CODE_FOR_builtin_unpkbl, CODE_FOR_builtin_unpkbw, /* TARGET_CIX */ CODE_FOR_builtin_cttz, CODE_FOR_builtin_ctlz, CODE_FOR_builtin_ctpop }; struct alpha_builtin_def { const char *name; enum alpha_builtin code; unsigned int target_mask; }; static struct alpha_builtin_def const zero_arg_builtins[] = { { "__builtin_alpha_implver", ALPHA_BUILTIN_IMPLVER, 0 }, { "__builtin_alpha_rpcc", ALPHA_BUILTIN_RPCC, 0 } }; static struct alpha_builtin_def const one_arg_builtins[] = { { "__builtin_alpha_amask", ALPHA_BUILTIN_AMASK, 0 }, { "__builtin_alpha_pklb", ALPHA_BUILTIN_PKLB, MASK_MAX }, { "__builtin_alpha_pkwb", ALPHA_BUILTIN_PKWB, MASK_MAX }, { "__builtin_alpha_unpkbl", ALPHA_BUILTIN_UNPKBL, MASK_MAX }, { "__builtin_alpha_unpkbw", ALPHA_BUILTIN_UNPKBW, MASK_MAX }, { "__builtin_alpha_cttz", ALPHA_BUILTIN_CTTZ, MASK_CIX }, { "__builtin_alpha_ctlz", ALPHA_BUILTIN_CTLZ, MASK_CIX }, { "__builtin_alpha_ctpop", ALPHA_BUILTIN_CTPOP, MASK_CIX } }; static struct alpha_builtin_def const two_arg_builtins[] = { { "__builtin_alpha_cmpbge", ALPHA_BUILTIN_CMPBGE, 0 }, { "__builtin_alpha_extbl", ALPHA_BUILTIN_EXTBL, 0 }, { "__builtin_alpha_extwl", ALPHA_BUILTIN_EXTWL, 0 }, { "__builtin_alpha_extll", ALPHA_BUILTIN_EXTLL, 0 }, { "__builtin_alpha_extql", ALPHA_BUILTIN_EXTQL, 0 }, { "__builtin_alpha_extwh", ALPHA_BUILTIN_EXTWH, 0 }, { "__builtin_alpha_extlh", ALPHA_BUILTIN_EXTLH, 0 }, { "__builtin_alpha_extqh", ALPHA_BUILTIN_EXTQH, 0 }, { "__builtin_alpha_insbl", ALPHA_BUILTIN_INSBL, 0 }, { "__builtin_alpha_inswl", ALPHA_BUILTIN_INSWL, 0 }, { "__builtin_alpha_insll", ALPHA_BUILTIN_INSLL, 0 }, { "__builtin_alpha_insql", ALPHA_BUILTIN_INSQL, 0 }, { "__builtin_alpha_inswh", ALPHA_BUILTIN_INSWH, 0 }, { "__builtin_alpha_inslh", ALPHA_BUILTIN_INSLH, 0 }, { "__builtin_alpha_insqh", ALPHA_BUILTIN_INSQH, 0 }, { "__builtin_alpha_mskbl", ALPHA_BUILTIN_MSKBL, 0 }, { "__builtin_alpha_mskwl", ALPHA_BUILTIN_MSKWL, 0 }, { "__builtin_alpha_mskll", ALPHA_BUILTIN_MSKLL, 0 }, { "__builtin_alpha_mskql", ALPHA_BUILTIN_MSKQL, 0 }, { "__builtin_alpha_mskwh", ALPHA_BUILTIN_MSKWH, 0 }, { "__builtin_alpha_msklh", ALPHA_BUILTIN_MSKLH, 0 }, { "__builtin_alpha_mskqh", ALPHA_BUILTIN_MSKQH, 0 }, { "__builtin_alpha_umulh", ALPHA_BUILTIN_UMULH, 0 }, { "__builtin_alpha_zap", ALPHA_BUILTIN_ZAP, 0 }, { "__builtin_alpha_zapnot", ALPHA_BUILTIN_ZAPNOT, 0 }, { "__builtin_alpha_minub8", ALPHA_BUILTIN_MINUB8, MASK_MAX }, { "__builtin_alpha_minsb8", ALPHA_BUILTIN_MINSB8, MASK_MAX }, { "__builtin_alpha_minuw4", ALPHA_BUILTIN_MINUW4, MASK_MAX }, { "__builtin_alpha_minsw4", ALPHA_BUILTIN_MINSW4, MASK_MAX }, { "__builtin_alpha_maxub8", ALPHA_BUILTIN_MAXUB8, MASK_MAX }, { "__builtin_alpha_maxsb8", ALPHA_BUILTIN_MAXSB8, MASK_MAX }, { "__builtin_alpha_maxuw4", ALPHA_BUILTIN_MAXUW4, MASK_MAX }, { "__builtin_alpha_maxsw4", ALPHA_BUILTIN_MAXSW4, MASK_MAX }, { "__builtin_alpha_perr", ALPHA_BUILTIN_PERR, MASK_MAX } }; static void alpha_init_builtins (void) { const struct alpha_builtin_def *p; tree ftype; size_t i; ftype = build_function_type (long_integer_type_node, void_list_node); p = zero_arg_builtins; for (i = 0; i < ARRAY_SIZE (zero_arg_builtins); ++i, ++p) if ((target_flags & p->target_mask) == p->target_mask) builtin_function (p->name, ftype, p->code, BUILT_IN_MD, NULL, NULL_TREE); ftype = build_function_type_list (long_integer_type_node, long_integer_type_node, NULL_TREE); p = one_arg_builtins; for (i = 0; i < ARRAY_SIZE (one_arg_builtins); ++i, ++p) if ((target_flags & p->target_mask) == p->target_mask) builtin_function (p->name, ftype, p->code, BUILT_IN_MD, NULL, NULL_TREE); ftype = build_function_type_list (long_integer_type_node, long_integer_type_node, long_integer_type_node, NULL_TREE); p = two_arg_builtins; for (i = 0; i < ARRAY_SIZE (two_arg_builtins); ++i, ++p) if ((target_flags & p->target_mask) == p->target_mask) builtin_function (p->name, ftype, p->code, BUILT_IN_MD, NULL, NULL_TREE); ftype = build_function_type (ptr_type_node, void_list_node); builtin_function ("__builtin_thread_pointer", ftype, ALPHA_BUILTIN_THREAD_POINTER, BUILT_IN_MD, NULL, NULL_TREE); ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE); builtin_function ("__builtin_set_thread_pointer", ftype, ALPHA_BUILTIN_SET_THREAD_POINTER, BUILT_IN_MD, NULL, NULL_TREE); } /* Expand an expression EXP that calls a built-in function, with result going to TARGET if that's convenient (and in mode MODE if that's convenient). SUBTARGET may be used as the target for computing one of EXP's operands. IGNORE is nonzero if the value is to be ignored. */ static rtx alpha_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED, enum machine_mode mode ATTRIBUTE_UNUSED, int ignore ATTRIBUTE_UNUSED) { #define MAX_ARGS 2 tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); unsigned int fcode = DECL_FUNCTION_CODE (fndecl); tree arglist = TREE_OPERAND (exp, 1); enum insn_code icode; rtx op[MAX_ARGS], pat; int arity; bool nonvoid; if (fcode >= ALPHA_BUILTIN_max) internal_error ("bad builtin fcode"); icode = code_for_builtin[fcode]; if (icode == 0) internal_error ("bad builtin fcode"); nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node; for (arglist = TREE_OPERAND (exp, 1), arity = 0; arglist; arglist = TREE_CHAIN (arglist), arity++) { const struct insn_operand_data *insn_op; tree arg = TREE_VALUE (arglist); if (arg == error_mark_node) return NULL_RTX; if (arity > MAX_ARGS) return NULL_RTX; insn_op = &insn_data[icode].operand[arity + nonvoid]; op[arity] = expand_expr (arg, NULL_RTX, insn_op->mode, 0); if (!(*insn_op->predicate) (op[arity], insn_op->mode)) op[arity] = copy_to_mode_reg (insn_op->mode, op[arity]); } if (nonvoid) { enum machine_mode tmode = insn_data[icode].operand[0].mode; if (!target || GET_MODE (target) != tmode || !(*insn_data[icode].operand[0].predicate) (target, tmode)) target = gen_reg_rtx (tmode); } switch (arity) { case 0: pat = GEN_FCN (icode) (target); break; case 1: if (nonvoid) pat = GEN_FCN (icode) (target, op[0]); else pat = GEN_FCN (icode) (op[0]); break; case 2: pat = GEN_FCN (icode) (target, op[0], op[1]); break; default: abort (); } if (!pat) return NULL_RTX; emit_insn (pat); if (nonvoid) return target; else return const0_rtx; } /* This page contains routines that are used to determine what the function prologue and epilogue code will do and write them out. */ /* Compute the size of the save area in the stack. */ /* These variables are used for communication between the following functions. They indicate various things about the current function being compiled that are used to tell what kind of prologue, epilogue and procedure descriptor to generate. */ /* Nonzero if we need a stack procedure. */ enum alpha_procedure_types {PT_NULL = 0, PT_REGISTER = 1, PT_STACK = 2}; static enum alpha_procedure_types alpha_procedure_type; /* Register number (either FP or SP) that is used to unwind the frame. */ static int vms_unwind_regno; /* Register number used to save FP. We need not have one for RA since we don't modify it for register procedures. This is only defined for register frame procedures. */ static int vms_save_fp_regno; /* Register number used to reference objects off our PV. */ static int vms_base_regno; /* Compute register masks for saved registers. */ static void alpha_sa_mask (unsigned long *imaskP, unsigned long *fmaskP) { unsigned long imask = 0; unsigned long fmask = 0; unsigned int i; /* When outputting a thunk, we don't have valid register life info, but assemble_start_function wants to output .frame and .mask directives. */ if (current_function_is_thunk) { *imaskP = 0; *fmaskP = 0; return; } if (TARGET_ABI_OPEN_VMS && alpha_procedure_type == PT_STACK) imask |= (1UL << HARD_FRAME_POINTER_REGNUM); /* One for every register we have to save. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) if (! fixed_regs[i] && ! call_used_regs[i] && regs_ever_live[i] && i != REG_RA && (!TARGET_ABI_UNICOSMK || i != HARD_FRAME_POINTER_REGNUM)) { if (i < 32) imask |= (1UL << i); else fmask |= (1UL << (i - 32)); } /* We need to restore these for the handler. */ if (current_function_calls_eh_return) { for (i = 0; ; ++i) { unsigned regno = EH_RETURN_DATA_REGNO (i); if (regno == INVALID_REGNUM) break; imask |= 1UL << regno; } } /* If any register spilled, then spill the return address also. */ /* ??? This is required by the Digital stack unwind specification and isn't needed if we're doing Dwarf2 unwinding. */ if (imask || fmask || alpha_ra_ever_killed ()) imask |= (1UL << REG_RA); *imaskP = imask; *fmaskP = fmask; } int alpha_sa_size (void) { unsigned long mask[2]; int sa_size = 0; int i, j; alpha_sa_mask (&mask[0], &mask[1]); if (TARGET_ABI_UNICOSMK) { if (mask[0] || mask[1]) sa_size = 14; } else { for (j = 0; j < 2; ++j) for (i = 0; i < 32; ++i) if ((mask[j] >> i) & 1) sa_size++; } if (TARGET_ABI_UNICOSMK) { /* We might not need to generate a frame if we don't make any calls (including calls to __T3E_MISMATCH if this is a vararg function), don't have any local variables which require stack slots, don't use alloca and have not determined that we need a frame for other reasons. */ alpha_procedure_type = (sa_size || get_frame_size() != 0 || current_function_outgoing_args_size || current_function_stdarg || current_function_calls_alloca || frame_pointer_needed) ? PT_STACK : PT_REGISTER; /* Always reserve space for saving callee-saved registers if we need a frame as required by the calling convention. */ if (alpha_procedure_type == PT_STACK) sa_size = 14; } else if (TARGET_ABI_OPEN_VMS) { /* Start by assuming we can use a register procedure if we don't make any calls (REG_RA not used) or need to save any registers and a stack procedure if we do. */ if ((mask[0] >> REG_RA) & 1) alpha_procedure_type = PT_STACK; else if (get_frame_size() != 0) alpha_procedure_type = PT_REGISTER; else alpha_procedure_type = PT_NULL; /* Don't reserve space for saving FP & RA yet. Do that later after we've made the final decision on stack procedure vs register procedure. */ if (alpha_procedure_type == PT_STACK) sa_size -= 2; /* Decide whether to refer to objects off our PV via FP or PV. If we need FP for something else or if we receive a nonlocal goto (which expects PV to contain the value), we must use PV. Otherwise, start by assuming we can use FP. */ vms_base_regno = (frame_pointer_needed || current_function_has_nonlocal_label || alpha_procedure_type == PT_STACK || current_function_outgoing_args_size) ? REG_PV : HARD_FRAME_POINTER_REGNUM; /* If we want to copy PV into FP, we need to find some register in which to save FP. */ vms_save_fp_regno = -1; if (vms_base_regno == HARD_FRAME_POINTER_REGNUM) for (i = 0; i < 32; i++) if (! fixed_regs[i] && call_used_regs[i] && ! regs_ever_live[i]) vms_save_fp_regno = i; if (vms_save_fp_regno == -1 && alpha_procedure_type == PT_REGISTER) vms_base_regno = REG_PV, alpha_procedure_type = PT_STACK; else if (alpha_procedure_type == PT_NULL) vms_base_regno = REG_PV; /* Stack unwinding should be done via FP unless we use it for PV. */ vms_unwind_regno = (vms_base_regno == REG_PV ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM); /* If this is a stack procedure, allow space for saving FP and RA. */ if (alpha_procedure_type == PT_STACK) sa_size += 2; } else { /* Our size must be even (multiple of 16 bytes). */ if (sa_size & 1) sa_size++; } return sa_size * 8; } /* Define the offset between two registers, one to be eliminated, and the other its replacement, at the start of a routine. */ HOST_WIDE_INT alpha_initial_elimination_offset (unsigned int from, unsigned int to ATTRIBUTE_UNUSED) { HOST_WIDE_INT ret; ret = alpha_sa_size (); ret += ALPHA_ROUND (current_function_outgoing_args_size); if (from == FRAME_POINTER_REGNUM) ; else if (from == ARG_POINTER_REGNUM) ret += (ALPHA_ROUND (get_frame_size () + current_function_pretend_args_size) - current_function_pretend_args_size); else abort (); return ret; } int alpha_pv_save_size (void) { alpha_sa_size (); return alpha_procedure_type == PT_STACK ? 8 : 0; } int alpha_using_fp (void) { alpha_sa_size (); return vms_unwind_regno == HARD_FRAME_POINTER_REGNUM; } #if TARGET_ABI_OPEN_VMS const struct attribute_spec vms_attribute_table[] = { /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ { "overlaid", 0, 0, true, false, false, NULL }, { "global", 0, 0, true, false, false, NULL }, { "initialize", 0, 0, true, false, false, NULL }, { NULL, 0, 0, false, false, false, NULL } }; #endif static int find_lo_sum_using_gp (rtx *px, void *data ATTRIBUTE_UNUSED) { return GET_CODE (*px) == LO_SUM && XEXP (*px, 0) == pic_offset_table_rtx; } int alpha_find_lo_sum_using_gp (rtx insn) { return for_each_rtx (&PATTERN (insn), find_lo_sum_using_gp, NULL) > 0; } static int alpha_does_function_need_gp (void) { rtx insn; /* The GP being variable is an OSF abi thing. */ if (! TARGET_ABI_OSF) return 0; /* We need the gp to load the address of __mcount. */ if (TARGET_PROFILING_NEEDS_GP && current_function_profile) return 1; /* The code emitted by alpha_output_mi_thunk_osf uses the gp. */ if (current_function_is_thunk) return 1; /* The nonlocal receiver pattern assumes that the gp is valid for the nested function. Reasonable because it's almost always set correctly already. For the cases where that's wrong, make sure the nested function loads its gp on entry. */ if (current_function_has_nonlocal_goto) return 1; /* If we need a GP (we have a LDSYM insn or a CALL_INSN), load it first. Even if we are a static function, we still need to do this in case our address is taken and passed to something like qsort. */ push_topmost_sequence (); insn = get_insns (); pop_topmost_sequence (); for (; insn; insn = NEXT_INSN (insn)) if (INSN_P (insn) && GET_CODE (PATTERN (insn)) != USE && GET_CODE (PATTERN (insn)) != CLOBBER && get_attr_usegp (insn)) return 1; return 0; } /* Helper function to set RTX_FRAME_RELATED_P on instructions, including sequences. */ static rtx set_frame_related_p (void) { rtx seq = get_insns (); rtx insn; end_sequence (); if (!seq) return NULL_RTX; if (INSN_P (seq)) { insn = seq; while (insn != NULL_RTX) { RTX_FRAME_RELATED_P (insn) = 1; insn = NEXT_INSN (insn); } seq = emit_insn (seq); } else { seq = emit_insn (seq); RTX_FRAME_RELATED_P (seq) = 1; } return seq; } #define FRP(exp) (start_sequence (), exp, set_frame_related_p ()) /* Generates a store with the proper unwind info attached. VALUE is stored at BASE_REG+BASE_OFS. If FRAME_BIAS is non-zero, then BASE_REG contains SP+FRAME_BIAS, and that is the unwind info that should be generated. If FRAME_REG != VALUE, then VALUE is being stored on behalf of FRAME_REG, and FRAME_REG should be present in the unwind. */ static void emit_frame_store_1 (rtx value, rtx base_reg, HOST_WIDE_INT frame_bias, HOST_WIDE_INT base_ofs, rtx frame_reg) { rtx addr, mem, insn; addr = plus_constant (base_reg, base_ofs); mem = gen_rtx_MEM (DImode, addr); set_mem_alias_set (mem, alpha_sr_alias_set); insn = emit_move_insn (mem, value); RTX_FRAME_RELATED_P (insn) = 1; if (frame_bias || value != frame_reg) { if (frame_bias) { addr = plus_constant (stack_pointer_rtx, frame_bias + base_ofs); mem = gen_rtx_MEM (DImode, addr); } REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, gen_rtx_SET (VOIDmode, mem, frame_reg), REG_NOTES (insn)); } } static void emit_frame_store (unsigned int regno, rtx base_reg, HOST_WIDE_INT frame_bias, HOST_WIDE_INT base_ofs) { rtx reg = gen_rtx_REG (DImode, regno); emit_frame_store_1 (reg, base_reg, frame_bias, base_ofs, reg); } /* Write function prologue. */ /* On vms we have two kinds of functions: - stack frame (PROC_STACK) these are 'normal' functions with local vars and which are calling other functions - register frame (PROC_REGISTER) keeps all data in registers, needs no stack We must pass this to the assembler so it can generate the proper pdsc (procedure descriptor) This is done with the '.pdesc' command. On not-vms, we don't really differentiate between the two, as we can simply allocate stack without saving registers. */ void alpha_expand_prologue (void) { /* Registers to save. */ unsigned long imask = 0; unsigned long fmask = 0; /* Stack space needed for pushing registers clobbered by us. */ HOST_WIDE_INT sa_size; /* Complete stack size needed. */ HOST_WIDE_INT frame_size; /* Offset from base reg to register save area. */ HOST_WIDE_INT reg_offset; rtx sa_reg; int i; sa_size = alpha_sa_size (); frame_size = get_frame_size (); if (TARGET_ABI_OPEN_VMS) frame_size = ALPHA_ROUND (sa_size + (alpha_procedure_type == PT_STACK ? 8 : 0) + frame_size + current_function_pretend_args_size); else if (TARGET_ABI_UNICOSMK) /* We have to allocate space for the DSIB if we generate a frame. */ frame_size = ALPHA_ROUND (sa_size + (alpha_procedure_type == PT_STACK ? 48 : 0)) + ALPHA_ROUND (frame_size + current_function_outgoing_args_size); else frame_size = (ALPHA_ROUND (current_function_outgoing_args_size) + sa_size + ALPHA_ROUND (frame_size + current_function_pretend_args_size)); if (TARGET_ABI_OPEN_VMS) reg_offset = 8; else reg_offset = ALPHA_ROUND (current_function_outgoing_args_size); alpha_sa_mask (&imask, &fmask); /* Emit an insn to reload GP, if needed. */ if (TARGET_ABI_OSF) { alpha_function_needs_gp = alpha_does_function_need_gp (); if (alpha_function_needs_gp) emit_insn (gen_prologue_ldgp ()); } /* TARGET_PROFILING_NEEDS_GP actually implies that we need to insert the call to mcount ourselves, rather than having the linker do it magically in response to -pg. Since _mcount has special linkage, don't represent the call as a call. */ if (TARGET_PROFILING_NEEDS_GP && current_function_profile) emit_insn (gen_prologue_mcount ()); if (TARGET_ABI_UNICOSMK) unicosmk_gen_dsib (&imask); /* Adjust the stack by the frame size. If the frame size is > 4096 bytes, we need to be sure we probe somewhere in the first and last 4096 bytes (we can probably get away without the latter test) and every 8192 bytes in between. If the frame size is > 32768, we do this in a loop. Otherwise, we generate the explicit probe instructions. Note that we are only allowed to adjust sp once in the prologue. */ if (frame_size <= 32768) { if (frame_size > 4096) { int probed = 4096; do emit_insn (gen_probe_stack (GEN_INT (TARGET_ABI_UNICOSMK ? -probed + 64 : -probed))); while ((probed += 8192) < frame_size); /* We only have to do this probe if we aren't saving registers. */ if (sa_size == 0 && probed + 4096 < frame_size) emit_insn (gen_probe_stack (GEN_INT (-frame_size))); } if (frame_size != 0) FRP (emit_insn (gen_adddi3 (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (TARGET_ABI_UNICOSMK ? -frame_size + 64 : -frame_size)))); } else { /* Here we generate code to set R22 to SP + 4096 and set R23 to the number of 8192 byte blocks to probe. We then probe each block in the loop and then set SP to the proper location. If the amount remaining is > 4096, we have to do one more probe if we are not saving any registers. */ HOST_WIDE_INT blocks = (frame_size + 4096) / 8192; HOST_WIDE_INT leftover = frame_size + 4096 - blocks * 8192; rtx ptr = gen_rtx_REG (DImode, 22); rtx count = gen_rtx_REG (DImode, 23); rtx seq; emit_move_insn (count, GEN_INT (blocks)); emit_insn (gen_adddi3 (ptr, stack_pointer_rtx, GEN_INT (TARGET_ABI_UNICOSMK ? 4096 - 64 : 4096))); /* Because of the difficulty in emitting a new basic block this late in the compilation, generate the loop as a single insn. */ emit_insn (gen_prologue_stack_probe_loop (count, ptr)); if (leftover > 4096 && sa_size == 0) { rtx last = gen_rtx_MEM (DImode, plus_constant (ptr, -leftover)); MEM_VOLATILE_P (last) = 1; emit_move_insn (last, const0_rtx); } if (TARGET_ABI_WINDOWS_NT) { /* For NT stack unwind (done by 'reverse execution'), it's not OK to take the result of a loop, even though the value is already in ptr, so we reload it via a single operation and subtract it to sp. Yes, that's correct -- we have to reload the whole constant into a temporary via ldah+lda then subtract from sp. */ HOST_WIDE_INT lo, hi; lo = ((frame_size & 0xffff) ^ 0x8000) - 0x8000; hi = frame_size - lo; emit_move_insn (ptr, GEN_INT (hi)); emit_insn (gen_adddi3 (ptr, ptr, GEN_INT (lo))); seq = emit_insn (gen_subdi3 (stack_pointer_rtx, stack_pointer_rtx, ptr)); } else { seq = emit_insn (gen_adddi3 (stack_pointer_rtx, ptr, GEN_INT (-leftover))); } /* This alternative is special, because the DWARF code cannot possibly intuit through the loop above. So we invent this note it looks at instead. */ RTX_FRAME_RELATED_P (seq) = 1; REG_NOTES (seq) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, gen_rtx_SET (VOIDmode, stack_pointer_rtx, gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (TARGET_ABI_UNICOSMK ? -frame_size + 64 : -frame_size))), REG_NOTES (seq)); } if (!TARGET_ABI_UNICOSMK) { HOST_WIDE_INT sa_bias = 0; /* Cope with very large offsets to the register save area. */ sa_reg = stack_pointer_rtx; if (reg_offset + sa_size > 0x8000) { int low = ((reg_offset & 0xffff) ^ 0x8000) - 0x8000; rtx sa_bias_rtx; if (low + sa_size <= 0x8000) sa_bias = reg_offset - low, reg_offset = low; else sa_bias = reg_offset, reg_offset = 0; sa_reg = gen_rtx_REG (DImode, 24); sa_bias_rtx = GEN_INT (sa_bias); if (add_operand (sa_bias_rtx, DImode)) emit_insn (gen_adddi3 (sa_reg, stack_pointer_rtx, sa_bias_rtx)); else { emit_move_insn (sa_reg, sa_bias_rtx); emit_insn (gen_adddi3 (sa_reg, stack_pointer_rtx, sa_reg)); } } /* Save regs in stack order. Beginning with VMS PV. */ if (TARGET_ABI_OPEN_VMS && alpha_procedure_type == PT_STACK) emit_frame_store (REG_PV, stack_pointer_rtx, 0, 0); /* Save register RA next. */ if (imask & (1UL << REG_RA)) { emit_frame_store (REG_RA, sa_reg, sa_bias, reg_offset); imask &= ~(1UL << REG_RA); reg_offset += 8; } /* Now save any other registers required to be saved. */ for (i = 0; i < 31; i++) if (imask & (1UL << i)) { emit_frame_store (i, sa_reg, sa_bias, reg_offset); reg_offset += 8; } for (i = 0; i < 31; i++) if (fmask & (1UL << i)) { emit_frame_store (i+32, sa_reg, sa_bias, reg_offset); reg_offset += 8; } } else if (TARGET_ABI_UNICOSMK && alpha_procedure_type == PT_STACK) { /* The standard frame on the T3E includes space for saving registers. We just have to use it. We don't have to save the return address and the old frame pointer here - they are saved in the DSIB. */ reg_offset = -56; for (i = 9; i < 15; i++) if (imask & (1UL << i)) { emit_frame_store (i, hard_frame_pointer_rtx, 0, reg_offset); reg_offset -= 8; } for (i = 2; i < 10; i++) if (fmask & (1UL << i)) { emit_frame_store (i+32, hard_frame_pointer_rtx, 0, reg_offset); reg_offset -= 8; } } if (TARGET_ABI_OPEN_VMS) { if (alpha_procedure_type == PT_REGISTER) /* Register frame procedures save the fp. ?? Ought to have a dwarf2 save for this. */ emit_move_insn (gen_rtx_REG (DImode, vms_save_fp_regno), hard_frame_pointer_rtx); if (alpha_procedure_type != PT_NULL && vms_base_regno != REG_PV) emit_insn (gen_force_movdi (gen_rtx_REG (DImode, vms_base_regno), gen_rtx_REG (DImode, REG_PV))); if (alpha_procedure_type != PT_NULL && vms_unwind_regno == HARD_FRAME_POINTER_REGNUM) FRP (emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx)); /* If we have to allocate space for outgoing args, do it now. */ if (current_function_outgoing_args_size != 0) { rtx seq = emit_move_insn (stack_pointer_rtx, plus_constant (hard_frame_pointer_rtx, - (ALPHA_ROUND (current_function_outgoing_args_size)))); /* Only set FRAME_RELATED_P on the stack adjustment we just emitted if ! frame_pointer_needed. Setting the bit will change the CFA computation rule to use sp again, which would be wrong if we had frame_pointer_needed, as this means sp might move unpredictably later on. Also, note that frame_pointer_needed => vms_unwind_regno == HARD_FRAME_POINTER_REGNUM and current_function_outgoing_args_size != 0 => alpha_procedure_type != PT_NULL, so when we are not setting the bit here, we are guaranteed to have emitted an FRP frame pointer update just before. */ RTX_FRAME_RELATED_P (seq) = ! frame_pointer_needed; } } else if (!TARGET_ABI_UNICOSMK) { /* If we need a frame pointer, set it from the stack pointer. */ if (frame_pointer_needed) { if (TARGET_CAN_FAULT_IN_PROLOGUE) FRP (emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx)); else /* This must always be the last instruction in the prologue, thus we emit a special move + clobber. */ FRP (emit_insn (gen_init_fp (hard_frame_pointer_rtx, stack_pointer_rtx, sa_reg))); } } /* The ABIs for VMS and OSF/1 say that while we can schedule insns into the prologue, for exception handling reasons, we cannot do this for any insn that might fault. We could prevent this for mems with a (clobber:BLK (scratch)), but this doesn't work for fp insns. So we have to prevent all such scheduling with a blockage. Linux, on the other hand, never bothered to implement OSF/1's exception handling, and so doesn't care about such things. Anyone planning to use dwarf2 frame-unwind info can also omit the blockage. */ if (! TARGET_CAN_FAULT_IN_PROLOGUE) emit_insn (gen_blockage ()); } /* Output the textual info surrounding the prologue. */ void alpha_start_function (FILE *file, const char *fnname, tree decl ATTRIBUTE_UNUSED) { unsigned long imask = 0; unsigned long fmask = 0; /* Stack space needed for pushing registers clobbered by us. */ HOST_WIDE_INT sa_size; /* Complete stack size needed. */ unsigned HOST_WIDE_INT frame_size; /* Offset from base reg to register save area. */ HOST_WIDE_INT reg_offset; char *entry_label = (char *) alloca (strlen (fnname) + 6); int i; /* Don't emit an extern directive for functions defined in the same file. */ if (TARGET_ABI_UNICOSMK) { tree name_tree; name_tree = get_identifier (fnname); TREE_ASM_WRITTEN (name_tree) = 1; } alpha_fnname = fnname; sa_size = alpha_sa_size (); frame_size = get_frame_size (); if (TARGET_ABI_OPEN_VMS) frame_size = ALPHA_ROUND (sa_size + (alpha_procedure_type == PT_STACK ? 8 : 0) + frame_size + current_function_pretend_args_size); else if (TARGET_ABI_UNICOSMK) frame_size = ALPHA_ROUND (sa_size + (alpha_procedure_type == PT_STACK ? 48 : 0)) + ALPHA_ROUND (frame_size + current_function_outgoing_args_size); else frame_size = (ALPHA_ROUND (current_function_outgoing_args_size) + sa_size + ALPHA_ROUND (frame_size + current_function_pretend_args_size)); if (TARGET_ABI_OPEN_VMS) reg_offset = 8; else reg_offset = ALPHA_ROUND (current_function_outgoing_args_size); alpha_sa_mask (&imask, &fmask); /* Ecoff can handle multiple .file directives, so put out file and lineno. We have to do that before the .ent directive as we cannot switch files within procedures with native ecoff because line numbers are linked to procedure descriptors. Outputting the lineno helps debugging of one line functions as they would otherwise get no line number at all. Please note that we would like to put out last_linenum from final.c, but it is not accessible. */ if (write_symbols == SDB_DEBUG) { #ifdef ASM_OUTPUT_SOURCE_FILENAME ASM_OUTPUT_SOURCE_FILENAME (file, DECL_SOURCE_FILE (current_function_decl)); #endif #ifdef ASM_OUTPUT_SOURCE_LINE if (debug_info_level != DINFO_LEVEL_TERSE) ASM_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl), 0); #endif } /* Issue function start and label. */ if (TARGET_ABI_OPEN_VMS || (!TARGET_ABI_UNICOSMK && !flag_inhibit_size_directive)) { fputs ("\t.ent ", file); assemble_name (file, fnname); putc ('\n', file); /* If the function needs GP, we'll write the "..ng" label there. Otherwise, do it here. */ if (TARGET_ABI_OSF && ! alpha_function_needs_gp && ! current_function_is_thunk) { putc ('$', file); assemble_name (file, fnname); fputs ("..ng:\n", file); } } strcpy (entry_label, fnname); if (TARGET_ABI_OPEN_VMS) strcat (entry_label, "..en"); /* For public functions, the label must be globalized by appending an additional colon. */ if (TARGET_ABI_UNICOSMK && TREE_PUBLIC (decl)) strcat (entry_label, ":"); ASM_OUTPUT_LABEL (file, entry_label); inside_function = TRUE; if (TARGET_ABI_OPEN_VMS) fprintf (file, "\t.base $%d\n", vms_base_regno); if (!TARGET_ABI_OPEN_VMS && !TARGET_ABI_UNICOSMK && TARGET_IEEE_CONFORMANT && !flag_inhibit_size_directive) { /* Set flags in procedure descriptor to request IEEE-conformant math-library routines. The value we set it to is PDSC_EXC_IEEE (/usr/include/pdsc.h). */ fputs ("\t.eflag 48\n", file); } /* Set up offsets to alpha virtual arg/local debugging pointer. */ alpha_auto_offset = -frame_size + current_function_pretend_args_size; alpha_arg_offset = -frame_size + 48; /* Describe our frame. If the frame size is larger than an integer, print it as zero to avoid an assembler error. We won't be properly describing such a frame, but that's the best we can do. */ if (TARGET_ABI_UNICOSMK) ; else if (TARGET_ABI_OPEN_VMS) fprintf (file, "\t.frame $%d," HOST_WIDE_INT_PRINT_DEC ",$26," HOST_WIDE_INT_PRINT_DEC "\n", vms_unwind_regno, frame_size >= (1UL << 31) ? 0 : frame_size, reg_offset); else if (!flag_inhibit_size_directive) fprintf (file, "\t.frame $%d," HOST_WIDE_INT_PRINT_DEC ",$26,%d\n", (frame_pointer_needed ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM), frame_size >= (1UL << 31) ? 0 : frame_size, current_function_pretend_args_size); /* Describe which registers were spilled. */ if (TARGET_ABI_UNICOSMK) ; else if (TARGET_ABI_OPEN_VMS) { if (imask) /* ??? Does VMS care if mask contains ra? The old code didn't set it, so I don't here. */ fprintf (file, "\t.mask 0x%lx,0\n", imask & ~(1UL << REG_RA)); if (fmask) fprintf (file, "\t.fmask 0x%lx,0\n", fmask); if (alpha_procedure_type == PT_REGISTER) fprintf (file, "\t.fp_save $%d\n", vms_save_fp_regno); } else if (!flag_inhibit_size_directive) { if (imask) { fprintf (file, "\t.mask 0x%lx," HOST_WIDE_INT_PRINT_DEC "\n", imask, frame_size >= (1UL << 31) ? 0 : reg_offset - frame_size); for (i = 0; i < 32; ++i) if (imask & (1UL << i)) reg_offset += 8; } if (fmask) fprintf (file, "\t.fmask 0x%lx," HOST_WIDE_INT_PRINT_DEC "\n", fmask, frame_size >= (1UL << 31) ? 0 : reg_offset - frame_size); } #if TARGET_ABI_OPEN_VMS /* Ifdef'ed cause link_section are only available then. */ readonly_data_section (); fprintf (file, "\t.align 3\n"); assemble_name (file, fnname); fputs ("..na:\n", file); fputs ("\t.ascii \"", file); assemble_name (file, fnname); fputs ("\\0\"\n", file); alpha_need_linkage (fnname, 1); text_section (); #endif } /* Emit the .prologue note at the scheduled end of the prologue. */ static void alpha_output_function_end_prologue (FILE *file) { if (TARGET_ABI_UNICOSMK) ; else if (TARGET_ABI_OPEN_VMS) fputs ("\t.prologue\n", file); else if (TARGET_ABI_WINDOWS_NT) fputs ("\t.prologue 0\n", file); else if (!flag_inhibit_size_directive) fprintf (file, "\t.prologue %d\n", alpha_function_needs_gp || current_function_is_thunk); } /* Write function epilogue. */ /* ??? At some point we will want to support full unwind, and so will need to mark the epilogue as well. At the moment, we just confuse dwarf2out. */ #undef FRP #define FRP(exp) exp void alpha_expand_epilogue (void) { /* Registers to save. */ unsigned long imask = 0; unsigned long fmask = 0; /* Stack space needed for pushing registers clobbered by us. */ HOST_WIDE_INT sa_size; /* Complete stack size needed. */ HOST_WIDE_INT frame_size; /* Offset from base reg to register save area. */ HOST_WIDE_INT reg_offset; int fp_is_frame_pointer, fp_offset; rtx sa_reg, sa_reg_exp = NULL; rtx sp_adj1, sp_adj2, mem; rtx eh_ofs; int i; sa_size = alpha_sa_size (); frame_size = get_frame_size (); if (TARGET_ABI_OPEN_VMS) frame_size = ALPHA_ROUND (sa_size + (alpha_procedure_type == PT_STACK ? 8 : 0) + frame_size + current_function_pretend_args_size); else if (TARGET_ABI_UNICOSMK) frame_size = ALPHA_ROUND (sa_size + (alpha_procedure_type == PT_STACK ? 48 : 0)) + ALPHA_ROUND (frame_size + current_function_outgoing_args_size); else frame_size = (ALPHA_ROUND (current_function_outgoing_args_size) + sa_size + ALPHA_ROUND (frame_size + current_function_pretend_args_size)); if (TARGET_ABI_OPEN_VMS) { if (alpha_procedure_type == PT_STACK) reg_offset = 8; else reg_offset = 0; } else reg_offset = ALPHA_ROUND (current_function_outgoing_args_size); alpha_sa_mask (&imask, &fmask); fp_is_frame_pointer = ((TARGET_ABI_OPEN_VMS && alpha_procedure_type == PT_STACK) || (!TARGET_ABI_OPEN_VMS && frame_pointer_needed)); fp_offset = 0; sa_reg = stack_pointer_rtx; if (current_function_calls_eh_return) eh_ofs = EH_RETURN_STACKADJ_RTX; else eh_ofs = NULL_RTX; if (!TARGET_ABI_UNICOSMK && sa_size) { /* If we have a frame pointer, restore SP from it. */ if ((TARGET_ABI_OPEN_VMS && vms_unwind_regno == HARD_FRAME_POINTER_REGNUM) || (!TARGET_ABI_OPEN_VMS && frame_pointer_needed)) FRP (emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx)); /* Cope with very large offsets to the register save area. */ if (reg_offset + sa_size > 0x8000) { int low = ((reg_offset & 0xffff) ^ 0x8000) - 0x8000; HOST_WIDE_INT bias; if (low + sa_size <= 0x8000) bias = reg_offset - low, reg_offset = low; else bias = reg_offset, reg_offset = 0; sa_reg = gen_rtx_REG (DImode, 22); sa_reg_exp = plus_constant (stack_pointer_rtx, bias); FRP (emit_move_insn (sa_reg, sa_reg_exp)); } /* Restore registers in order, excepting a true frame pointer. */ mem = gen_rtx_MEM (DImode, plus_constant (sa_reg, reg_offset)); if (! eh_ofs) set_mem_alias_set (mem, alpha_sr_alias_set); FRP (emit_move_insn (gen_rtx_REG (DImode, REG_RA), mem)); reg_offset += 8; imask &= ~(1UL << REG_RA); for (i = 0; i < 31; ++i) if (imask & (1UL << i)) { if (i == HARD_FRAME_POINTER_REGNUM && fp_is_frame_pointer) fp_offset = reg_offset; else { mem = gen_rtx_MEM (DImode, plus_constant(sa_reg, reg_offset)); set_mem_alias_set (mem, alpha_sr_alias_set); FRP (emit_move_insn (gen_rtx_REG (DImode, i), mem)); } reg_offset += 8; } for (i = 0; i < 31; ++i) if (fmask & (1UL << i)) { mem = gen_rtx_MEM (DFmode, plus_constant(sa_reg, reg_offset)); set_mem_alias_set (mem, alpha_sr_alias_set); FRP (emit_move_insn (gen_rtx_REG (DFmode, i+32), mem)); reg_offset += 8; } } else if (TARGET_ABI_UNICOSMK && alpha_procedure_type == PT_STACK) { /* Restore callee-saved general-purpose registers. */ reg_offset = -56; for (i = 9; i < 15; i++) if (imask & (1UL << i)) { mem = gen_rtx_MEM (DImode, plus_constant(hard_frame_pointer_rtx, reg_offset)); set_mem_alias_set (mem, alpha_sr_alias_set); FRP (emit_move_insn (gen_rtx_REG (DImode, i), mem)); reg_offset -= 8; } for (i = 2; i < 10; i++) if (fmask & (1UL << i)) { mem = gen_rtx_MEM (DFmode, plus_constant(hard_frame_pointer_rtx, reg_offset)); set_mem_alias_set (mem, alpha_sr_alias_set); FRP (emit_move_insn (gen_rtx_REG (DFmode, i+32), mem)); reg_offset -= 8; } /* Restore the return address from the DSIB. */ mem = gen_rtx_MEM (DImode, plus_constant(hard_frame_pointer_rtx, -8)); set_mem_alias_set (mem, alpha_sr_alias_set); FRP (emit_move_insn (gen_rtx_REG (DImode, REG_RA), mem)); } if (frame_size || eh_ofs) { sp_adj1 = stack_pointer_rtx; if (eh_ofs) { sp_adj1 = gen_rtx_REG (DImode, 23); emit_move_insn (sp_adj1, gen_rtx_PLUS (Pmode, stack_pointer_rtx, eh_ofs)); } /* If the stack size is large, begin computation into a temporary register so as not to interfere with a potential fp restore, which must be consecutive with an SP restore. */ if (frame_size < 32768 && ! (TARGET_ABI_UNICOSMK && current_function_calls_alloca)) sp_adj2 = GEN_INT (frame_size); else if (TARGET_ABI_UNICOSMK) { sp_adj1 = gen_rtx_REG (DImode, 23); FRP (emit_move_insn (sp_adj1, hard_frame_pointer_rtx)); sp_adj2 = const0_rtx; } else if (frame_size < 0x40007fffL) { int low = ((frame_size & 0xffff) ^ 0x8000) - 0x8000; sp_adj2 = plus_constant (sp_adj1, frame_size - low); if (sa_reg_exp && rtx_equal_p (sa_reg_exp, sp_adj2)) sp_adj1 = sa_reg; else { sp_adj1 = gen_rtx_REG (DImode, 23); FRP (emit_move_insn (sp_adj1, sp_adj2)); } sp_adj2 = GEN_INT (low); } else { rtx tmp = gen_rtx_REG (DImode, 23); FRP (sp_adj2 = alpha_emit_set_const (tmp, DImode, frame_size, 3)); if (!sp_adj2) { /* We can't drop new things to memory this late, afaik, so build it up by pieces. */ FRP (sp_adj2 = alpha_emit_set_long_const (tmp, frame_size, -(frame_size < 0))); if (!sp_adj2) abort (); } } /* From now on, things must be in order. So emit blockages. */ /* Restore the frame pointer. */ if (TARGET_ABI_UNICOSMK) { emit_insn (gen_blockage ()); mem = gen_rtx_MEM (DImode, plus_constant (hard_frame_pointer_rtx, -16)); set_mem_alias_set (mem, alpha_sr_alias_set); FRP (emit_move_insn (hard_frame_pointer_rtx, mem)); } else if (fp_is_frame_pointer) { emit_insn (gen_blockage ()); mem = gen_rtx_MEM (DImode, plus_constant (sa_reg, fp_offset)); set_mem_alias_set (mem, alpha_sr_alias_set); FRP (emit_move_insn (hard_frame_pointer_rtx, mem)); } else if (TARGET_ABI_OPEN_VMS) { emit_insn (gen_blockage ()); FRP (emit_move_insn (hard_frame_pointer_rtx, gen_rtx_REG (DImode, vms_save_fp_regno))); } /* Restore the stack pointer. */ emit_insn (gen_blockage ()); if (sp_adj2 == const0_rtx) FRP (emit_move_insn (stack_pointer_rtx, sp_adj1)); else FRP (emit_move_insn (stack_pointer_rtx, gen_rtx_PLUS (DImode, sp_adj1, sp_adj2))); } else { if (TARGET_ABI_OPEN_VMS && alpha_procedure_type == PT_REGISTER) { emit_insn (gen_blockage ()); FRP (emit_move_insn (hard_frame_pointer_rtx, gen_rtx_REG (DImode, vms_save_fp_regno))); } else if (TARGET_ABI_UNICOSMK && alpha_procedure_type != PT_STACK) { /* Decrement the frame pointer if the function does not have a frame. */ emit_insn (gen_blockage ()); FRP (emit_insn (gen_adddi3 (hard_frame_pointer_rtx, hard_frame_pointer_rtx, GEN_INT (-1)))); } } } /* Output the rest of the textual info surrounding the epilogue. */ void alpha_end_function (FILE *file, const char *fnname, tree decl ATTRIBUTE_UNUSED) { /* End the function. */ if (!TARGET_ABI_UNICOSMK && !flag_inhibit_size_directive) { fputs ("\t.end ", file); assemble_name (file, fnname); putc ('\n', file); } inside_function = FALSE; #if TARGET_ABI_OPEN_VMS alpha_write_linkage (file, fnname, decl); #endif /* Output jump tables and the static subroutine information block. */ if (TARGET_ABI_UNICOSMK) { unicosmk_output_ssib (file, fnname); unicosmk_output_deferred_case_vectors (file); } } #if TARGET_ABI_OSF /* Emit a tail call to FUNCTION after adjusting THIS by DELTA. In order to avoid the hordes of differences between generated code with and without TARGET_EXPLICIT_RELOCS, and to avoid duplicating lots of code loading up large constants, generate rtl and emit it instead of going straight to text. Not sure why this idea hasn't been explored before... */ static void alpha_output_mi_thunk_osf (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset, tree function) { HOST_WIDE_INT hi, lo; rtx this, insn, funexp; /* We always require a valid GP. */ emit_insn (gen_prologue_ldgp ()); emit_note (NOTE_INSN_PROLOGUE_END); /* Find the "this" pointer. If the function returns a structure, the structure return pointer is in $16. */ if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function)) this = gen_rtx_REG (Pmode, 17); else this = gen_rtx_REG (Pmode, 16); /* Add DELTA. When possible we use ldah+lda. Otherwise load the entire constant for the add. */ lo = ((delta & 0xffff) ^ 0x8000) - 0x8000; hi = (((delta - lo) & 0xffffffff) ^ 0x80000000) - 0x80000000; if (hi + lo == delta) { if (hi) emit_insn (gen_adddi3 (this, this, GEN_INT (hi))); if (lo) emit_insn (gen_adddi3 (this, this, GEN_INT (lo))); } else { rtx tmp = alpha_emit_set_long_const (gen_rtx_REG (Pmode, 0), delta, -(delta < 0)); emit_insn (gen_adddi3 (this, this, tmp)); } /* Add a delta stored in the vtable at VCALL_OFFSET. */ if (vcall_offset) { rtx tmp, tmp2; tmp = gen_rtx_REG (Pmode, 0); emit_move_insn (tmp, gen_rtx_MEM (Pmode, this)); lo = ((vcall_offset & 0xffff) ^ 0x8000) - 0x8000; hi = (((vcall_offset - lo) & 0xffffffff) ^ 0x80000000) - 0x80000000; if (hi + lo == vcall_offset) { if (hi) emit_insn (gen_adddi3 (tmp, tmp, GEN_INT (hi))); } else { tmp2 = alpha_emit_set_long_const (gen_rtx_REG (Pmode, 1), vcall_offset, -(vcall_offset < 0)); emit_insn (gen_adddi3 (tmp, tmp, tmp2)); lo = 0; } if (lo) tmp2 = gen_rtx_PLUS (Pmode, tmp, GEN_INT (lo)); else tmp2 = tmp; emit_move_insn (tmp, gen_rtx_MEM (Pmode, tmp2)); emit_insn (gen_adddi3 (this, this, tmp)); } /* Generate a tail call to the target function. */ if (! TREE_USED (function)) { assemble_external (function); TREE_USED (function) = 1; } funexp = XEXP (DECL_RTL (function), 0); funexp = gen_rtx_MEM (FUNCTION_MODE, funexp); insn = emit_call_insn (gen_sibcall (funexp, const0_rtx)); SIBLING_CALL_P (insn) = 1; /* Run just enough of rest_of_compilation to get the insns emitted. There's not really enough bulk here to make other passes such as instruction scheduling worth while. Note that use_thunk calls assemble_start_function and assemble_end_function. */ insn = get_insns (); insn_locators_initialize (); shorten_branches (insn); final_start_function (insn, file, 1); final (insn, file, 1, 0); final_end_function (); } #endif /* TARGET_ABI_OSF */ /* Debugging support. */ #include "gstab.h" /* Count the number of sdb related labels are generated (to find block start and end boundaries). */ int sdb_label_count = 0; /* Next label # for each statement. */ static int sym_lineno = 0; /* Count the number of .file directives, so that .loc is up to date. */ static int num_source_filenames = 0; /* Name of the file containing the current function. */ static const char *current_function_file = ""; /* Offsets to alpha virtual arg/local debugging pointers. */ long alpha_arg_offset; long alpha_auto_offset; /* Emit a new filename to a stream. */ void alpha_output_filename (FILE *stream, const char *name) { static int first_time = TRUE; char ltext_label_name[100]; if (first_time) { first_time = FALSE; ++num_source_filenames; current_function_file = name; fprintf (stream, "\t.file\t%d ", num_source_filenames); output_quoted_string (stream, name); fprintf (stream, "\n"); if (!TARGET_GAS && write_symbols == DBX_DEBUG) fprintf (stream, "\t#@stabs\n"); } else if (write_symbols == DBX_DEBUG) { ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext", 0); fprintf (stream, "%s", ASM_STABS_OP); output_quoted_string (stream, name); fprintf (stream, ",%d,0,0,%s\n", N_SOL, <ext_label_name[1]); } else if (name != current_function_file && strcmp (name, current_function_file) != 0) { if (inside_function && ! TARGET_GAS) fprintf (stream, "\t#.file\t%d ", num_source_filenames); else { ++num_source_filenames; current_function_file = name; fprintf (stream, "\t.file\t%d ", num_source_filenames); } output_quoted_string (stream, name); fprintf (stream, "\n"); } } /* Emit a linenumber to a stream. */ void alpha_output_lineno (FILE *stream, int line) { if (write_symbols == DBX_DEBUG) { /* mips-tfile doesn't understand .stabd directives. */ ++sym_lineno; fprintf (stream, "$LM%d:\n%s%d,0,%d,$LM%d\n", sym_lineno, ASM_STABN_OP, N_SLINE, line, sym_lineno); } else fprintf (stream, "\n\t.loc\t%d %d\n", num_source_filenames, line); } /* Structure to show the current status of registers and memory. */ struct shadow_summary { struct { unsigned int i : 31; /* Mask of int regs */ unsigned int fp : 31; /* Mask of fp regs */ unsigned int mem : 1; /* mem == imem | fpmem */ } used, defd; }; /* Summary the effects of expression X on the machine. Update SUM, a pointer to the summary structure. SET is nonzero if the insn is setting the object, otherwise zero. */ static void summarize_insn (rtx x, struct shadow_summary *sum, int set) { const char *format_ptr; int i, j; if (x == 0) return; switch (GET_CODE (x)) { /* ??? Note that this case would be incorrect if the Alpha had a ZERO_EXTRACT in SET_DEST. */ case SET: summarize_insn (SET_SRC (x), sum, 0); summarize_insn (SET_DEST (x), sum, 1); break; case CLOBBER: summarize_insn (XEXP (x, 0), sum, 1); break; case USE: summarize_insn (XEXP (x, 0), sum, 0); break; case ASM_OPERANDS: for (i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--) summarize_insn (ASM_OPERANDS_INPUT (x, i), sum, 0); break; case PARALLEL: for (i = XVECLEN (x, 0) - 1; i >= 0; i--) summarize_insn (XVECEXP (x, 0, i), sum, 0); break; case SUBREG: summarize_insn (SUBREG_REG (x), sum, 0); break; case REG: { int regno = REGNO (x); unsigned long mask = ((unsigned long) 1) << (regno % 32); if (regno == 31 || regno == 63) break; if (set) { if (regno < 32) sum->defd.i |= mask; else sum->defd.fp |= mask; } else { if (regno < 32) sum->used.i |= mask; else sum->used.fp |= mask; } } break; case MEM: if (set) sum->defd.mem = 1; else sum->used.mem = 1; /* Find the regs used in memory address computation: */ summarize_insn (XEXP (x, 0), sum, 0); break; case CONST_INT: case CONST_DOUBLE: case SYMBOL_REF: case LABEL_REF: case CONST: case SCRATCH: case ASM_INPUT: break; /* Handle common unary and binary ops for efficiency. */ case COMPARE: case PLUS: case MINUS: case MULT: case DIV: case MOD: case UDIV: case UMOD: case AND: case IOR: case XOR: case ASHIFT: case ROTATE: case ASHIFTRT: case LSHIFTRT: case ROTATERT: case SMIN: case SMAX: case UMIN: case UMAX: case NE: case EQ: case GE: case GT: case LE: case LT: case GEU: case GTU: case LEU: case LTU: summarize_insn (XEXP (x, 0), sum, 0); summarize_insn (XEXP (x, 1), sum, 0); break; case NEG: case NOT: case SIGN_EXTEND: case ZERO_EXTEND: case TRUNCATE: case FLOAT_EXTEND: case FLOAT_TRUNCATE: case FLOAT: case FIX: case UNSIGNED_FLOAT: case UNSIGNED_FIX: case ABS: case SQRT: case FFS: summarize_insn (XEXP (x, 0), sum, 0); break; default: format_ptr = GET_RTX_FORMAT (GET_CODE (x)); for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) switch (format_ptr[i]) { case 'e': summarize_insn (XEXP (x, i), sum, 0); break; case 'E': for (j = XVECLEN (x, i) - 1; j >= 0; j--) summarize_insn (XVECEXP (x, i, j), sum, 0); break; case 'i': break; default: abort (); } } } /* Ensure a sufficient number of `trapb' insns are in the code when the user requests code with a trap precision of functions or instructions. In naive mode, when the user requests a trap-precision of "instruction", a trapb is needed after every instruction that may generate a trap. This ensures that the code is resumption safe but it is also slow. When optimizations are turned on, we delay issuing a trapb as long as possible. In this context, a trap shadow is the sequence of instructions that starts with a (potentially) trap generating instruction and extends to the next trapb or call_pal instruction (but GCC never generates call_pal by itself). We can delay (and therefore sometimes omit) a trapb subject to the following conditions: (a) On entry to the trap shadow, if any Alpha register or memory location contains a value that is used as an operand value by some instruction in the trap shadow (live on entry), then no instruction in the trap shadow may modify the register or memory location. (b) Within the trap shadow, the computation of the base register for a memory load or store instruction may not involve using the result of an instruction that might generate an UNPREDICTABLE result. (c) Within the trap shadow, no register may be used more than once as a destination register. (This is to make life easier for the trap-handler.) (d) The trap shadow may not include any branch instructions. */ static void alpha_handle_trap_shadows (void) { struct shadow_summary shadow; int trap_pending, exception_nesting; rtx i, n; trap_pending = 0; exception_nesting = 0; shadow.used.i = 0; shadow.used.fp = 0; shadow.used.mem = 0; shadow.defd = shadow.used; for (i = get_insns (); i ; i = NEXT_INSN (i)) { if (GET_CODE (i) == NOTE) { switch (NOTE_LINE_NUMBER (i)) { case NOTE_INSN_EH_REGION_BEG: exception_nesting++; if (trap_pending) goto close_shadow; break; case NOTE_INSN_EH_REGION_END: exception_nesting--; if (trap_pending) goto close_shadow; break; case NOTE_INSN_EPILOGUE_BEG: if (trap_pending && alpha_tp >= ALPHA_TP_FUNC) goto close_shadow; break; } } else if (trap_pending) { if (alpha_tp == ALPHA_TP_FUNC) { if (GET_CODE (i) == JUMP_INSN && GET_CODE (PATTERN (i)) == RETURN) goto close_shadow; } else if (alpha_tp == ALPHA_TP_INSN) { if (optimize > 0) { struct shadow_summary sum; sum.used.i = 0; sum.used.fp = 0; sum.used.mem = 0; sum.defd = sum.used; switch (GET_CODE (i)) { case INSN: /* Annoyingly, get_attr_trap will abort on these. */ if (GET_CODE (PATTERN (i)) == USE || GET_CODE (PATTERN (i)) == CLOBBER) break; summarize_insn (PATTERN (i), &sum, 0); if ((sum.defd.i & shadow.defd.i) || (sum.defd.fp & shadow.defd.fp)) { /* (c) would be violated */ goto close_shadow; } /* Combine shadow with summary of current insn: */ shadow.used.i |= sum.used.i; shadow.used.fp |= sum.used.fp; shadow.used.mem |= sum.used.mem; shadow.defd.i |= sum.defd.i; shadow.defd.fp |= sum.defd.fp; shadow.defd.mem |= sum.defd.mem; if ((sum.defd.i & shadow.used.i) || (sum.defd.fp & shadow.used.fp) || (sum.defd.mem & shadow.used.mem)) { /* (a) would be violated (also takes care of (b)) */ if (get_attr_trap (i) == TRAP_YES && ((sum.defd.i & sum.used.i) || (sum.defd.fp & sum.used.fp))) abort (); goto close_shadow; } break; case JUMP_INSN: case CALL_INSN: case CODE_LABEL: goto close_shadow; default: abort (); } } else { close_shadow: n = emit_insn_before (gen_trapb (), i); PUT_MODE (n, TImode); PUT_MODE (i, TImode); trap_pending = 0; shadow.used.i = 0; shadow.used.fp = 0; shadow.used.mem = 0; shadow.defd = shadow.used; } } } if ((exception_nesting > 0 || alpha_tp >= ALPHA_TP_FUNC) && GET_CODE (i) == INSN && GET_CODE (PATTERN (i)) != USE && GET_CODE (PATTERN (i)) != CLOBBER && get_attr_trap (i) == TRAP_YES) { if (optimize && !trap_pending) summarize_insn (PATTERN (i), &shadow, 0); trap_pending = 1; } } } /* Alpha can only issue instruction groups simultaneously if they are suitably aligned. This is very processor-specific. */ enum alphaev4_pipe { EV4_STOP = 0, EV4_IB0 = 1, EV4_IB1 = 2, EV4_IBX = 4 }; enum alphaev5_pipe { EV5_STOP = 0, EV5_NONE = 1, EV5_E01 = 2, EV5_E0 = 4, EV5_E1 = 8, EV5_FAM = 16, EV5_FA = 32, EV5_FM = 64 }; static enum alphaev4_pipe alphaev4_insn_pipe (rtx insn) { if (recog_memoized (insn) < 0) return EV4_STOP; if (get_attr_length (insn) != 4) return EV4_STOP; switch (get_attr_type (insn)) { case TYPE_ILD: case TYPE_FLD: return EV4_IBX; case TYPE_LDSYM: case TYPE_IADD: case TYPE_ILOG: case TYPE_ICMOV: case TYPE_ICMP: case TYPE_IST: case TYPE_FST: case TYPE_SHIFT: case TYPE_IMUL: case TYPE_FBR: return EV4_IB0; case TYPE_MISC: case TYPE_IBR: case TYPE_JSR: case TYPE_CALLPAL: case TYPE_FCPYS: case TYPE_FCMOV: case TYPE_FADD: case TYPE_FDIV: case TYPE_FMUL: return EV4_IB1; default: abort (); } } static enum alphaev5_pipe alphaev5_insn_pipe (rtx insn) { if (recog_memoized (insn) < 0) return EV5_STOP; if (get_attr_length (insn) != 4) return EV5_STOP; switch (get_attr_type (insn)) { case TYPE_ILD: case TYPE_FLD: case TYPE_LDSYM: case TYPE_IADD: case TYPE_ILOG: case TYPE_ICMOV: case TYPE_ICMP: return EV5_E01; case TYPE_IST: case TYPE_FST: case TYPE_SHIFT: case TYPE_IMUL: case TYPE_MISC: case TYPE_MVI: return EV5_E0; case TYPE_IBR: case TYPE_JSR: case TYPE_CALLPAL: return EV5_E1; case TYPE_FCPYS: return EV5_FAM; case TYPE_FBR: case TYPE_FCMOV: case TYPE_FADD: case TYPE_FDIV: return EV5_FA; case TYPE_FMUL: return EV5_FM; default: abort(); } } /* IN_USE is a mask of the slots currently filled within the insn group. The mask bits come from alphaev4_pipe above. If EV4_IBX is set, then the insn in EV4_IB0 can be swapped by the hardware into EV4_IB1. LEN is, of course, the length of the group in bytes. */ static rtx alphaev4_next_group (rtx insn, int *pin_use, int *plen) { int len, in_use; len = in_use = 0; if (! INSN_P (insn) || GET_CODE (PATTERN (insn)) == CLOBBER || GET_CODE (PATTERN (insn)) == USE) goto next_and_done; while (1) { enum alphaev4_pipe pipe; pipe = alphaev4_insn_pipe (insn); switch (pipe) { case EV4_STOP: /* Force complex instructions to start new groups. */ if (in_use) goto done; /* If this is a completely unrecognized insn, its an asm. We don't know how long it is, so record length as -1 to signal a needed realignment. */ if (recog_memoized (insn) < 0) len = -1; else len = get_attr_length (insn); goto next_and_done; case EV4_IBX: if (in_use & EV4_IB0) { if (in_use & EV4_IB1) goto done; in_use |= EV4_IB1; } else in_use |= EV4_IB0 | EV4_IBX; break; case EV4_IB0: if (in_use & EV4_IB0) { if (!(in_use & EV4_IBX) || (in_use & EV4_IB1)) goto done; in_use |= EV4_IB1; } in_use |= EV4_IB0; break; case EV4_IB1: if (in_use & EV4_IB1) goto done; in_use |= EV4_IB1; break; default: abort(); } len += 4; /* Haifa doesn't do well scheduling branches. */ if (GET_CODE (insn) == JUMP_INSN) goto next_and_done; next: insn = next_nonnote_insn (insn); if (!insn || ! INSN_P (insn)) goto done; /* Let Haifa tell us where it thinks insn group boundaries are. */ if (GET_MODE (insn) == TImode) goto done; if (GET_CODE (insn) == CLOBBER || GET_CODE (insn) == USE) goto next; } next_and_done: insn = next_nonnote_insn (insn); done: *plen = len; *pin_use = in_use; return insn; } /* IN_USE is a mask of the slots currently filled within the insn group. The mask bits come from alphaev5_pipe above. If EV5_E01 is set, then the insn in EV5_E0 can be swapped by the hardware into EV5_E1. LEN is, of course, the length of the group in bytes. */ static rtx alphaev5_next_group (rtx insn, int *pin_use, int *plen) { int len, in_use; len = in_use = 0; if (! INSN_P (insn) || GET_CODE (PATTERN (insn)) == CLOBBER || GET_CODE (PATTERN (insn)) == USE) goto next_and_done; while (1) { enum alphaev5_pipe pipe; pipe = alphaev5_insn_pipe (insn); switch (pipe) { case EV5_STOP: /* Force complex instructions to start new groups. */ if (in_use) goto done; /* If this is a completely unrecognized insn, its an asm. We don't know how long it is, so record length as -1 to signal a needed realignment. */ if (recog_memoized (insn) < 0) len = -1; else len = get_attr_length (insn); goto next_and_done; /* ??? Most of the places below, we would like to abort, as it would indicate an error either in Haifa, or in the scheduling description. Unfortunately, Haifa never schedules the last instruction of the BB, so we don't have an accurate TI bit to go off. */ case EV5_E01: if (in_use & EV5_E0) { if (in_use & EV5_E1) goto done; in_use |= EV5_E1; } else in_use |= EV5_E0 | EV5_E01; break; case EV5_E0: if (in_use & EV5_E0) { if (!(in_use & EV5_E01) || (in_use & EV5_E1)) goto done; in_use |= EV5_E1; } in_use |= EV5_E0; break; case EV5_E1: if (in_use & EV5_E1) goto done; in_use |= EV5_E1; break; case EV5_FAM: if (in_use & EV5_FA) { if (in_use & EV5_FM) goto done; in_use |= EV5_FM; } else in_use |= EV5_FA | EV5_FAM; break; case EV5_FA: if (in_use & EV5_FA) goto done; in_use |= EV5_FA; break; case EV5_FM: if (in_use & EV5_FM) goto done; in_use |= EV5_FM; break; case EV5_NONE: break; default: abort(); } len += 4; /* Haifa doesn't do well scheduling branches. */ /* ??? If this is predicted not-taken, slotting continues, except that no more IBR, FBR, or JSR insns may be slotted. */ if (GET_CODE (insn) == JUMP_INSN) goto next_and_done; next: insn = next_nonnote_insn (insn); if (!insn || ! INSN_P (insn)) goto done; /* Let Haifa tell us where it thinks insn group boundaries are. */ if (GET_MODE (insn) == TImode) goto done; if (GET_CODE (insn) == CLOBBER || GET_CODE (insn) == USE) goto next; } next_and_done: insn = next_nonnote_insn (insn); done: *plen = len; *pin_use = in_use; return insn; } static rtx alphaev4_next_nop (int *pin_use) { int in_use = *pin_use; rtx nop; if (!(in_use & EV4_IB0)) { in_use |= EV4_IB0; nop = gen_nop (); } else if ((in_use & (EV4_IBX|EV4_IB1)) == EV4_IBX) { in_use |= EV4_IB1; nop = gen_nop (); } else if (TARGET_FP && !(in_use & EV4_IB1)) { in_use |= EV4_IB1; nop = gen_fnop (); } else nop = gen_unop (); *pin_use = in_use; return nop; } static rtx alphaev5_next_nop (int *pin_use) { int in_use = *pin_use; rtx nop; if (!(in_use & EV5_E1)) { in_use |= EV5_E1; nop = gen_nop (); } else if (TARGET_FP && !(in_use & EV5_FA)) { in_use |= EV5_FA; nop = gen_fnop (); } else if (TARGET_FP && !(in_use & EV5_FM)) { in_use |= EV5_FM; nop = gen_fnop (); } else nop = gen_unop (); *pin_use = in_use; return nop; } /* The instruction group alignment main loop. */ static void alpha_align_insns (unsigned int max_align, rtx (*next_group) (rtx, int *, int *), rtx (*next_nop) (int *)) { /* ALIGN is the known alignment for the insn group. */ unsigned int align; /* OFS is the offset of the current insn in the insn group. */ int ofs; - int prev_in_use, in_use, len; + int prev_in_use, in_use, len, ldgp; rtx i, next; /* Let shorten branches care for assigning alignments to code labels. */ shorten_branches (get_insns ()); if (align_functions < 4) align = 4; else if ((unsigned int) align_functions < max_align) align = align_functions; else align = max_align; ofs = prev_in_use = 0; i = get_insns (); if (GET_CODE (i) == NOTE) i = next_nonnote_insn (i); + ldgp = alpha_function_needs_gp ? 8 : 0; + while (i) { next = (*next_group) (i, &in_use, &len); /* When we see a label, resync alignment etc. */ if (GET_CODE (i) == CODE_LABEL) { unsigned int new_align = 1 << label_to_alignment (i); if (new_align >= align) { align = new_align < max_align ? new_align : max_align; ofs = 0; } else if (ofs & (new_align-1)) ofs = (ofs | (new_align-1)) + 1; if (len != 0) abort(); } /* Handle complex instructions special. */ else if (in_use == 0) { /* Asms will have length < 0. This is a signal that we have lost alignment knowledge. Assume, however, that the asm will not mis-align instructions. */ if (len < 0) { ofs = 0; align = 4; len = 0; } } /* If the known alignment is smaller than the recognized insn group, realign the output. */ else if ((int) align < len) { unsigned int new_log_align = len > 8 ? 4 : 3; rtx prev, where; where = prev = prev_nonnote_insn (i); if (!where || GET_CODE (where) != CODE_LABEL) where = i; /* Can't realign between a call and its gp reload. */ if (! (TARGET_EXPLICIT_RELOCS && prev && GET_CODE (prev) == CALL_INSN)) { emit_insn_before (gen_realign (GEN_INT (new_log_align)), where); align = 1 << new_log_align; ofs = 0; } } + /* We may not insert padding inside the initial ldgp sequence. */ + else if (ldgp > 0) + ldgp -= len; + /* If the group won't fit in the same INT16 as the previous, we need to add padding to keep the group together. Rather than simply leaving the insn filling to the assembler, we can make use of the knowledge of what sorts of instructions were issued in the previous group to make sure that all of the added nops are really free. */ else if (ofs + len > (int) align) { int nop_count = (align - ofs) / 4; rtx where; /* Insert nops before labels, branches, and calls to truly merge the execution of the nops with the previous instruction group. */ where = prev_nonnote_insn (i); if (where) { if (GET_CODE (where) == CODE_LABEL) { rtx where2 = prev_nonnote_insn (where); if (where2 && GET_CODE (where2) == JUMP_INSN) where = where2; } else if (GET_CODE (where) == INSN) where = i; } else where = i; do emit_insn_before ((*next_nop)(&prev_in_use), where); while (--nop_count); ofs = 0; } ofs = (ofs + len) & (align - 1); prev_in_use = in_use; i = next; } } /* Machine dependent reorg pass. */ static void alpha_reorg (void) { if (alpha_tp != ALPHA_TP_PROG || flag_exceptions) alpha_handle_trap_shadows (); /* Due to the number of extra trapb insns, don't bother fixing up alignment when trap precision is instruction. Moreover, we can only do our job when sched2 is run. */ if (optimize && !optimize_size && alpha_tp != ALPHA_TP_INSN && flag_schedule_insns_after_reload) { if (alpha_cpu == PROCESSOR_EV4) alpha_align_insns (8, alphaev4_next_group, alphaev4_next_nop); else if (alpha_cpu == PROCESSOR_EV5) alpha_align_insns (16, alphaev5_next_group, alphaev5_next_nop); } } #if !TARGET_ABI_UNICOSMK #ifdef HAVE_STAMP_H #include #endif static void alpha_file_start (void) { #ifdef OBJECT_FORMAT_ELF /* If emitting dwarf2 debug information, we cannot generate a .file directive to start the file, as it will conflict with dwarf2out file numbers. So it's only useful when emitting mdebug output. */ targetm.file_start_file_directive = (write_symbols == DBX_DEBUG); #endif default_file_start (); #ifdef MS_STAMP fprintf (asm_out_file, "\t.verstamp %d %d\n", MS_STAMP, LS_STAMP); #endif fputs ("\t.set noreorder\n", asm_out_file); fputs ("\t.set volatile\n", asm_out_file); if (!TARGET_ABI_OPEN_VMS) fputs ("\t.set noat\n", asm_out_file); if (TARGET_EXPLICIT_RELOCS) fputs ("\t.set nomacro\n", asm_out_file); if (TARGET_SUPPORT_ARCH | TARGET_BWX | TARGET_MAX | TARGET_FIX | TARGET_CIX) fprintf (asm_out_file, "\t.arch %s\n", TARGET_CPU_EV6 ? "ev6" : (TARGET_CPU_EV5 ? (TARGET_MAX ? "pca56" : TARGET_BWX ? "ev56" : "ev5") : "ev4")); } #endif #ifdef OBJECT_FORMAT_ELF /* Switch to the section to which we should output X. The only thing special we do here is to honor small data. */ static void alpha_elf_select_rtx_section (enum machine_mode mode, rtx x, unsigned HOST_WIDE_INT align) { if (TARGET_SMALL_DATA && GET_MODE_SIZE (mode) <= g_switch_value) /* ??? Consider using mergeable sdata sections. */ sdata_section (); else default_elf_select_rtx_section (mode, x, align); } #endif /* OBJECT_FORMAT_ELF */ /* Structure to collect function names for final output in link section. */ /* Note that items marked with GTY can't be ifdef'ed out. */ enum links_kind {KIND_UNUSED, KIND_LOCAL, KIND_EXTERN}; enum reloc_kind {KIND_LINKAGE, KIND_CODEADDR}; struct alpha_links GTY(()) { int num; rtx linkage; enum links_kind lkind; enum reloc_kind rkind; }; struct alpha_funcs GTY(()) { int num; splay_tree GTY ((param1_is (char *), param2_is (struct alpha_links *))) links; }; static GTY ((param1_is (char *), param2_is (struct alpha_links *))) splay_tree alpha_links_tree; static GTY ((param1_is (tree), param2_is (struct alpha_funcs *))) splay_tree alpha_funcs_tree; static GTY(()) int alpha_funcs_num; #if TARGET_ABI_OPEN_VMS /* Return the VMS argument type corresponding to MODE. */ enum avms_arg_type alpha_arg_type (enum machine_mode mode) { switch (mode) { case SFmode: return TARGET_FLOAT_VAX ? FF : FS; case DFmode: return TARGET_FLOAT_VAX ? FD : FT; default: return I64; } } /* Return an rtx for an integer representing the VMS Argument Information register value. */ rtx alpha_arg_info_reg_val (CUMULATIVE_ARGS cum) { unsigned HOST_WIDE_INT regval = cum.num_args; int i; for (i = 0; i < 6; i++) regval |= ((int) cum.atypes[i]) << (i * 3 + 8); return GEN_INT (regval); } /* Make (or fake) .linkage entry for function call. IS_LOCAL is 0 if name is used in call, 1 if name is used in definition. Return an SYMBOL_REF rtx for the linkage. */ rtx alpha_need_linkage (const char *name, int is_local) { splay_tree_node node; struct alpha_links *al; if (name[0] == '*') name++; if (is_local) { struct alpha_funcs *cfaf; if (!alpha_funcs_tree) alpha_funcs_tree = splay_tree_new_ggc ((splay_tree_compare_fn) splay_tree_compare_pointers); cfaf = (struct alpha_funcs *) ggc_alloc (sizeof (struct alpha_funcs)); cfaf->links = 0; cfaf->num = ++alpha_funcs_num; splay_tree_insert (alpha_funcs_tree, (splay_tree_key) current_function_decl, (splay_tree_value) cfaf); } if (alpha_links_tree) { /* Is this name already defined? */ node = splay_tree_lookup (alpha_links_tree, (splay_tree_key) name); if (node) { al = (struct alpha_links *) node->value; if (is_local) { /* Defined here but external assumed. */ if (al->lkind == KIND_EXTERN) al->lkind = KIND_LOCAL; } else { /* Used here but unused assumed. */ if (al->lkind == KIND_UNUSED) al->lkind = KIND_LOCAL; } return al->linkage; } } else alpha_links_tree = splay_tree_new_ggc ((splay_tree_compare_fn) strcmp); al = (struct alpha_links *) ggc_alloc (sizeof (struct alpha_links)); name = ggc_strdup (name); /* Assume external if no definition. */ al->lkind = (is_local ? KIND_UNUSED : KIND_EXTERN); /* Ensure we have an IDENTIFIER so assemble_name can mark it used. */ get_identifier (name); /* Construct a SYMBOL_REF for us to call. */ { size_t name_len = strlen (name); char *linksym = alloca (name_len + 6); linksym[0] = '$'; memcpy (linksym + 1, name, name_len); memcpy (linksym + 1 + name_len, "..lk", 5); al->linkage = gen_rtx_SYMBOL_REF (Pmode, ggc_alloc_string (linksym, name_len + 5)); } splay_tree_insert (alpha_links_tree, (splay_tree_key) name, (splay_tree_value) al); return al->linkage; } rtx alpha_use_linkage (rtx linkage, tree cfundecl, int lflag, int rflag) { splay_tree_node cfunnode; struct alpha_funcs *cfaf; struct alpha_links *al; const char *name = XSTR (linkage, 0); cfaf = (struct alpha_funcs *) 0; al = (struct alpha_links *) 0; cfunnode = splay_tree_lookup (alpha_funcs_tree, (splay_tree_key) cfundecl); cfaf = (struct alpha_funcs *) cfunnode->value; if (cfaf->links) { splay_tree_node lnode; /* Is this name already defined? */ lnode = splay_tree_lookup (cfaf->links, (splay_tree_key) name); if (lnode) al = (struct alpha_links *) lnode->value; } else cfaf->links = splay_tree_new_ggc ((splay_tree_compare_fn) strcmp); if (!al) { size_t name_len; size_t buflen; char buf [512]; char *linksym; splay_tree_node node = 0; struct alpha_links *anl; if (name[0] == '*') name++; name_len = strlen (name); al = (struct alpha_links *) ggc_alloc (sizeof (struct alpha_links)); al->num = cfaf->num; node = splay_tree_lookup (alpha_links_tree, (splay_tree_key) name); if (node) { anl = (struct alpha_links *) node->value; al->lkind = anl->lkind; } sprintf (buf, "$%d..%s..lk", cfaf->num, name); buflen = strlen (buf); linksym = alloca (buflen + 1); memcpy (linksym, buf, buflen + 1); al->linkage = gen_rtx_SYMBOL_REF (Pmode, ggc_alloc_string (linksym, buflen + 1)); splay_tree_insert (cfaf->links, (splay_tree_key) name, (splay_tree_value) al); } if (rflag) al->rkind = KIND_CODEADDR; else al->rkind = KIND_LINKAGE; if (lflag) return gen_rtx_MEM (Pmode, plus_constant (al->linkage, 8)); else return al->linkage; } static int alpha_write_one_linkage (splay_tree_node node, void *data) { const char *const name = (const char *) node->key; struct alpha_links *link = (struct alpha_links *) node->value; FILE *stream = (FILE *) data; fprintf (stream, "$%d..%s..lk:\n", link->num, name); if (link->rkind == KIND_CODEADDR) { if (link->lkind == KIND_LOCAL) { /* Local and used */ fprintf (stream, "\t.quad %s..en\n", name); } else { /* External and used, request code address. */ fprintf (stream, "\t.code_address %s\n", name); } } else { if (link->lkind == KIND_LOCAL) { /* Local and used, build linkage pair. */ fprintf (stream, "\t.quad %s..en\n", name); fprintf (stream, "\t.quad %s\n", name); } else { /* External and used, request linkage pair. */ fprintf (stream, "\t.linkage %s\n", name); } } return 0; } static void alpha_write_linkage (FILE *stream, const char *funname, tree fundecl) { splay_tree_node node; struct alpha_funcs *func; link_section (); fprintf (stream, "\t.align 3\n"); node = splay_tree_lookup (alpha_funcs_tree, (splay_tree_key) fundecl); func = (struct alpha_funcs *) node->value; fputs ("\t.name ", stream); assemble_name (stream, funname); fputs ("..na\n", stream); ASM_OUTPUT_LABEL (stream, funname); fprintf (stream, "\t.pdesc "); assemble_name (stream, funname); fprintf (stream, "..en,%s\n", alpha_procedure_type == PT_STACK ? "stack" : alpha_procedure_type == PT_REGISTER ? "reg" : "null"); if (func->links) { splay_tree_foreach (func->links, alpha_write_one_linkage, stream); /* splay_tree_delete (func->links); */ } } /* Given a decl, a section name, and whether the decl initializer has relocs, choose attributes for the section. */ #define SECTION_VMS_OVERLAY SECTION_FORGET #define SECTION_VMS_GLOBAL SECTION_MACH_DEP #define SECTION_VMS_INITIALIZE (SECTION_VMS_GLOBAL << 1) static unsigned int vms_section_type_flags (tree decl, const char *name, int reloc) { unsigned int flags = default_section_type_flags (decl, name, reloc); if (decl && DECL_ATTRIBUTES (decl) && lookup_attribute ("overlaid", DECL_ATTRIBUTES (decl))) flags |= SECTION_VMS_OVERLAY; if (decl && DECL_ATTRIBUTES (decl) && lookup_attribute ("global", DECL_ATTRIBUTES (decl))) flags |= SECTION_VMS_GLOBAL; if (decl && DECL_ATTRIBUTES (decl) && lookup_attribute ("initialize", DECL_ATTRIBUTES (decl))) flags |= SECTION_VMS_INITIALIZE; return flags; } /* Switch to an arbitrary section NAME with attributes as specified by FLAGS. ALIGN specifies any known alignment requirements for the section; 0 if the default should be used. */ static void vms_asm_named_section (const char *name, unsigned int flags) { fputc ('\n', asm_out_file); fprintf (asm_out_file, ".section\t%s", name); if (flags & SECTION_VMS_OVERLAY) fprintf (asm_out_file, ",OVR"); if (flags & SECTION_VMS_GLOBAL) fprintf (asm_out_file, ",GBL"); if (flags & SECTION_VMS_INITIALIZE) fprintf (asm_out_file, ",NOMOD"); if (flags & SECTION_DEBUG) fprintf (asm_out_file, ",NOWRT"); fputc ('\n', asm_out_file); } /* Record an element in the table of global constructors. SYMBOL is a SYMBOL_REF of the function to be called; PRIORITY is a number between 0 and MAX_INIT_PRIORITY. Differs from default_ctors_section_asm_out_constructor in that the width of the .ctors entry is always 64 bits, rather than the 32 bits used by a normal pointer. */ static void vms_asm_out_constructor (rtx symbol, int priority ATTRIBUTE_UNUSED) { ctors_section (); assemble_align (BITS_PER_WORD); assemble_integer (symbol, UNITS_PER_WORD, BITS_PER_WORD, 1); } static void vms_asm_out_destructor (rtx symbol, int priority ATTRIBUTE_UNUSED) { dtors_section (); assemble_align (BITS_PER_WORD); assemble_integer (symbol, UNITS_PER_WORD, BITS_PER_WORD, 1); } #else rtx alpha_need_linkage (const char *name ATTRIBUTE_UNUSED, int is_local ATTRIBUTE_UNUSED) { return NULL_RTX; } rtx alpha_use_linkage (rtx linkage ATTRIBUTE_UNUSED, tree cfundecl ATTRIBUTE_UNUSED, int lflag ATTRIBUTE_UNUSED, int rflag ATTRIBUTE_UNUSED) { return NULL_RTX; } #endif /* TARGET_ABI_OPEN_VMS */ #if TARGET_ABI_UNICOSMK /* Define the offset between two registers, one to be eliminated, and the other its replacement, at the start of a routine. */ int unicosmk_initial_elimination_offset (int from, int to) { int fixed_size; fixed_size = alpha_sa_size(); if (fixed_size != 0) fixed_size += 48; if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM) return -fixed_size; else if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM) return 0; else if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) return (ALPHA_ROUND (current_function_outgoing_args_size) + ALPHA_ROUND (get_frame_size())); else if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) return (ALPHA_ROUND (fixed_size) + ALPHA_ROUND (get_frame_size() + current_function_outgoing_args_size)); else abort (); } /* Output the module name for .ident and .end directives. We have to strip directories and add make sure that the module name starts with a letter or '$'. */ static void unicosmk_output_module_name (FILE *file) { const char *name = lbasename (main_input_filename); unsigned len = strlen (name); char *clean_name = alloca (len + 2); char *ptr = clean_name; /* CAM only accepts module names that start with a letter or '$'. We prefix the module name with a '$' if necessary. */ if (!ISALPHA (*name)) *ptr++ = '$'; memcpy (ptr, name, len + 1); clean_symbol_name (clean_name); fputs (clean_name, file); } /* Output the definition of a common variable. */ void unicosmk_output_common (FILE *file, const char *name, int size, int align) { tree name_tree; printf ("T3E__: common %s\n", name); common_section (); fputs("\t.endp\n\n\t.psect ", file); assemble_name(file, name); fprintf(file, ",%d,common\n", floor_log2 (align / BITS_PER_UNIT)); fprintf(file, "\t.byte\t0:%d\n", size); /* Mark the symbol as defined in this module. */ name_tree = get_identifier (name); TREE_ASM_WRITTEN (name_tree) = 1; } #define SECTION_PUBLIC SECTION_MACH_DEP #define SECTION_MAIN (SECTION_PUBLIC << 1) static int current_section_align; static unsigned int unicosmk_section_type_flags (tree decl, const char *name, int reloc ATTRIBUTE_UNUSED) { unsigned int flags = default_section_type_flags (decl, name, reloc); if (!decl) return flags; if (TREE_CODE (decl) == FUNCTION_DECL) { current_section_align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT); if (align_functions_log > current_section_align) current_section_align = align_functions_log; if (! strcmp (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)), "main")) flags |= SECTION_MAIN; } else current_section_align = floor_log2 (DECL_ALIGN (decl) / BITS_PER_UNIT); if (TREE_PUBLIC (decl)) flags |= SECTION_PUBLIC; return flags; } /* Generate a section name for decl and associate it with the declaration. */ static void unicosmk_unique_section (tree decl, int reloc ATTRIBUTE_UNUSED) { const char *name; int len; if (!decl) abort (); name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); name = default_strip_name_encoding (name); len = strlen (name); if (TREE_CODE (decl) == FUNCTION_DECL) { char *string; /* It is essential that we prefix the section name here because otherwise the section names generated for constructors and destructors confuse collect2. */ string = alloca (len + 6); sprintf (string, "code@%s", name); DECL_SECTION_NAME (decl) = build_string (len + 5, string); } else if (TREE_PUBLIC (decl)) DECL_SECTION_NAME (decl) = build_string (len, name); else { char *string; string = alloca (len + 6); sprintf (string, "data@%s", name); DECL_SECTION_NAME (decl) = build_string (len + 5, string); } } /* Switch to an arbitrary section NAME with attributes as specified by FLAGS. ALIGN specifies any known alignment requirements for the section; 0 if the default should be used. */ static void unicosmk_asm_named_section (const char *name, unsigned int flags) { const char *kind; /* Close the previous section. */ fputs ("\t.endp\n\n", asm_out_file); /* Find out what kind of section we are opening. */ if (flags & SECTION_MAIN) fputs ("\t.start\tmain\n", asm_out_file); if (flags & SECTION_CODE) kind = "code"; else if (flags & SECTION_PUBLIC) kind = "common"; else kind = "data"; if (current_section_align != 0) fprintf (asm_out_file, "\t.psect\t%s,%d,%s\n", name, current_section_align, kind); else fprintf (asm_out_file, "\t.psect\t%s,%s\n", name, kind); } static void unicosmk_insert_attributes (tree decl, tree *attr_ptr ATTRIBUTE_UNUSED) { if (DECL_P (decl) && (TREE_PUBLIC (decl) || TREE_CODE (decl) == FUNCTION_DECL)) unicosmk_unique_section (decl, 0); } /* Output an alignment directive. We have to use the macro 'gcc@code@align' in code sections because .align fill unused space with zeroes. */ void unicosmk_output_align (FILE *file, int align) { if (inside_function) fprintf (file, "\tgcc@code@align\t%d\n", align); else fprintf (file, "\t.align\t%d\n", align); } /* Add a case vector to the current function's list of deferred case vectors. Case vectors have to be put into a separate section because CAM does not allow data definitions in code sections. */ void unicosmk_defer_case_vector (rtx lab, rtx vec) { struct machine_function *machine = cfun->machine; vec = gen_rtx_EXPR_LIST (VOIDmode, lab, vec); machine->addr_list = gen_rtx_EXPR_LIST (VOIDmode, vec, machine->addr_list); } /* Output a case vector. */ static void unicosmk_output_addr_vec (FILE *file, rtx vec) { rtx lab = XEXP (vec, 0); rtx body = XEXP (vec, 1); int vlen = XVECLEN (body, 0); int idx; (*targetm.asm_out.internal_label) (file, "L", CODE_LABEL_NUMBER (lab)); for (idx = 0; idx < vlen; idx++) { ASM_OUTPUT_ADDR_VEC_ELT (file, CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 0, idx), 0))); } } /* Output current function's deferred case vectors. */ static void unicosmk_output_deferred_case_vectors (FILE *file) { struct machine_function *machine = cfun->machine; rtx t; if (machine->addr_list == NULL_RTX) return; data_section (); for (t = machine->addr_list; t; t = XEXP (t, 1)) unicosmk_output_addr_vec (file, XEXP (t, 0)); } /* Generate the name of the SSIB section for the current function. */ #define SSIB_PREFIX "__SSIB_" #define SSIB_PREFIX_LEN 7 static const char * unicosmk_ssib_name (void) { /* This is ok since CAM won't be able to deal with names longer than that anyway. */ static char name[256]; rtx x; const char *fnname; int len; x = DECL_RTL (cfun->decl); if (GET_CODE (x) != MEM) abort (); x = XEXP (x, 0); if (GET_CODE (x) != SYMBOL_REF) abort (); fnname = XSTR (x, 0); len = strlen (fnname); if (len + SSIB_PREFIX_LEN > 255) len = 255 - SSIB_PREFIX_LEN; strcpy (name, SSIB_PREFIX); strncpy (name + SSIB_PREFIX_LEN, fnname, len); name[len + SSIB_PREFIX_LEN] = 0; return name; } /* Set up the dynamic subprogram information block (DSIB) and update the frame pointer register ($15) for subroutines which have a frame. If the subroutine doesn't have a frame, simply increment $15. */ static void unicosmk_gen_dsib (unsigned long *imaskP) { if (alpha_procedure_type == PT_STACK) { const char *ssib_name; rtx mem; /* Allocate 64 bytes for the DSIB. */ FRP (emit_insn (gen_adddi3 (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (-64)))); emit_insn (gen_blockage ()); /* Save the return address. */ mem = gen_rtx_MEM (DImode, plus_constant (stack_pointer_rtx, 56)); set_mem_alias_set (mem, alpha_sr_alias_set); FRP (emit_move_insn (mem, gen_rtx_REG (DImode, REG_RA))); (*imaskP) &= ~(1UL << REG_RA); /* Save the old frame pointer. */ mem = gen_rtx_MEM (DImode, plus_constant (stack_pointer_rtx, 48)); set_mem_alias_set (mem, alpha_sr_alias_set); FRP (emit_move_insn (mem, hard_frame_pointer_rtx)); (*imaskP) &= ~(1UL << HARD_FRAME_POINTER_REGNUM); emit_insn (gen_blockage ()); /* Store the SSIB pointer. */ ssib_name = ggc_strdup (unicosmk_ssib_name ()); mem = gen_rtx_MEM (DImode, plus_constant (stack_pointer_rtx, 32)); set_mem_alias_set (mem, alpha_sr_alias_set); FRP (emit_move_insn (gen_rtx_REG (DImode, 5), gen_rtx_SYMBOL_REF (Pmode, ssib_name))); FRP (emit_move_insn (mem, gen_rtx_REG (DImode, 5))); /* Save the CIW index. */ mem = gen_rtx_MEM (DImode, plus_constant (stack_pointer_rtx, 24)); set_mem_alias_set (mem, alpha_sr_alias_set); FRP (emit_move_insn (mem, gen_rtx_REG (DImode, 25))); emit_insn (gen_blockage ()); /* Set the new frame pointer. */ FRP (emit_insn (gen_adddi3 (hard_frame_pointer_rtx, stack_pointer_rtx, GEN_INT (64)))); } else { /* Increment the frame pointer register to indicate that we do not have a frame. */ FRP (emit_insn (gen_adddi3 (hard_frame_pointer_rtx, hard_frame_pointer_rtx, GEN_INT (1)))); } } /* Output the static subroutine information block for the current function. */ static void unicosmk_output_ssib (FILE *file, const char *fnname) { int len; int i; rtx x; rtx ciw; struct machine_function *machine = cfun->machine; ssib_section (); fprintf (file, "\t.endp\n\n\t.psect\t%s%s,data\n", user_label_prefix, unicosmk_ssib_name ()); /* Some required stuff and the function name length. */ len = strlen (fnname); fprintf (file, "\t.quad\t^X20008%2.2X28\n", len); /* Saved registers ??? We don't do that yet. */ fputs ("\t.quad\t0\n", file); /* Function address. */ fputs ("\t.quad\t", file); assemble_name (file, fnname); putc ('\n', file); fputs ("\t.quad\t0\n", file); fputs ("\t.quad\t0\n", file); /* Function name. ??? We do it the same way Cray CC does it but this could be simplified. */ for( i = 0; i < len; i++ ) fprintf (file, "\t.byte\t%d\n", (int)(fnname[i])); if( (len % 8) == 0 ) fputs ("\t.quad\t0\n", file); else fprintf (file, "\t.bits\t%d : 0\n", (8 - (len % 8))*8); /* All call information words used in the function. */ for (x = machine->first_ciw; x; x = XEXP (x, 1)) { ciw = XEXP (x, 0); #if HOST_BITS_PER_WIDE_INT == 32 fprintf (file, "\t.quad\t" HOST_WIDE_INT_PRINT_DOUBLE_HEX "\n", CONST_DOUBLE_HIGH (ciw), CONST_DOUBLE_LOW (ciw)); #else fprintf (file, "\t.quad\t" HOST_WIDE_INT_PRINT_HEX "\n", INTVAL (ciw)); #endif } } /* Add a call information word (CIW) to the list of the current function's CIWs and return its index. X is a CONST_INT or CONST_DOUBLE representing the CIW. */ rtx unicosmk_add_call_info_word (rtx x) { rtx node; struct machine_function *machine = cfun->machine; node = gen_rtx_EXPR_LIST (VOIDmode, x, NULL_RTX); if (machine->first_ciw == NULL_RTX) machine->first_ciw = node; else XEXP (machine->last_ciw, 1) = node; machine->last_ciw = node; ++machine->ciw_count; return GEN_INT (machine->ciw_count + strlen (current_function_name ())/8 + 5); } static char unicosmk_section_buf[100]; char * unicosmk_text_section (void) { static int count = 0; sprintf (unicosmk_section_buf, "\t.endp\n\n\t.psect\tgcc@text___%d,code", count++); return unicosmk_section_buf; } char * unicosmk_data_section (void) { static int count = 1; sprintf (unicosmk_section_buf, "\t.endp\n\n\t.psect\tgcc@data___%d,data", count++); return unicosmk_section_buf; } /* The Cray assembler doesn't accept extern declarations for symbols which are defined in the same file. We have to keep track of all global symbols which are referenced and/or defined in a source file and output extern declarations for those which are referenced but not defined at the end of file. */ /* List of identifiers for which an extern declaration might have to be emitted. */ /* FIXME: needs to use GC, so it can be saved and restored for PCH. */ struct unicosmk_extern_list { struct unicosmk_extern_list *next; const char *name; }; static struct unicosmk_extern_list *unicosmk_extern_head = 0; /* Output extern declarations which are required for every asm file. */ static void unicosmk_output_default_externs (FILE *file) { static const char *const externs[] = { "__T3E_MISMATCH" }; int i; int n; n = ARRAY_SIZE (externs); for (i = 0; i < n; i++) fprintf (file, "\t.extern\t%s\n", externs[i]); } /* Output extern declarations for global symbols which are have been referenced but not defined. */ static void unicosmk_output_externs (FILE *file) { struct unicosmk_extern_list *p; const char *real_name; int len; tree name_tree; len = strlen (user_label_prefix); for (p = unicosmk_extern_head; p != 0; p = p->next) { /* We have to strip the encoding and possibly remove user_label_prefix from the identifier in order to handle -fleading-underscore and explicit asm names correctly (cf. gcc.dg/asm-names-1.c). */ real_name = default_strip_name_encoding (p->name); if (len && p->name[0] == '*' && !memcmp (real_name, user_label_prefix, len)) real_name += len; name_tree = get_identifier (real_name); if (! TREE_ASM_WRITTEN (name_tree)) { TREE_ASM_WRITTEN (name_tree) = 1; fputs ("\t.extern\t", file); assemble_name (file, p->name); putc ('\n', file); } } } /* Record an extern. */ void unicosmk_add_extern (const char *name) { struct unicosmk_extern_list *p; p = (struct unicosmk_extern_list *) xmalloc (sizeof (struct unicosmk_extern_list)); p->next = unicosmk_extern_head; p->name = name; unicosmk_extern_head = p; } /* The Cray assembler generates incorrect code if identifiers which conflict with register names are used as instruction operands. We have to replace such identifiers with DEX expressions. */ /* Structure to collect identifiers which have been replaced by DEX expressions. */ /* FIXME: needs to use GC, so it can be saved and restored for PCH. */ struct unicosmk_dex { struct unicosmk_dex *next; const char *name; }; /* List of identifiers which have been replaced by DEX expressions. The DEX number is determined by the position in the list. */ static struct unicosmk_dex *unicosmk_dex_list = NULL; /* The number of elements in the DEX list. */ static int unicosmk_dex_count = 0; /* Check if NAME must be replaced by a DEX expression. */ static int unicosmk_special_name (const char *name) { if (name[0] == '*') ++name; if (name[0] == '$') ++name; if (name[0] != 'r' && name[0] != 'f' && name[0] != 'R' && name[0] != 'F') return 0; switch (name[1]) { case '1': case '2': return (name[2] == '\0' || (ISDIGIT (name[2]) && name[3] == '\0')); case '3': return (name[2] == '\0' || ((name[2] == '0' || name[2] == '1') && name[3] == '\0')); default: return (ISDIGIT (name[1]) && name[2] == '\0'); } } /* Return the DEX number if X must be replaced by a DEX expression and 0 otherwise. */ static int unicosmk_need_dex (rtx x) { struct unicosmk_dex *dex; const char *name; int i; if (GET_CODE (x) != SYMBOL_REF) return 0; name = XSTR (x,0); if (! unicosmk_special_name (name)) return 0; i = unicosmk_dex_count; for (dex = unicosmk_dex_list; dex; dex = dex->next) { if (! strcmp (name, dex->name)) return i; --i; } dex = (struct unicosmk_dex *) xmalloc (sizeof (struct unicosmk_dex)); dex->name = name; dex->next = unicosmk_dex_list; unicosmk_dex_list = dex; ++unicosmk_dex_count; return unicosmk_dex_count; } /* Output the DEX definitions for this file. */ static void unicosmk_output_dex (FILE *file) { struct unicosmk_dex *dex; int i; if (unicosmk_dex_list == NULL) return; fprintf (file, "\t.dexstart\n"); i = unicosmk_dex_count; for (dex = unicosmk_dex_list; dex; dex = dex->next) { fprintf (file, "\tDEX (%d) = ", i); assemble_name (file, dex->name); putc ('\n', file); --i; } fprintf (file, "\t.dexend\n"); } /* Output text that to appear at the beginning of an assembler file. */ static void unicosmk_file_start (void) { int i; fputs ("\t.ident\t", asm_out_file); unicosmk_output_module_name (asm_out_file); fputs ("\n\n", asm_out_file); /* The Unicos/Mk assembler uses different register names. Instead of trying to support them, we simply use micro definitions. */ /* CAM has different register names: rN for the integer register N and fN for the floating-point register N. Instead of trying to use these in alpha.md, we define the symbols $N and $fN to refer to the appropriate register. */ for (i = 0; i < 32; ++i) fprintf (asm_out_file, "$%d <- r%d\n", i, i); for (i = 0; i < 32; ++i) fprintf (asm_out_file, "$f%d <- f%d\n", i, i); putc ('\n', asm_out_file); /* The .align directive fill unused space with zeroes which does not work in code sections. We define the macro 'gcc@code@align' which uses nops instead. Note that it assumes that code sections always have the biggest possible alignment since . refers to the current offset from the beginning of the section. */ fputs ("\t.macro gcc@code@align n\n", asm_out_file); fputs ("gcc@n@bytes = 1 << n\n", asm_out_file); fputs ("gcc@here = . % gcc@n@bytes\n", asm_out_file); fputs ("\t.if ne, gcc@here, 0\n", asm_out_file); fputs ("\t.repeat (gcc@n@bytes - gcc@here) / 4\n", asm_out_file); fputs ("\tbis r31,r31,r31\n", asm_out_file); fputs ("\t.endr\n", asm_out_file); fputs ("\t.endif\n", asm_out_file); fputs ("\t.endm gcc@code@align\n\n", asm_out_file); /* Output extern declarations which should always be visible. */ unicosmk_output_default_externs (asm_out_file); /* Open a dummy section. We always need to be inside a section for the section-switching code to work correctly. ??? This should be a module id or something like that. I still have to figure out what the rules for those are. */ fputs ("\n\t.psect\t$SG00000,data\n", asm_out_file); } /* Output text to appear at the end of an assembler file. This includes all pending extern declarations and DEX expressions. */ static void unicosmk_file_end (void) { fputs ("\t.endp\n\n", asm_out_file); /* Output all pending externs. */ unicosmk_output_externs (asm_out_file); /* Output dex definitions used for functions whose names conflict with register names. */ unicosmk_output_dex (asm_out_file); fputs ("\t.end\t", asm_out_file); unicosmk_output_module_name (asm_out_file); putc ('\n', asm_out_file); } #else static void unicosmk_output_deferred_case_vectors (FILE *file ATTRIBUTE_UNUSED) {} static void unicosmk_gen_dsib (unsigned long *imaskP ATTRIBUTE_UNUSED) {} static void unicosmk_output_ssib (FILE * file ATTRIBUTE_UNUSED, const char * fnname ATTRIBUTE_UNUSED) {} rtx unicosmk_add_call_info_word (rtx x ATTRIBUTE_UNUSED) { return NULL_RTX; } static int unicosmk_need_dex (rtx x ATTRIBUTE_UNUSED) { return 0; } #endif /* TARGET_ABI_UNICOSMK */ static void alpha_init_libfuncs (void) { if (TARGET_ABI_UNICOSMK) { /* Prevent gcc from generating calls to __divsi3. */ set_optab_libfunc (sdiv_optab, SImode, 0); set_optab_libfunc (udiv_optab, SImode, 0); /* Use the functions provided by the system library for DImode integer division. */ set_optab_libfunc (sdiv_optab, DImode, "$sldiv"); set_optab_libfunc (udiv_optab, DImode, "$uldiv"); } else if (TARGET_ABI_OPEN_VMS) { /* Use the VMS runtime library functions for division and remainder. */ set_optab_libfunc (sdiv_optab, SImode, "OTS$DIV_I"); set_optab_libfunc (sdiv_optab, DImode, "OTS$DIV_L"); set_optab_libfunc (udiv_optab, SImode, "OTS$DIV_UI"); set_optab_libfunc (udiv_optab, DImode, "OTS$DIV_UL"); set_optab_libfunc (smod_optab, SImode, "OTS$REM_I"); set_optab_libfunc (smod_optab, DImode, "OTS$REM_L"); set_optab_libfunc (umod_optab, SImode, "OTS$REM_UI"); set_optab_libfunc (umod_optab, DImode, "OTS$REM_UL"); } } /* Initialize the GCC target structure. */ #if TARGET_ABI_OPEN_VMS # undef TARGET_ATTRIBUTE_TABLE # define TARGET_ATTRIBUTE_TABLE vms_attribute_table # undef TARGET_SECTION_TYPE_FLAGS # define TARGET_SECTION_TYPE_FLAGS vms_section_type_flags #endif #undef TARGET_IN_SMALL_DATA_P #define TARGET_IN_SMALL_DATA_P alpha_in_small_data_p #if TARGET_ABI_UNICOSMK # undef TARGET_INSERT_ATTRIBUTES # define TARGET_INSERT_ATTRIBUTES unicosmk_insert_attributes # undef TARGET_SECTION_TYPE_FLAGS # define TARGET_SECTION_TYPE_FLAGS unicosmk_section_type_flags # undef TARGET_ASM_UNIQUE_SECTION # define TARGET_ASM_UNIQUE_SECTION unicosmk_unique_section # undef TARGET_ASM_GLOBALIZE_LABEL # define TARGET_ASM_GLOBALIZE_LABEL hook_void_FILEptr_constcharptr #endif #undef TARGET_ASM_ALIGNED_HI_OP #define TARGET_ASM_ALIGNED_HI_OP "\t.word\t" #undef TARGET_ASM_ALIGNED_DI_OP #define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t" /* Default unaligned ops are provided for ELF systems. To get unaligned data for non-ELF systems, we have to turn off auto alignment. */ #ifndef OBJECT_FORMAT_ELF #undef TARGET_ASM_UNALIGNED_HI_OP #define TARGET_ASM_UNALIGNED_HI_OP "\t.align 0\n\t.word\t" #undef TARGET_ASM_UNALIGNED_SI_OP #define TARGET_ASM_UNALIGNED_SI_OP "\t.align 0\n\t.long\t" #undef TARGET_ASM_UNALIGNED_DI_OP #define TARGET_ASM_UNALIGNED_DI_OP "\t.align 0\n\t.quad\t" #endif #ifdef OBJECT_FORMAT_ELF #undef TARGET_ASM_SELECT_RTX_SECTION #define TARGET_ASM_SELECT_RTX_SECTION alpha_elf_select_rtx_section #endif #undef TARGET_ASM_FUNCTION_END_PROLOGUE #define TARGET_ASM_FUNCTION_END_PROLOGUE alpha_output_function_end_prologue #undef TARGET_INIT_LIBFUNCS #define TARGET_INIT_LIBFUNCS alpha_init_libfuncs #if TARGET_ABI_UNICOSMK #undef TARGET_ASM_FILE_START #define TARGET_ASM_FILE_START unicosmk_file_start #undef TARGET_ASM_FILE_END #define TARGET_ASM_FILE_END unicosmk_file_end #else #undef TARGET_ASM_FILE_START #define TARGET_ASM_FILE_START alpha_file_start #undef TARGET_ASM_FILE_START_FILE_DIRECTIVE #define TARGET_ASM_FILE_START_FILE_DIRECTIVE true #endif #undef TARGET_SCHED_ADJUST_COST #define TARGET_SCHED_ADJUST_COST alpha_adjust_cost #undef TARGET_SCHED_ISSUE_RATE #define TARGET_SCHED_ISSUE_RATE alpha_issue_rate #undef TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE #define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE \ alpha_use_dfa_pipeline_interface #undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD #define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD \ alpha_multipass_dfa_lookahead #undef TARGET_HAVE_TLS #define TARGET_HAVE_TLS HAVE_AS_TLS #undef TARGET_INIT_BUILTINS #define TARGET_INIT_BUILTINS alpha_init_builtins #undef TARGET_EXPAND_BUILTIN #define TARGET_EXPAND_BUILTIN alpha_expand_builtin #undef TARGET_FUNCTION_OK_FOR_SIBCALL #define TARGET_FUNCTION_OK_FOR_SIBCALL alpha_function_ok_for_sibcall #undef TARGET_CANNOT_COPY_INSN_P #define TARGET_CANNOT_COPY_INSN_P alpha_cannot_copy_insn_p #undef TARGET_CANNOT_FORCE_CONST_MEM #define TARGET_CANNOT_FORCE_CONST_MEM alpha_cannot_force_const_mem #if TARGET_ABI_OSF #undef TARGET_ASM_OUTPUT_MI_THUNK #define TARGET_ASM_OUTPUT_MI_THUNK alpha_output_mi_thunk_osf #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK #define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true #endif #undef TARGET_RTX_COSTS #define TARGET_RTX_COSTS alpha_rtx_costs #undef TARGET_ADDRESS_COST #define TARGET_ADDRESS_COST hook_int_rtx_0 #undef TARGET_MACHINE_DEPENDENT_REORG #define TARGET_MACHINE_DEPENDENT_REORG alpha_reorg #undef TARGET_PROMOTE_FUNCTION_ARGS #define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true #undef TARGET_PROMOTE_FUNCTION_RETURN #define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true #undef TARGET_PROMOTE_PROTOTYPES #define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_false #undef TARGET_STRUCT_VALUE_RTX #define TARGET_STRUCT_VALUE_RTX hook_rtx_tree_int_null #undef TARGET_RETURN_IN_MEMORY #define TARGET_RETURN_IN_MEMORY alpha_return_in_memory #undef TARGET_SETUP_INCOMING_VARARGS #define TARGET_SETUP_INCOMING_VARARGS alpha_setup_incoming_varargs #undef TARGET_STRICT_ARGUMENT_NAMING #define TARGET_STRICT_ARGUMENT_NAMING hook_bool_CUMULATIVE_ARGS_true #undef TARGET_PRETEND_OUTGOING_VARARGS_NAMED #define TARGET_PRETEND_OUTGOING_VARARGS_NAMED hook_bool_CUMULATIVE_ARGS_true #undef TARGET_SPLIT_COMPLEX_ARG #define TARGET_SPLIT_COMPLEX_ARG alpha_split_complex_arg #undef TARGET_BUILD_BUILTIN_VA_LIST #define TARGET_BUILD_BUILTIN_VA_LIST alpha_build_builtin_va_list struct gcc_target targetm = TARGET_INITIALIZER; #include "gt-alpha.h" diff --git a/contrib/gcc/config/i386/i386.c b/contrib/gcc/config/i386/i386.c index d0040fb591d3..0360ff1f197d 100644 --- a/contrib/gcc/config/i386/i386.c +++ b/contrib/gcc/config/i386/i386.c @@ -1,16098 +1,16099 @@ /* Subroutines used for code generation on IA-32. Copyright (C) 1988, 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* $FreeBSD$ */ #include "config.h" #include "system.h" #include "coretypes.h" #include "tm.h" #include "rtl.h" #include "tree.h" #include "tm_p.h" #include "regs.h" #include "hard-reg-set.h" #include "real.h" #include "insn-config.h" #include "conditions.h" #include "output.h" #include "insn-attr.h" #include "flags.h" #include "except.h" #include "function.h" #include "recog.h" #include "expr.h" #include "optabs.h" #include "toplev.h" #include "basic-block.h" #include "ggc.h" #include "target.h" #include "target-def.h" #include "langhooks.h" #include "cgraph.h" #ifndef CHECK_STACK_LIMIT #define CHECK_STACK_LIMIT (-1) #endif /* Return index of given mode in mult and division cost tables. */ #define MODE_INDEX(mode) \ ((mode) == QImode ? 0 \ : (mode) == HImode ? 1 \ : (mode) == SImode ? 2 \ : (mode) == DImode ? 3 \ : 4) /* Processor costs (relative to an add) */ static const struct processor_costs size_cost = { /* costs for tunning for size */ 2, /* cost of an add instruction */ 3, /* cost of a lea instruction */ 2, /* variable shift costs */ 3, /* constant shift costs */ {3, 3, 3, 3, 5}, /* cost of starting a multiply */ 0, /* cost of multiply per each bit set */ {3, 3, 3, 3, 5}, /* cost of a divide/mod */ 3, /* cost of movsx */ 3, /* cost of movzx */ 0, /* "large" insn */ 2, /* MOVE_RATIO */ 2, /* cost for loading QImode using movzbl */ {2, 2, 2}, /* cost of loading integer registers in QImode, HImode and SImode. Relative to reg-reg move (2). */ {2, 2, 2}, /* cost of storing integer registers */ 2, /* cost of reg,reg fld/fst */ {2, 2, 2}, /* cost of loading fp registers in SFmode, DFmode and XFmode */ {2, 2, 2}, /* cost of loading integer registers */ 3, /* cost of moving MMX register */ {3, 3}, /* cost of loading MMX registers in SImode and DImode */ {3, 3}, /* cost of storing MMX registers in SImode and DImode */ 3, /* cost of moving SSE register */ {3, 3, 3}, /* cost of loading SSE registers in SImode, DImode and TImode */ {3, 3, 3}, /* cost of storing SSE registers in SImode, DImode and TImode */ 3, /* MMX or SSE register to integer */ 0, /* size of prefetch block */ 0, /* number of parallel prefetches */ 1, /* Branch cost */ 2, /* cost of FADD and FSUB insns. */ 2, /* cost of FMUL instruction. */ 2, /* cost of FDIV instruction. */ 2, /* cost of FABS instruction. */ 2, /* cost of FCHS instruction. */ 2, /* cost of FSQRT instruction. */ }; /* Processor costs (relative to an add) */ static const struct processor_costs i386_cost = { /* 386 specific costs */ 1, /* cost of an add instruction */ 1, /* cost of a lea instruction */ 3, /* variable shift costs */ 2, /* constant shift costs */ {6, 6, 6, 6, 6}, /* cost of starting a multiply */ 1, /* cost of multiply per each bit set */ {23, 23, 23, 23, 23}, /* cost of a divide/mod */ 3, /* cost of movsx */ 2, /* cost of movzx */ 15, /* "large" insn */ 3, /* MOVE_RATIO */ 4, /* cost for loading QImode using movzbl */ {2, 4, 2}, /* cost of loading integer registers in QImode, HImode and SImode. Relative to reg-reg move (2). */ {2, 4, 2}, /* cost of storing integer registers */ 2, /* cost of reg,reg fld/fst */ {8, 8, 8}, /* cost of loading fp registers in SFmode, DFmode and XFmode */ {8, 8, 8}, /* cost of loading integer registers */ 2, /* cost of moving MMX register */ {4, 8}, /* cost of loading MMX registers in SImode and DImode */ {4, 8}, /* cost of storing MMX registers in SImode and DImode */ 2, /* cost of moving SSE register */ {4, 8, 16}, /* cost of loading SSE registers in SImode, DImode and TImode */ {4, 8, 16}, /* cost of storing SSE registers in SImode, DImode and TImode */ 3, /* MMX or SSE register to integer */ 0, /* size of prefetch block */ 0, /* number of parallel prefetches */ 1, /* Branch cost */ 23, /* cost of FADD and FSUB insns. */ 27, /* cost of FMUL instruction. */ 88, /* cost of FDIV instruction. */ 22, /* cost of FABS instruction. */ 24, /* cost of FCHS instruction. */ 122, /* cost of FSQRT instruction. */ }; static const struct processor_costs i486_cost = { /* 486 specific costs */ 1, /* cost of an add instruction */ 1, /* cost of a lea instruction */ 3, /* variable shift costs */ 2, /* constant shift costs */ {12, 12, 12, 12, 12}, /* cost of starting a multiply */ 1, /* cost of multiply per each bit set */ {40, 40, 40, 40, 40}, /* cost of a divide/mod */ 3, /* cost of movsx */ 2, /* cost of movzx */ 15, /* "large" insn */ 3, /* MOVE_RATIO */ 4, /* cost for loading QImode using movzbl */ {2, 4, 2}, /* cost of loading integer registers in QImode, HImode and SImode. Relative to reg-reg move (2). */ {2, 4, 2}, /* cost of storing integer registers */ 2, /* cost of reg,reg fld/fst */ {8, 8, 8}, /* cost of loading fp registers in SFmode, DFmode and XFmode */ {8, 8, 8}, /* cost of loading integer registers */ 2, /* cost of moving MMX register */ {4, 8}, /* cost of loading MMX registers in SImode and DImode */ {4, 8}, /* cost of storing MMX registers in SImode and DImode */ 2, /* cost of moving SSE register */ {4, 8, 16}, /* cost of loading SSE registers in SImode, DImode and TImode */ {4, 8, 16}, /* cost of storing SSE registers in SImode, DImode and TImode */ 3, /* MMX or SSE register to integer */ 0, /* size of prefetch block */ 0, /* number of parallel prefetches */ 1, /* Branch cost */ 8, /* cost of FADD and FSUB insns. */ 16, /* cost of FMUL instruction. */ 73, /* cost of FDIV instruction. */ 3, /* cost of FABS instruction. */ 3, /* cost of FCHS instruction. */ 83, /* cost of FSQRT instruction. */ }; static const struct processor_costs pentium_cost = { 1, /* cost of an add instruction */ 1, /* cost of a lea instruction */ 4, /* variable shift costs */ 1, /* constant shift costs */ {11, 11, 11, 11, 11}, /* cost of starting a multiply */ 0, /* cost of multiply per each bit set */ {25, 25, 25, 25, 25}, /* cost of a divide/mod */ 3, /* cost of movsx */ 2, /* cost of movzx */ 8, /* "large" insn */ 6, /* MOVE_RATIO */ 6, /* cost for loading QImode using movzbl */ {2, 4, 2}, /* cost of loading integer registers in QImode, HImode and SImode. Relative to reg-reg move (2). */ {2, 4, 2}, /* cost of storing integer registers */ 2, /* cost of reg,reg fld/fst */ {2, 2, 6}, /* cost of loading fp registers in SFmode, DFmode and XFmode */ {4, 4, 6}, /* cost of loading integer registers */ 8, /* cost of moving MMX register */ {8, 8}, /* cost of loading MMX registers in SImode and DImode */ {8, 8}, /* cost of storing MMX registers in SImode and DImode */ 2, /* cost of moving SSE register */ {4, 8, 16}, /* cost of loading SSE registers in SImode, DImode and TImode */ {4, 8, 16}, /* cost of storing SSE registers in SImode, DImode and TImode */ 3, /* MMX or SSE register to integer */ 0, /* size of prefetch block */ 0, /* number of parallel prefetches */ 2, /* Branch cost */ 3, /* cost of FADD and FSUB insns. */ 3, /* cost of FMUL instruction. */ 39, /* cost of FDIV instruction. */ 1, /* cost of FABS instruction. */ 1, /* cost of FCHS instruction. */ 70, /* cost of FSQRT instruction. */ }; static const struct processor_costs pentiumpro_cost = { 1, /* cost of an add instruction */ 1, /* cost of a lea instruction */ 1, /* variable shift costs */ 1, /* constant shift costs */ {4, 4, 4, 4, 4}, /* cost of starting a multiply */ 0, /* cost of multiply per each bit set */ {17, 17, 17, 17, 17}, /* cost of a divide/mod */ 1, /* cost of movsx */ 1, /* cost of movzx */ 8, /* "large" insn */ 6, /* MOVE_RATIO */ 2, /* cost for loading QImode using movzbl */ {4, 4, 4}, /* cost of loading integer registers in QImode, HImode and SImode. Relative to reg-reg move (2). */ {2, 2, 2}, /* cost of storing integer registers */ 2, /* cost of reg,reg fld/fst */ {2, 2, 6}, /* cost of loading fp registers in SFmode, DFmode and XFmode */ {4, 4, 6}, /* cost of loading integer registers */ 2, /* cost of moving MMX register */ {2, 2}, /* cost of loading MMX registers in SImode and DImode */ {2, 2}, /* cost of storing MMX registers in SImode and DImode */ 2, /* cost of moving SSE register */ {2, 2, 8}, /* cost of loading SSE registers in SImode, DImode and TImode */ {2, 2, 8}, /* cost of storing SSE registers in SImode, DImode and TImode */ 3, /* MMX or SSE register to integer */ 32, /* size of prefetch block */ 6, /* number of parallel prefetches */ 2, /* Branch cost */ 3, /* cost of FADD and FSUB insns. */ 5, /* cost of FMUL instruction. */ 56, /* cost of FDIV instruction. */ 2, /* cost of FABS instruction. */ 2, /* cost of FCHS instruction. */ 56, /* cost of FSQRT instruction. */ }; static const struct processor_costs k6_cost = { 1, /* cost of an add instruction */ 2, /* cost of a lea instruction */ 1, /* variable shift costs */ 1, /* constant shift costs */ {3, 3, 3, 3, 3}, /* cost of starting a multiply */ 0, /* cost of multiply per each bit set */ {18, 18, 18, 18, 18}, /* cost of a divide/mod */ 2, /* cost of movsx */ 2, /* cost of movzx */ 8, /* "large" insn */ 4, /* MOVE_RATIO */ 3, /* cost for loading QImode using movzbl */ {4, 5, 4}, /* cost of loading integer registers in QImode, HImode and SImode. Relative to reg-reg move (2). */ {2, 3, 2}, /* cost of storing integer registers */ 4, /* cost of reg,reg fld/fst */ {6, 6, 6}, /* cost of loading fp registers in SFmode, DFmode and XFmode */ {4, 4, 4}, /* cost of loading integer registers */ 2, /* cost of moving MMX register */ {2, 2}, /* cost of loading MMX registers in SImode and DImode */ {2, 2}, /* cost of storing MMX registers in SImode and DImode */ 2, /* cost of moving SSE register */ {2, 2, 8}, /* cost of loading SSE registers in SImode, DImode and TImode */ {2, 2, 8}, /* cost of storing SSE registers in SImode, DImode and TImode */ 6, /* MMX or SSE register to integer */ 32, /* size of prefetch block */ 1, /* number of parallel prefetches */ 1, /* Branch cost */ 2, /* cost of FADD and FSUB insns. */ 2, /* cost of FMUL instruction. */ 56, /* cost of FDIV instruction. */ 2, /* cost of FABS instruction. */ 2, /* cost of FCHS instruction. */ 56, /* cost of FSQRT instruction. */ }; static const struct processor_costs athlon_cost = { 1, /* cost of an add instruction */ 2, /* cost of a lea instruction */ 1, /* variable shift costs */ 1, /* constant shift costs */ {5, 5, 5, 5, 5}, /* cost of starting a multiply */ 0, /* cost of multiply per each bit set */ {18, 26, 42, 74, 74}, /* cost of a divide/mod */ 1, /* cost of movsx */ 1, /* cost of movzx */ 8, /* "large" insn */ 9, /* MOVE_RATIO */ 4, /* cost for loading QImode using movzbl */ {3, 4, 3}, /* cost of loading integer registers in QImode, HImode and SImode. Relative to reg-reg move (2). */ {3, 4, 3}, /* cost of storing integer registers */ 4, /* cost of reg,reg fld/fst */ {4, 4, 12}, /* cost of loading fp registers in SFmode, DFmode and XFmode */ {6, 6, 8}, /* cost of loading integer registers */ 2, /* cost of moving MMX register */ {4, 4}, /* cost of loading MMX registers in SImode and DImode */ {4, 4}, /* cost of storing MMX registers in SImode and DImode */ 2, /* cost of moving SSE register */ {4, 4, 6}, /* cost of loading SSE registers in SImode, DImode and TImode */ {4, 4, 5}, /* cost of storing SSE registers in SImode, DImode and TImode */ 5, /* MMX or SSE register to integer */ 64, /* size of prefetch block */ 6, /* number of parallel prefetches */ 2, /* Branch cost */ 4, /* cost of FADD and FSUB insns. */ 4, /* cost of FMUL instruction. */ 24, /* cost of FDIV instruction. */ 2, /* cost of FABS instruction. */ 2, /* cost of FCHS instruction. */ 35, /* cost of FSQRT instruction. */ }; static const struct processor_costs k8_cost = { 1, /* cost of an add instruction */ 2, /* cost of a lea instruction */ 1, /* variable shift costs */ 1, /* constant shift costs */ {3, 4, 3, 4, 5}, /* cost of starting a multiply */ 0, /* cost of multiply per each bit set */ {18, 26, 42, 74, 74}, /* cost of a divide/mod */ 1, /* cost of movsx */ 1, /* cost of movzx */ 8, /* "large" insn */ 9, /* MOVE_RATIO */ 4, /* cost for loading QImode using movzbl */ {3, 4, 3}, /* cost of loading integer registers in QImode, HImode and SImode. Relative to reg-reg move (2). */ {3, 4, 3}, /* cost of storing integer registers */ 4, /* cost of reg,reg fld/fst */ {4, 4, 12}, /* cost of loading fp registers in SFmode, DFmode and XFmode */ {6, 6, 8}, /* cost of loading integer registers */ 2, /* cost of moving MMX register */ {3, 3}, /* cost of loading MMX registers in SImode and DImode */ {4, 4}, /* cost of storing MMX registers in SImode and DImode */ 2, /* cost of moving SSE register */ {4, 3, 6}, /* cost of loading SSE registers in SImode, DImode and TImode */ {4, 4, 5}, /* cost of storing SSE registers in SImode, DImode and TImode */ 5, /* MMX or SSE register to integer */ 64, /* size of prefetch block */ 6, /* number of parallel prefetches */ 2, /* Branch cost */ 4, /* cost of FADD and FSUB insns. */ 4, /* cost of FMUL instruction. */ 19, /* cost of FDIV instruction. */ 2, /* cost of FABS instruction. */ 2, /* cost of FCHS instruction. */ 35, /* cost of FSQRT instruction. */ }; static const struct processor_costs pentium4_cost = { 1, /* cost of an add instruction */ 1, /* cost of a lea instruction */ 4, /* variable shift costs */ 4, /* constant shift costs */ {15, 15, 15, 15, 15}, /* cost of starting a multiply */ 0, /* cost of multiply per each bit set */ {56, 56, 56, 56, 56}, /* cost of a divide/mod */ 1, /* cost of movsx */ 1, /* cost of movzx */ 16, /* "large" insn */ 6, /* MOVE_RATIO */ 2, /* cost for loading QImode using movzbl */ {4, 5, 4}, /* cost of loading integer registers in QImode, HImode and SImode. Relative to reg-reg move (2). */ {2, 3, 2}, /* cost of storing integer registers */ 2, /* cost of reg,reg fld/fst */ {2, 2, 6}, /* cost of loading fp registers in SFmode, DFmode and XFmode */ {4, 4, 6}, /* cost of loading integer registers */ 2, /* cost of moving MMX register */ {2, 2}, /* cost of loading MMX registers in SImode and DImode */ {2, 2}, /* cost of storing MMX registers in SImode and DImode */ 12, /* cost of moving SSE register */ {12, 12, 12}, /* cost of loading SSE registers in SImode, DImode and TImode */ {2, 2, 8}, /* cost of storing SSE registers in SImode, DImode and TImode */ 10, /* MMX or SSE register to integer */ 64, /* size of prefetch block */ 6, /* number of parallel prefetches */ 2, /* Branch cost */ 5, /* cost of FADD and FSUB insns. */ 7, /* cost of FMUL instruction. */ 43, /* cost of FDIV instruction. */ 2, /* cost of FABS instruction. */ 2, /* cost of FCHS instruction. */ 43, /* cost of FSQRT instruction. */ }; const struct processor_costs *ix86_cost = &pentium_cost; /* Processor feature/optimization bitmasks. */ #define m_386 (1< to_allocate <- FRAME_POINTER [frame] ( ) [padding2] / */ struct ix86_frame { int nregs; int padding1; int va_arg_size; HOST_WIDE_INT frame; int padding2; int outgoing_arguments_size; int red_zone_size; HOST_WIDE_INT to_allocate; /* The offsets relative to ARG_POINTER. */ HOST_WIDE_INT frame_pointer_offset; HOST_WIDE_INT hard_frame_pointer_offset; HOST_WIDE_INT stack_pointer_offset; /* When save_regs_using_mov is set, emit prologue using move instead of push instructions. */ bool save_regs_using_mov; }; /* Used to enable/disable debugging features. */ const char *ix86_debug_arg_string, *ix86_debug_addr_string; /* Code model option as passed by user. */ const char *ix86_cmodel_string; /* Parsed value. */ enum cmodel ix86_cmodel; /* Asm dialect. */ const char *ix86_asm_string; enum asm_dialect ix86_asm_dialect = ASM_ATT; /* TLS dialext. */ const char *ix86_tls_dialect_string; enum tls_dialect ix86_tls_dialect = TLS_DIALECT_GNU; /* Which unit we are generating floating point math for. */ enum fpmath_unit ix86_fpmath; /* Which cpu are we scheduling for. */ enum processor_type ix86_tune; /* Which instruction set architecture to use. */ enum processor_type ix86_arch; /* Strings to hold which cpu and instruction set architecture to use. */ const char *ix86_tune_string; /* for -mtune= */ const char *ix86_arch_string; /* for -march= */ const char *ix86_fpmath_string; /* for -mfpmath= */ /* # of registers to use to pass arguments. */ const char *ix86_regparm_string; /* true if sse prefetch instruction is not NOOP. */ int x86_prefetch_sse; /* ix86_regparm_string as a number */ int ix86_regparm; /* Alignment to use for loops and jumps: */ /* Power of two alignment for loops. */ const char *ix86_align_loops_string; /* Power of two alignment for non-loop jumps. */ const char *ix86_align_jumps_string; /* Power of two alignment for stack boundary in bytes. */ const char *ix86_preferred_stack_boundary_string; /* Preferred alignment for stack boundary in bits. */ int ix86_preferred_stack_boundary; /* Values 1-5: see jump.c */ int ix86_branch_cost; const char *ix86_branch_cost_string; /* Power of two alignment for functions. */ const char *ix86_align_funcs_string; /* Prefix built by ASM_GENERATE_INTERNAL_LABEL. */ static char internal_label_prefix[16]; static int internal_label_prefix_len; static int local_symbolic_operand (rtx, enum machine_mode); static int tls_symbolic_operand_1 (rtx, enum tls_model); static void output_pic_addr_const (FILE *, rtx, int); static void put_condition_code (enum rtx_code, enum machine_mode, int, int, FILE *); static const char *get_some_local_dynamic_name (void); static int get_some_local_dynamic_name_1 (rtx *, void *); static rtx maybe_get_pool_constant (rtx); static rtx ix86_expand_int_compare (enum rtx_code, rtx, rtx); static enum rtx_code ix86_prepare_fp_compare_args (enum rtx_code, rtx *, rtx *); static bool ix86_fixed_condition_code_regs (unsigned int *, unsigned int *); static enum machine_mode ix86_cc_modes_compatible (enum machine_mode, enum machine_mode); static rtx get_thread_pointer (int); static rtx legitimize_tls_address (rtx, enum tls_model, int); static void get_pc_thunk_name (char [32], unsigned int); static rtx gen_push (rtx); static int memory_address_length (rtx addr); static int ix86_flags_dependant (rtx, rtx, enum attr_type); static int ix86_agi_dependant (rtx, rtx, enum attr_type); static enum attr_ppro_uops ix86_safe_ppro_uops (rtx); static void ix86_dump_ppro_packet (FILE *); static void ix86_reorder_insn (rtx *, rtx *); static struct machine_function * ix86_init_machine_status (void); static int ix86_split_to_parts (rtx, rtx *, enum machine_mode); static int ix86_nsaved_regs (void); static void ix86_emit_save_regs (void); static void ix86_emit_save_regs_using_mov (rtx, HOST_WIDE_INT); static void ix86_emit_restore_regs_using_mov (rtx, HOST_WIDE_INT, int); static void ix86_output_function_epilogue (FILE *, HOST_WIDE_INT); static void ix86_sched_reorder_ppro (rtx *, rtx *); static HOST_WIDE_INT ix86_GOT_alias_set (void); static void ix86_adjust_counter (rtx, HOST_WIDE_INT); static rtx ix86_expand_aligntest (rtx, int); static void ix86_expand_strlensi_unroll_1 (rtx, rtx, rtx); static int ix86_issue_rate (void); static int ix86_adjust_cost (rtx, rtx, rtx, int); static void ix86_sched_init (FILE *, int, int); static int ix86_sched_reorder (FILE *, int, rtx *, int *, int); static int ix86_variable_issue (FILE *, int, rtx, int); static int ia32_use_dfa_pipeline_interface (void); static int ia32_multipass_dfa_lookahead (void); static void ix86_init_mmx_sse_builtins (void); static rtx x86_this_parameter (tree); static void x86_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree); static bool x86_can_output_mi_thunk (tree, HOST_WIDE_INT, HOST_WIDE_INT, tree); static void x86_file_start (void); static void ix86_reorg (void); static bool ix86_expand_carry_flag_compare (enum rtx_code, rtx, rtx, rtx*); static tree ix86_build_builtin_va_list (void); struct ix86_address { rtx base, index, disp; HOST_WIDE_INT scale; enum ix86_address_seg { SEG_DEFAULT, SEG_FS, SEG_GS } seg; }; static int ix86_decompose_address (rtx, struct ix86_address *); static int ix86_address_cost (rtx); static bool ix86_cannot_force_const_mem (rtx); static rtx ix86_delegitimize_address (rtx); struct builtin_description; static rtx ix86_expand_sse_comi (const struct builtin_description *, tree, rtx); static rtx ix86_expand_sse_compare (const struct builtin_description *, tree, rtx); static rtx ix86_expand_unop1_builtin (enum insn_code, tree, rtx); static rtx ix86_expand_unop_builtin (enum insn_code, tree, rtx, int); static rtx ix86_expand_binop_builtin (enum insn_code, tree, rtx); static rtx ix86_expand_store_builtin (enum insn_code, tree); static rtx safe_vector_operand (rtx, enum machine_mode); static enum rtx_code ix86_fp_compare_code_to_integer (enum rtx_code); static void ix86_fp_comparison_codes (enum rtx_code code, enum rtx_code *, enum rtx_code *, enum rtx_code *); static rtx ix86_expand_fp_compare (enum rtx_code, rtx, rtx, rtx, rtx *, rtx *); static int ix86_fp_comparison_arithmetics_cost (enum rtx_code code); static int ix86_fp_comparison_fcomi_cost (enum rtx_code code); static int ix86_fp_comparison_sahf_cost (enum rtx_code code); static int ix86_fp_comparison_cost (enum rtx_code code); static unsigned int ix86_select_alt_pic_regnum (void); static int ix86_save_reg (unsigned int, int); static void ix86_compute_frame_layout (struct ix86_frame *); static int ix86_comp_type_attributes (tree, tree); static int ix86_function_regparm (tree, tree); const struct attribute_spec ix86_attribute_table[]; static bool ix86_function_ok_for_sibcall (tree, tree); static tree ix86_handle_cdecl_attribute (tree *, tree, tree, int, bool *); static tree ix86_handle_regparm_attribute (tree *, tree, tree, int, bool *); static int ix86_value_regno (enum machine_mode); static bool contains_128bit_aligned_vector_p (tree); static bool ix86_ms_bitfield_layout_p (tree); static tree ix86_handle_struct_attribute (tree *, tree, tree, int, bool *); static int extended_reg_mentioned_1 (rtx *, void *); static bool ix86_rtx_costs (rtx, int, int, int *); static int min_insn_size (rtx); static void k8_avoid_jump_misspredicts (void); #if defined (DO_GLOBAL_CTORS_BODY) && defined (HAS_INIT_SECTION) static void ix86_svr3_asm_out_constructor (rtx, int); #endif /* Register class used for passing given 64bit part of the argument. These represent classes as documented by the PS ABI, with the exception of SSESF, SSEDF classes, that are basically SSE class, just gcc will use SF or DFmode move instead of DImode to avoid reformatting penalties. Similarly we play games with INTEGERSI_CLASS to use cheaper SImode moves whenever possible (upper half does contain padding). */ enum x86_64_reg_class { X86_64_NO_CLASS, X86_64_INTEGER_CLASS, X86_64_INTEGERSI_CLASS, X86_64_SSE_CLASS, X86_64_SSESF_CLASS, X86_64_SSEDF_CLASS, X86_64_SSEUP_CLASS, X86_64_X87_CLASS, X86_64_X87UP_CLASS, X86_64_MEMORY_CLASS }; static const char * const x86_64_reg_class_name[] = {"no", "integer", "integerSI", "sse", "sseSF", "sseDF", "sseup", "x87", "x87up", "no"}; #define MAX_CLASSES 4 static int classify_argument (enum machine_mode, tree, enum x86_64_reg_class [MAX_CLASSES], int); static int examine_argument (enum machine_mode, tree, int, int *, int *); static rtx construct_container (enum machine_mode, tree, int, int, int, const int *, int); static enum x86_64_reg_class merge_classes (enum x86_64_reg_class, enum x86_64_reg_class); /* Table of constants used by fldpi, fldln2, etc.... */ static REAL_VALUE_TYPE ext_80387_constants_table [5]; static bool ext_80387_constants_init = 0; static void init_ext_80387_constants (void); /* Initialize the GCC target structure. */ #undef TARGET_ATTRIBUTE_TABLE #define TARGET_ATTRIBUTE_TABLE ix86_attribute_table #ifdef TARGET_DLLIMPORT_DECL_ATTRIBUTES # undef TARGET_MERGE_DECL_ATTRIBUTES # define TARGET_MERGE_DECL_ATTRIBUTES merge_dllimport_decl_attributes #endif #undef TARGET_COMP_TYPE_ATTRIBUTES #define TARGET_COMP_TYPE_ATTRIBUTES ix86_comp_type_attributes #undef TARGET_INIT_BUILTINS #define TARGET_INIT_BUILTINS ix86_init_builtins #undef TARGET_EXPAND_BUILTIN #define TARGET_EXPAND_BUILTIN ix86_expand_builtin #undef TARGET_ASM_FUNCTION_EPILOGUE #define TARGET_ASM_FUNCTION_EPILOGUE ix86_output_function_epilogue #undef TARGET_ASM_OPEN_PAREN #define TARGET_ASM_OPEN_PAREN "" #undef TARGET_ASM_CLOSE_PAREN #define TARGET_ASM_CLOSE_PAREN "" #undef TARGET_ASM_ALIGNED_HI_OP #define TARGET_ASM_ALIGNED_HI_OP ASM_SHORT #undef TARGET_ASM_ALIGNED_SI_OP #define TARGET_ASM_ALIGNED_SI_OP ASM_LONG #ifdef ASM_QUAD #undef TARGET_ASM_ALIGNED_DI_OP #define TARGET_ASM_ALIGNED_DI_OP ASM_QUAD #endif #undef TARGET_ASM_UNALIGNED_HI_OP #define TARGET_ASM_UNALIGNED_HI_OP TARGET_ASM_ALIGNED_HI_OP #undef TARGET_ASM_UNALIGNED_SI_OP #define TARGET_ASM_UNALIGNED_SI_OP TARGET_ASM_ALIGNED_SI_OP #undef TARGET_ASM_UNALIGNED_DI_OP #define TARGET_ASM_UNALIGNED_DI_OP TARGET_ASM_ALIGNED_DI_OP #undef TARGET_SCHED_ADJUST_COST #define TARGET_SCHED_ADJUST_COST ix86_adjust_cost #undef TARGET_SCHED_ISSUE_RATE #define TARGET_SCHED_ISSUE_RATE ix86_issue_rate #undef TARGET_SCHED_VARIABLE_ISSUE #define TARGET_SCHED_VARIABLE_ISSUE ix86_variable_issue #undef TARGET_SCHED_INIT #define TARGET_SCHED_INIT ix86_sched_init #undef TARGET_SCHED_REORDER #define TARGET_SCHED_REORDER ix86_sched_reorder #undef TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE #define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE \ ia32_use_dfa_pipeline_interface #undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD #define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD \ ia32_multipass_dfa_lookahead #undef TARGET_FUNCTION_OK_FOR_SIBCALL #define TARGET_FUNCTION_OK_FOR_SIBCALL ix86_function_ok_for_sibcall #ifdef HAVE_AS_TLS #undef TARGET_HAVE_TLS #define TARGET_HAVE_TLS true #endif #undef TARGET_CANNOT_FORCE_CONST_MEM #define TARGET_CANNOT_FORCE_CONST_MEM ix86_cannot_force_const_mem #undef TARGET_DELEGITIMIZE_ADDRESS #define TARGET_DELEGITIMIZE_ADDRESS ix86_delegitimize_address #undef TARGET_MS_BITFIELD_LAYOUT_P #define TARGET_MS_BITFIELD_LAYOUT_P ix86_ms_bitfield_layout_p #undef TARGET_ASM_OUTPUT_MI_THUNK #define TARGET_ASM_OUTPUT_MI_THUNK x86_output_mi_thunk #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK #define TARGET_ASM_CAN_OUTPUT_MI_THUNK x86_can_output_mi_thunk #undef TARGET_ASM_FILE_START #define TARGET_ASM_FILE_START x86_file_start #undef TARGET_RTX_COSTS #define TARGET_RTX_COSTS ix86_rtx_costs #undef TARGET_ADDRESS_COST #define TARGET_ADDRESS_COST ix86_address_cost #undef TARGET_FIXED_CONDITION_CODE_REGS #define TARGET_FIXED_CONDITION_CODE_REGS ix86_fixed_condition_code_regs #undef TARGET_CC_MODES_COMPATIBLE #define TARGET_CC_MODES_COMPATIBLE ix86_cc_modes_compatible #undef TARGET_MACHINE_DEPENDENT_REORG #define TARGET_MACHINE_DEPENDENT_REORG ix86_reorg #undef TARGET_BUILD_BUILTIN_VA_LIST #define TARGET_BUILD_BUILTIN_VA_LIST ix86_build_builtin_va_list struct gcc_target targetm = TARGET_INITIALIZER; /* The svr4 ABI for the i386 says that records and unions are returned in memory. */ #ifndef DEFAULT_PCC_STRUCT_RETURN #define DEFAULT_PCC_STRUCT_RETURN 1 #endif /* 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 (void) { int i; /* Comes from final.c -- no real reason to change it. */ #define MAX_CODE_ALIGN 16 static struct ptt { const struct processor_costs *cost; /* Processor costs */ const int target_enable; /* Target flags to enable. */ const int target_disable; /* Target flags to disable. */ const int align_loop; /* Default alignments. */ const int align_loop_max_skip; const int align_jump; const int align_jump_max_skip; const int align_func; } const processor_target_table[PROCESSOR_max] = { {&i386_cost, 0, 0, 4, 3, 4, 3, 4}, {&i486_cost, 0, 0, 16, 15, 16, 15, 16}, {&pentium_cost, 0, 0, 16, 7, 16, 7, 16}, {&pentiumpro_cost, 0, 0, 16, 15, 16, 7, 16}, {&k6_cost, 0, 0, 32, 7, 32, 7, 32}, {&athlon_cost, 0, 0, 16, 7, 16, 7, 16}, {&pentium4_cost, 0, 0, 0, 0, 0, 0, 0}, {&k8_cost, 0, 0, 16, 7, 16, 7, 16} }; static const char * const cpu_names[] = TARGET_CPU_DEFAULT_NAMES; static struct pta { const char *const name; /* processor name or nickname. */ const enum processor_type processor; const enum pta_flags { PTA_SSE = 1, PTA_SSE2 = 2, PTA_SSE3 = 4, PTA_MMX = 8, PTA_PREFETCH_SSE = 16, PTA_3DNOW = 32, PTA_3DNOW_A = 64, PTA_64BIT = 128 } flags; } const processor_alias_table[] = { {"i386", PROCESSOR_I386, 0}, {"i486", PROCESSOR_I486, 0}, {"i586", PROCESSOR_PENTIUM, 0}, {"pentium", PROCESSOR_PENTIUM, 0}, {"pentium-mmx", PROCESSOR_PENTIUM, PTA_MMX}, {"winchip-c6", PROCESSOR_I486, PTA_MMX}, {"winchip2", PROCESSOR_I486, PTA_MMX | PTA_3DNOW}, {"c3", PROCESSOR_I486, PTA_MMX | PTA_3DNOW}, {"c3-2", PROCESSOR_PENTIUMPRO, PTA_MMX | PTA_PREFETCH_SSE | PTA_SSE}, {"i686", PROCESSOR_PENTIUMPRO, 0}, {"pentiumpro", PROCESSOR_PENTIUMPRO, 0}, {"pentium2", PROCESSOR_PENTIUMPRO, PTA_MMX}, {"pentium3", PROCESSOR_PENTIUMPRO, PTA_MMX | PTA_SSE | PTA_PREFETCH_SSE}, {"pentium3m", PROCESSOR_PENTIUMPRO, PTA_MMX | PTA_SSE | PTA_PREFETCH_SSE}, {"pentium-m", PROCESSOR_PENTIUMPRO, PTA_MMX | PTA_SSE | PTA_PREFETCH_SSE | PTA_SSE2}, {"pentium4", PROCESSOR_PENTIUM4, PTA_SSE | PTA_SSE2 | PTA_MMX | PTA_PREFETCH_SSE}, {"pentium4m", PROCESSOR_PENTIUM4, PTA_SSE | PTA_SSE2 | PTA_MMX | PTA_PREFETCH_SSE}, {"prescott", PROCESSOR_PENTIUM4, PTA_SSE | PTA_SSE2 | PTA_SSE3 | PTA_MMX | PTA_PREFETCH_SSE}, {"nocona", PROCESSOR_PENTIUM4, PTA_SSE | PTA_SSE2 | PTA_SSE3 | PTA_64BIT | PTA_MMX | PTA_PREFETCH_SSE}, {"k6", PROCESSOR_K6, PTA_MMX}, {"k6-2", PROCESSOR_K6, PTA_MMX | PTA_3DNOW}, {"k6-3", PROCESSOR_K6, PTA_MMX | PTA_3DNOW}, {"athlon", PROCESSOR_ATHLON, PTA_MMX | PTA_PREFETCH_SSE | PTA_3DNOW | PTA_3DNOW_A}, {"athlon-tbird", PROCESSOR_ATHLON, PTA_MMX | PTA_PREFETCH_SSE | PTA_3DNOW | PTA_3DNOW_A}, {"athlon-4", PROCESSOR_ATHLON, PTA_MMX | PTA_PREFETCH_SSE | PTA_3DNOW | PTA_3DNOW_A | PTA_SSE}, {"athlon-xp", PROCESSOR_ATHLON, PTA_MMX | PTA_PREFETCH_SSE | PTA_3DNOW | PTA_3DNOW_A | PTA_SSE}, {"athlon-mp", PROCESSOR_ATHLON, PTA_MMX | PTA_PREFETCH_SSE | PTA_3DNOW | PTA_3DNOW_A | PTA_SSE}, {"x86-64", PROCESSOR_K8, PTA_MMX | PTA_PREFETCH_SSE | PTA_64BIT | PTA_SSE | PTA_SSE2 }, {"k8", PROCESSOR_K8, PTA_MMX | PTA_PREFETCH_SSE | PTA_3DNOW | PTA_64BIT | PTA_3DNOW_A | PTA_SSE | PTA_SSE2}, {"opteron", PROCESSOR_K8, PTA_MMX | PTA_PREFETCH_SSE | PTA_3DNOW | PTA_64BIT | PTA_3DNOW_A | PTA_SSE | PTA_SSE2}, {"athlon64", PROCESSOR_K8, PTA_MMX | PTA_PREFETCH_SSE | PTA_3DNOW | PTA_64BIT | PTA_3DNOW_A | PTA_SSE | PTA_SSE2}, {"athlon-fx", PROCESSOR_K8, PTA_MMX | PTA_PREFETCH_SSE | PTA_3DNOW | PTA_64BIT | PTA_3DNOW_A | PTA_SSE | PTA_SSE2}, }; int const pta_size = ARRAY_SIZE (processor_alias_table); /* Set the default values for switches whose default depends on TARGET_64BIT in case they weren't overwritten by command line options. */ if (TARGET_64BIT) { if (flag_omit_frame_pointer == 2) flag_omit_frame_pointer = 1; if (flag_asynchronous_unwind_tables == 2) flag_asynchronous_unwind_tables = 1; if (flag_pcc_struct_return == 2) flag_pcc_struct_return = 0; } else { if (flag_omit_frame_pointer == 2) flag_omit_frame_pointer = 0; if (flag_asynchronous_unwind_tables == 2) flag_asynchronous_unwind_tables = 0; if (flag_pcc_struct_return == 2) flag_pcc_struct_return = DEFAULT_PCC_STRUCT_RETURN; } #ifdef SUBTARGET_OVERRIDE_OPTIONS SUBTARGET_OVERRIDE_OPTIONS; #endif if (!ix86_tune_string && ix86_arch_string) ix86_tune_string = ix86_arch_string; if (!ix86_tune_string) ix86_tune_string = cpu_names [TARGET_CPU_DEFAULT]; if (!ix86_arch_string) ix86_arch_string = TARGET_64BIT ? "x86-64" : "i386"; if (ix86_cmodel_string != 0) { if (!strcmp (ix86_cmodel_string, "small")) ix86_cmodel = flag_pic ? CM_SMALL_PIC : CM_SMALL; else if (flag_pic) sorry ("code model %s not supported in PIC mode", ix86_cmodel_string); else if (!strcmp (ix86_cmodel_string, "32")) ix86_cmodel = CM_32; else if (!strcmp (ix86_cmodel_string, "kernel") && !flag_pic) ix86_cmodel = CM_KERNEL; else if (!strcmp (ix86_cmodel_string, "medium") && !flag_pic) ix86_cmodel = CM_MEDIUM; else if (!strcmp (ix86_cmodel_string, "large") && !flag_pic) ix86_cmodel = CM_LARGE; else error ("bad value (%s) for -mcmodel= switch", ix86_cmodel_string); } else { ix86_cmodel = CM_32; if (TARGET_64BIT) ix86_cmodel = flag_pic ? CM_SMALL_PIC : CM_SMALL; } if (ix86_asm_string != 0) { if (!strcmp (ix86_asm_string, "intel")) ix86_asm_dialect = ASM_INTEL; else if (!strcmp (ix86_asm_string, "att")) ix86_asm_dialect = ASM_ATT; else error ("bad value (%s) for -masm= switch", ix86_asm_string); } if ((TARGET_64BIT == 0) != (ix86_cmodel == CM_32)) error ("code model `%s' not supported in the %s bit mode", ix86_cmodel_string, TARGET_64BIT ? "64" : "32"); if (ix86_cmodel == CM_LARGE) sorry ("code model `large' not supported yet"); if ((TARGET_64BIT != 0) != ((target_flags & MASK_64BIT) != 0)) sorry ("%i-bit mode not compiled in", (target_flags & MASK_64BIT) ? 64 : 32); for (i = 0; i < pta_size; i++) if (! strcmp (ix86_arch_string, processor_alias_table[i].name)) { ix86_arch = processor_alias_table[i].processor; /* Default cpu tuning to the architecture. */ ix86_tune = ix86_arch; if (processor_alias_table[i].flags & PTA_MMX && !(target_flags_explicit & MASK_MMX)) target_flags |= MASK_MMX; if (processor_alias_table[i].flags & PTA_3DNOW && !(target_flags_explicit & MASK_3DNOW)) target_flags |= MASK_3DNOW; if (processor_alias_table[i].flags & PTA_3DNOW_A && !(target_flags_explicit & MASK_3DNOW_A)) target_flags |= MASK_3DNOW_A; if (processor_alias_table[i].flags & PTA_SSE && !(target_flags_explicit & MASK_SSE)) target_flags |= MASK_SSE; if (processor_alias_table[i].flags & PTA_SSE2 && !(target_flags_explicit & MASK_SSE2)) target_flags |= MASK_SSE2; if (processor_alias_table[i].flags & PTA_SSE3 && !(target_flags_explicit & MASK_SSE3)) target_flags |= MASK_SSE3; if (processor_alias_table[i].flags & PTA_PREFETCH_SSE) x86_prefetch_sse = true; if (TARGET_64BIT && !(processor_alias_table[i].flags & PTA_64BIT)) error ("CPU you selected does not support x86-64 instruction set"); break; } if (i == pta_size) error ("bad value (%s) for -march= switch", ix86_arch_string); for (i = 0; i < pta_size; i++) if (! strcmp (ix86_tune_string, processor_alias_table[i].name)) { ix86_tune = processor_alias_table[i].processor; if (TARGET_64BIT && !(processor_alias_table[i].flags & PTA_64BIT)) error ("CPU you selected does not support x86-64 instruction set"); /* Intel CPUs have always interpreted SSE prefetch instructions as NOPs; so, we can enable SSE prefetch instructions even when -mtune (rather than -march) points us to a processor that has them. However, the VIA C3 gives a SIGILL, so we only do that for i686 and higher processors. */ if (TARGET_CMOVE && (processor_alias_table[i].flags & PTA_PREFETCH_SSE)) x86_prefetch_sse = true; break; } if (i == pta_size) error ("bad value (%s) for -mtune= switch", ix86_tune_string); if (optimize_size) ix86_cost = &size_cost; else ix86_cost = processor_target_table[ix86_tune].cost; target_flags |= processor_target_table[ix86_tune].target_enable; target_flags &= ~processor_target_table[ix86_tune].target_disable; /* Arrange to set up i386_stack_locals for all functions. */ init_machine_status = ix86_init_machine_status; /* Validate -mregparm= value. */ if (ix86_regparm_string) { i = atoi (ix86_regparm_string); if (i < 0 || i > REGPARM_MAX) error ("-mregparm=%d is not between 0 and %d", i, REGPARM_MAX); else ix86_regparm = i; } else if (TARGET_64BIT) ix86_regparm = REGPARM_MAX; /* If the user has provided any of the -malign-* options, warn and use that value only if -falign-* is not set. Remove this code in GCC 3.2 or later. */ if (ix86_align_loops_string) { warning ("-malign-loops is obsolete, use -falign-loops"); if (align_loops == 0) { i = atoi (ix86_align_loops_string); if (i < 0 || i > MAX_CODE_ALIGN) error ("-malign-loops=%d is not between 0 and %d", i, MAX_CODE_ALIGN); else align_loops = 1 << i; } } if (ix86_align_jumps_string) { warning ("-malign-jumps is obsolete, use -falign-jumps"); if (align_jumps == 0) { i = atoi (ix86_align_jumps_string); if (i < 0 || i > MAX_CODE_ALIGN) error ("-malign-loops=%d is not between 0 and %d", i, MAX_CODE_ALIGN); else align_jumps = 1 << i; } } if (ix86_align_funcs_string) { warning ("-malign-functions is obsolete, use -falign-functions"); if (align_functions == 0) { i = atoi (ix86_align_funcs_string); if (i < 0 || i > MAX_CODE_ALIGN) error ("-malign-loops=%d is not between 0 and %d", i, MAX_CODE_ALIGN); else align_functions = 1 << i; } } /* Default align_* from the processor table. */ if (align_loops == 0) { align_loops = processor_target_table[ix86_tune].align_loop; align_loops_max_skip = processor_target_table[ix86_tune].align_loop_max_skip; } if (align_jumps == 0) { align_jumps = processor_target_table[ix86_tune].align_jump; align_jumps_max_skip = processor_target_table[ix86_tune].align_jump_max_skip; } if (align_functions == 0) { align_functions = processor_target_table[ix86_tune].align_func; } /* Validate -mpreferred-stack-boundary= value, or provide default. The default of 128 bits is for Pentium III's SSE __m128, but we don't want additional code to keep the stack aligned when optimizing for code size. */ ix86_preferred_stack_boundary = (optimize_size ? TARGET_64BIT ? 128 : 32 : 128); if (ix86_preferred_stack_boundary_string) { i = atoi (ix86_preferred_stack_boundary_string); if (i < (TARGET_64BIT ? 4 : 2) || i > 12) error ("-mpreferred-stack-boundary=%d is not between %d and 12", i, TARGET_64BIT ? 4 : 2); else ix86_preferred_stack_boundary = (1 << i) * BITS_PER_UNIT; } /* Validate -mbranch-cost= value, or provide default. */ ix86_branch_cost = processor_target_table[ix86_tune].cost->branch_cost; if (ix86_branch_cost_string) { i = atoi (ix86_branch_cost_string); if (i < 0 || i > 5) error ("-mbranch-cost=%d is not between 0 and 5", i); else ix86_branch_cost = i; } if (ix86_tls_dialect_string) { if (strcmp (ix86_tls_dialect_string, "gnu") == 0) ix86_tls_dialect = TLS_DIALECT_GNU; else if (strcmp (ix86_tls_dialect_string, "sun") == 0) ix86_tls_dialect = TLS_DIALECT_SUN; else error ("bad value (%s) for -mtls-dialect= switch", ix86_tls_dialect_string); } /* Keep nonleaf frame pointers. */ if (TARGET_OMIT_LEAF_FRAME_POINTER) flag_omit_frame_pointer = 1; /* If we're doing fast math, we don't care about comparison order wrt NaNs. This lets us use a shorter comparison sequence. */ if (flag_unsafe_math_optimizations) target_flags &= ~MASK_IEEE_FP; /* If the architecture always has an FPU, turn off NO_FANCY_MATH_387, since the insns won't need emulation. */ if (x86_arch_always_fancy_math_387 & (1 << ix86_arch)) target_flags &= ~MASK_NO_FANCY_MATH_387; /* Turn on SSE2 builtins for -msse3. */ if (TARGET_SSE3) target_flags |= MASK_SSE2; /* Turn on SSE builtins for -msse2. */ if (TARGET_SSE2) target_flags |= MASK_SSE; if (TARGET_64BIT) { if (TARGET_ALIGN_DOUBLE) error ("-malign-double makes no sense in the 64bit mode"); if (TARGET_RTD) error ("-mrtd calling convention not supported in the 64bit mode"); /* Enable by default the SSE and MMX builtins. */ target_flags |= (MASK_SSE2 | MASK_SSE | MASK_MMX | MASK_128BIT_LONG_DOUBLE); ix86_fpmath = FPMATH_SSE; } else { ix86_fpmath = FPMATH_387; /* i386 ABI does not specify red zone. It still makes sense to use it when programmer takes care to stack from being destroyed. */ if (!(target_flags_explicit & MASK_NO_RED_ZONE)) target_flags |= MASK_NO_RED_ZONE; } if (ix86_fpmath_string != 0) { if (! strcmp (ix86_fpmath_string, "387")) ix86_fpmath = FPMATH_387; else if (! strcmp (ix86_fpmath_string, "sse")) { if (!TARGET_SSE) { warning ("SSE instruction set disabled, using 387 arithmetics"); ix86_fpmath = FPMATH_387; } else ix86_fpmath = FPMATH_SSE; } else if (! strcmp (ix86_fpmath_string, "387,sse") || ! strcmp (ix86_fpmath_string, "sse,387")) { if (!TARGET_SSE) { warning ("SSE instruction set disabled, using 387 arithmetics"); ix86_fpmath = FPMATH_387; } else if (!TARGET_80387) { warning ("387 instruction set disabled, using SSE arithmetics"); ix86_fpmath = FPMATH_SSE; } else ix86_fpmath = FPMATH_SSE | FPMATH_387; } else error ("bad value (%s) for -mfpmath= switch", ix86_fpmath_string); } /* It makes no sense to ask for just SSE builtins, so MMX is also turned on by -msse. */ if (TARGET_SSE) { target_flags |= MASK_MMX; x86_prefetch_sse = true; } /* If it has 3DNow! it also has MMX so MMX is also turned on by -m3dnow */ if (TARGET_3DNOW) { target_flags |= MASK_MMX; /* If we are targeting the Athlon architecture, enable the 3Dnow/MMX extensions it adds. */ if (x86_3dnow_a & (1 << ix86_arch)) target_flags |= MASK_3DNOW_A; } if ((x86_accumulate_outgoing_args & TUNEMASK) && !(target_flags_explicit & MASK_ACCUMULATE_OUTGOING_ARGS) && !optimize_size) target_flags |= MASK_ACCUMULATE_OUTGOING_ARGS; /* Figure out what ASM_GENERATE_INTERNAL_LABEL builds as a prefix. */ { char *p; ASM_GENERATE_INTERNAL_LABEL (internal_label_prefix, "LX", 0); p = strchr (internal_label_prefix, 'X'); internal_label_prefix_len = p - internal_label_prefix; *p = '\0'; } } void optimization_options (int level, int size ATTRIBUTE_UNUSED) { /* For -O2 and beyond, turn off -fschedule-insns by default. It tends to make the problem with not enough registers even worse. */ #ifdef INSN_SCHEDULING if (level > 1) flag_schedule_insns = 0; #endif /* The default values of these switches depend on the TARGET_64BIT that is not known at this moment. Mark these values with 2 and let user the to override these. In case there is no command line option specifying them, we will set the defaults in override_options. */ if (optimize >= 1) flag_omit_frame_pointer = 2; flag_pcc_struct_return = 2; flag_asynchronous_unwind_tables = 2; } /* Table of valid machine attributes. */ const struct attribute_spec ix86_attribute_table[] = { /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ /* Stdcall attribute says callee is responsible for popping arguments if they are not variable. */ { "stdcall", 0, 0, false, true, true, ix86_handle_cdecl_attribute }, /* Fastcall attribute says callee is responsible for popping arguments if they are not variable. */ { "fastcall", 0, 0, false, true, true, ix86_handle_cdecl_attribute }, /* Cdecl attribute says the callee is a normal C declaration */ { "cdecl", 0, 0, false, true, true, ix86_handle_cdecl_attribute }, /* Regparm attribute specifies how many integer arguments are to be passed in registers. */ { "regparm", 1, 1, false, true, true, ix86_handle_regparm_attribute }, #ifdef TARGET_DLLIMPORT_DECL_ATTRIBUTES { "dllimport", 0, 0, false, false, false, ix86_handle_dll_attribute }, { "dllexport", 0, 0, false, false, false, ix86_handle_dll_attribute }, { "shared", 0, 0, true, false, false, ix86_handle_shared_attribute }, #endif { "ms_struct", 0, 0, false, false, false, ix86_handle_struct_attribute }, { "gcc_struct", 0, 0, false, false, false, ix86_handle_struct_attribute }, { NULL, 0, 0, false, false, false, NULL } }; /* Decide whether we can make a sibling call to a function. DECL is the declaration of the function being targeted by the call and EXP is the CALL_EXPR representing the call. */ static bool ix86_function_ok_for_sibcall (tree decl, tree exp) { /* If we are generating position-independent code, we cannot sibcall optimize any indirect call, or a direct call to a global function, as the PLT requires %ebx be live. */ if (!TARGET_64BIT && flag_pic && (!decl || TREE_PUBLIC (decl))) return false; /* If we are returning floats on the 80387 register stack, we cannot make a sibcall from a function that doesn't return a float to a function that does or, conversely, from a function that does return a float to a function that doesn't; the necessary stack adjustment would not be executed. */ if (STACK_REG_P (ix86_function_value (TREE_TYPE (exp))) != STACK_REG_P (ix86_function_value (TREE_TYPE (DECL_RESULT (cfun->decl))))) return false; /* If this call is indirect, we'll need to be able to use a call-clobbered register for the address of the target function. Make sure that all such registers are not used for passing parameters. */ if (!decl && !TARGET_64BIT) { tree type; /* We're looking at the CALL_EXPR, we need the type of the function. */ type = TREE_OPERAND (exp, 0); /* pointer expression */ type = TREE_TYPE (type); /* pointer type */ type = TREE_TYPE (type); /* function type */ if (ix86_function_regparm (type, NULL) >= 3) { /* ??? Need to count the actual number of registers to be used, not the possible number of registers. Fix later. */ return false; } } /* Otherwise okay. That also includes certain types of indirect calls. */ return true; } /* Handle a "cdecl", "stdcall", or "fastcall" attribute; arguments as in struct attribute_spec.handler. */ static tree ix86_handle_cdecl_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { if (TREE_CODE (*node) != FUNCTION_TYPE && TREE_CODE (*node) != METHOD_TYPE && TREE_CODE (*node) != FIELD_DECL && TREE_CODE (*node) != TYPE_DECL) { warning ("`%s' attribute only applies to functions", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } else { if (is_attribute_p ("fastcall", name)) { if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (*node))) { error ("fastcall and stdcall attributes are not compatible"); } else if (lookup_attribute ("regparm", TYPE_ATTRIBUTES (*node))) { error ("fastcall and regparm attributes are not compatible"); } } else if (is_attribute_p ("stdcall", name)) { if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (*node))) { error ("fastcall and stdcall attributes are not compatible"); } } } if (TARGET_64BIT) { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "regparm" attribute; arguments as in struct attribute_spec.handler. */ static tree ix86_handle_regparm_attribute (tree *node, tree name, tree args, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { if (TREE_CODE (*node) != FUNCTION_TYPE && TREE_CODE (*node) != METHOD_TYPE && TREE_CODE (*node) != FIELD_DECL && TREE_CODE (*node) != TYPE_DECL) { warning ("`%s' attribute only applies to functions", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } else { tree cst; cst = TREE_VALUE (args); if (TREE_CODE (cst) != INTEGER_CST) { warning ("`%s' attribute requires an integer constant argument", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } else if (compare_tree_int (cst, REGPARM_MAX) > 0) { warning ("argument to `%s' attribute larger than %d", IDENTIFIER_POINTER (name), REGPARM_MAX); *no_add_attrs = true; } if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (*node))) { error ("fastcall and regparm attributes are not compatible"); } } return NULL_TREE; } /* 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). */ static int ix86_comp_type_attributes (tree type1, tree type2) { /* Check for mismatch of non-default calling convention. */ const char *const rtdstr = TARGET_RTD ? "cdecl" : "stdcall"; if (TREE_CODE (type1) != FUNCTION_TYPE) return 1; /* Check for mismatched fastcall types */ if (!lookup_attribute ("fastcall", TYPE_ATTRIBUTES (type1)) != !lookup_attribute ("fastcall", TYPE_ATTRIBUTES (type2))) return 0; /* Check for mismatched return types (cdecl vs stdcall). */ if (!lookup_attribute (rtdstr, TYPE_ATTRIBUTES (type1)) != !lookup_attribute (rtdstr, TYPE_ATTRIBUTES (type2))) return 0; if (ix86_function_regparm (type1, NULL) != ix86_function_regparm (type2, NULL)) return 0; return 1; } /* Return the regparm value for a fuctio with the indicated TYPE and DECL. DECL may be NULL when calling function indirectly or considering a libcall. */ static int ix86_function_regparm (tree type, tree decl) { tree attr; int regparm = ix86_regparm; bool user_convention = false; if (!TARGET_64BIT) { attr = lookup_attribute ("regparm", TYPE_ATTRIBUTES (type)); if (attr) { regparm = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (attr))); user_convention = true; } if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (type))) { regparm = 2; user_convention = true; } /* Use register calling convention for local functions when possible. */ if (!TARGET_64BIT && !user_convention && decl && flag_unit_at_a_time && !profile_flag) { struct cgraph_local_info *i = cgraph_local_info (decl); if (i && i->local) { /* We can't use regparm(3) for nested functions as these use static chain pointer in third argument. */ if (DECL_CONTEXT (decl) && !DECL_NO_STATIC_CHAIN (decl)) regparm = 2; else regparm = 3; } } } return regparm; } /* Return true if EAX is live at the start of the function. Used by ix86_expand_prologue to determine if we need special help before calling allocate_stack_worker. */ static bool ix86_eax_live_at_start_p (void) { /* Cheat. Don't bother working forward from ix86_function_regparm to the function type to whether an actual argument is located in eax. Instead just look at cfg info, which is still close enough to correct at this point. This gives false positives for broken functions that might use uninitialized data that happens to be allocated in eax, but who cares? */ return REGNO_REG_SET_P (ENTRY_BLOCK_PTR->global_live_at_end, 0); } /* 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 ix86_return_pops_args (tree fundecl, tree funtype, int size) { int rtd = TARGET_RTD && (!fundecl || TREE_CODE (fundecl) != IDENTIFIER_NODE); /* Cdecl functions override -mrtd, and never pop the stack. */ if (! lookup_attribute ("cdecl", TYPE_ATTRIBUTES (funtype))) { /* Stdcall and fastcall functions will pop the stack if not variable args. */ if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (funtype)) || lookup_attribute ("fastcall", TYPE_ATTRIBUTES (funtype))) rtd = 1; if (rtd && (TYPE_ARG_TYPES (funtype) == NULL_TREE || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (funtype))) == void_type_node))) return size; } /* Lose any fake structure return argument if it is passed on the stack. */ if (aggregate_value_p (TREE_TYPE (funtype), fundecl) && !TARGET_64BIT) { int nregs = ix86_function_regparm (funtype, fundecl); if (!nregs) return GET_MODE_SIZE (Pmode); } return 0; } /* Argument support functions. */ /* Return true when register may be used to pass function parameters. */ bool ix86_function_arg_regno_p (int regno) { int i; if (!TARGET_64BIT) return (regno < REGPARM_MAX || (TARGET_SSE && SSE_REGNO_P (regno) && !fixed_regs[regno])); if (SSE_REGNO_P (regno) && TARGET_SSE) return true; /* RAX is used as hidden argument to va_arg functions. */ if (!regno) return true; for (i = 0; i < REGPARM_MAX; i++) if (regno == x86_64_int_parameter_registers[i]) return true; return false; } /* 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 (CUMULATIVE_ARGS *cum, /* Argument info to initialize */ tree fntype, /* tree ptr for function decl */ rtx libname, /* SYMBOL_REF of library name or 0 */ tree fndecl) { static CUMULATIVE_ARGS zero_cum; tree param, next_param; if (TARGET_DEBUG_ARG) { fprintf (stderr, "\ninit_cumulative_args ("); if (fntype) fprintf (stderr, "fntype code = %s, ret code = %s", tree_code_name[(int) TREE_CODE (fntype)], tree_code_name[(int) TREE_CODE (TREE_TYPE (fntype))]); 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. */ if (fntype) cum->nregs = ix86_function_regparm (fntype, fndecl); else cum->nregs = ix86_regparm; cum->sse_nregs = SSE_REGPARM_MAX; cum->mmx_nregs = MMX_REGPARM_MAX; cum->warn_sse = true; cum->warn_mmx = true; cum->maybe_vaarg = false; /* Use ecx and edx registers if function has fastcall attribute */ if (fntype && !TARGET_64BIT) { if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (fntype))) { cum->nregs = 2; cum->fastcall = 1; } } /* 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 || !TARGET_MMX || !TARGET_SSE) { for (param = (fntype) ? TYPE_ARG_TYPES (fntype) : 0; param != 0; param = next_param) { next_param = TREE_CHAIN (param); if (next_param == 0 && TREE_VALUE (param) != void_type_node) { if (!TARGET_64BIT) { cum->nregs = 0; cum->sse_nregs = 0; cum->mmx_nregs = 0; cum->warn_sse = 0; cum->warn_mmx = 0; cum->fastcall = 0; } cum->maybe_vaarg = true; } } } if ((!fntype && !libname) || (fntype && !TYPE_ARG_TYPES (fntype))) cum->maybe_vaarg = 1; if (TARGET_DEBUG_ARG) fprintf (stderr, ", nregs=%d )\n", cum->nregs); return; } /* x86-64 register passing implementation. See x86-64 ABI for details. Goal of this code is to classify each 8bytes of incoming argument by the register class and assign registers accordingly. */ /* Return the union class of CLASS1 and CLASS2. See the x86-64 PS ABI for details. */ static enum x86_64_reg_class merge_classes (enum x86_64_reg_class class1, enum x86_64_reg_class class2) { /* Rule #1: If both classes are equal, this is the resulting class. */ if (class1 == class2) return class1; /* Rule #2: If one of the classes is NO_CLASS, the resulting class is the other class. */ if (class1 == X86_64_NO_CLASS) return class2; if (class2 == X86_64_NO_CLASS) return class1; /* Rule #3: If one of the classes is MEMORY, the result is MEMORY. */ if (class1 == X86_64_MEMORY_CLASS || class2 == X86_64_MEMORY_CLASS) return X86_64_MEMORY_CLASS; /* Rule #4: If one of the classes is INTEGER, the result is INTEGER. */ if ((class1 == X86_64_INTEGERSI_CLASS && class2 == X86_64_SSESF_CLASS) || (class2 == X86_64_INTEGERSI_CLASS && class1 == X86_64_SSESF_CLASS)) return X86_64_INTEGERSI_CLASS; if (class1 == X86_64_INTEGER_CLASS || class1 == X86_64_INTEGERSI_CLASS || class2 == X86_64_INTEGER_CLASS || class2 == X86_64_INTEGERSI_CLASS) return X86_64_INTEGER_CLASS; /* Rule #5: If one of the classes is X87 or X87UP class, MEMORY is used. */ if (class1 == X86_64_X87_CLASS || class1 == X86_64_X87UP_CLASS || class2 == X86_64_X87_CLASS || class2 == X86_64_X87UP_CLASS) return X86_64_MEMORY_CLASS; /* Rule #6: Otherwise class SSE is used. */ return X86_64_SSE_CLASS; } /* Classify the argument of type TYPE and mode MODE. CLASSES will be filled by the register class used to pass each word of the operand. The number of words is returned. In case the parameter should be passed in memory, 0 is returned. As a special case for zero sized containers, classes[0] will be NO_CLASS and 1 is returned. BIT_OFFSET is used internally for handling records and specifies offset of the offset in bits modulo 256 to avoid overflow cases. See the x86-64 PS ABI for details. */ static int classify_argument (enum machine_mode mode, tree type, enum x86_64_reg_class classes[MAX_CLASSES], int bit_offset) { HOST_WIDE_INT bytes = (mode == BLKmode) ? int_size_in_bytes (type) : (int) GET_MODE_SIZE (mode); int words = (bytes + (bit_offset % 64) / 8 + UNITS_PER_WORD - 1) / UNITS_PER_WORD; /* Variable sized entities are always passed/returned in memory. */ if (bytes < 0) return 0; if (mode != VOIDmode && MUST_PASS_IN_STACK (mode, type)) return 0; if (type && AGGREGATE_TYPE_P (type)) { int i; tree field; enum x86_64_reg_class subclasses[MAX_CLASSES]; /* On x86-64 we pass structures larger than 16 bytes on the stack. */ if (bytes > 16) return 0; for (i = 0; i < words; i++) classes[i] = X86_64_NO_CLASS; /* Zero sized arrays or structures are NO_CLASS. We return 0 to signalize memory class, so handle it as special case. */ if (!words) { classes[0] = X86_64_NO_CLASS; return 1; } /* Classify each field of record and merge classes. */ if (TREE_CODE (type) == RECORD_TYPE) { /* For classes first merge in the field of the subclasses. */ if (TYPE_BINFO (type) != NULL && TYPE_BINFO_BASETYPES (type) != NULL) { tree bases = TYPE_BINFO_BASETYPES (type); int n_bases = TREE_VEC_LENGTH (bases); int i; for (i = 0; i < n_bases; ++i) { tree binfo = TREE_VEC_ELT (bases, i); int num; int offset = tree_low_cst (BINFO_OFFSET (binfo), 0) * 8; tree type = BINFO_TYPE (binfo); num = classify_argument (TYPE_MODE (type), type, subclasses, (offset + bit_offset) % 256); if (!num) return 0; for (i = 0; i < num; i++) { int pos = (offset + (bit_offset % 64)) / 8 / 8; classes[i + pos] = merge_classes (subclasses[i], classes[i + pos]); } } } /* And now merge the fields of structure. */ for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) { if (TREE_CODE (field) == FIELD_DECL) { int num; /* Bitfields are always classified as integer. Handle them early, since later code would consider them to be misaligned integers. */ if (DECL_BIT_FIELD (field)) { for (i = int_bit_position (field) / 8 / 8; i < (int_bit_position (field) + tree_low_cst (DECL_SIZE (field), 0) + 63) / 8 / 8; i++) classes[i] = merge_classes (X86_64_INTEGER_CLASS, classes[i]); } else { num = classify_argument (TYPE_MODE (TREE_TYPE (field)), TREE_TYPE (field), subclasses, (int_bit_position (field) + bit_offset) % 256); if (!num) return 0; for (i = 0; i < num; i++) { int pos = (int_bit_position (field) + (bit_offset % 64)) / 8 / 8; classes[i + pos] = merge_classes (subclasses[i], classes[i + pos]); } } } } } /* Arrays are handled as small records. */ else if (TREE_CODE (type) == ARRAY_TYPE) { int num; num = classify_argument (TYPE_MODE (TREE_TYPE (type)), TREE_TYPE (type), subclasses, bit_offset); if (!num) return 0; /* The partial classes are now full classes. */ if (subclasses[0] == X86_64_SSESF_CLASS && bytes != 4) subclasses[0] = X86_64_SSE_CLASS; if (subclasses[0] == X86_64_INTEGERSI_CLASS && bytes != 4) subclasses[0] = X86_64_INTEGER_CLASS; for (i = 0; i < words; i++) classes[i] = subclasses[i % num]; } /* Unions are similar to RECORD_TYPE but offset is always 0. */ else if (TREE_CODE (type) == UNION_TYPE || TREE_CODE (type) == QUAL_UNION_TYPE) { /* For classes first merge in the field of the subclasses. */ if (TYPE_BINFO (type) != NULL && TYPE_BINFO_BASETYPES (type) != NULL) { tree bases = TYPE_BINFO_BASETYPES (type); int n_bases = TREE_VEC_LENGTH (bases); int i; for (i = 0; i < n_bases; ++i) { tree binfo = TREE_VEC_ELT (bases, i); int num; int offset = tree_low_cst (BINFO_OFFSET (binfo), 0) * 8; tree type = BINFO_TYPE (binfo); num = classify_argument (TYPE_MODE (type), type, subclasses, (offset + (bit_offset % 64)) % 256); if (!num) return 0; for (i = 0; i < num; i++) { int pos = (offset + (bit_offset % 64)) / 8 / 8; classes[i + pos] = merge_classes (subclasses[i], classes[i + pos]); } } } for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) { if (TREE_CODE (field) == FIELD_DECL) { int num; num = classify_argument (TYPE_MODE (TREE_TYPE (field)), TREE_TYPE (field), subclasses, bit_offset); if (!num) return 0; for (i = 0; i < num; i++) classes[i] = merge_classes (subclasses[i], classes[i]); } } } else if (TREE_CODE (type) == SET_TYPE) { if (bytes <= 4) { classes[0] = X86_64_INTEGERSI_CLASS; return 1; } else if (bytes <= 8) { classes[0] = X86_64_INTEGER_CLASS; return 1; } else if (bytes <= 12) { classes[0] = X86_64_INTEGER_CLASS; classes[1] = X86_64_INTEGERSI_CLASS; return 2; } else { classes[0] = X86_64_INTEGER_CLASS; classes[1] = X86_64_INTEGER_CLASS; return 2; } } else abort (); /* Final merger cleanup. */ for (i = 0; i < words; i++) { /* If one class is MEMORY, everything should be passed in memory. */ if (classes[i] == X86_64_MEMORY_CLASS) return 0; /* The X86_64_SSEUP_CLASS should be always preceded by X86_64_SSE_CLASS. */ if (classes[i] == X86_64_SSEUP_CLASS && (i == 0 || classes[i - 1] != X86_64_SSE_CLASS)) classes[i] = X86_64_SSE_CLASS; /* X86_64_X87UP_CLASS should be preceded by X86_64_X87_CLASS. */ if (classes[i] == X86_64_X87UP_CLASS && (i == 0 || classes[i - 1] != X86_64_X87_CLASS)) classes[i] = X86_64_SSE_CLASS; } return words; } /* Compute alignment needed. We align all types to natural boundaries with exception of XFmode that is aligned to 64bits. */ if (mode != VOIDmode && mode != BLKmode) { int mode_alignment = GET_MODE_BITSIZE (mode); if (mode == XFmode) mode_alignment = 128; else if (mode == XCmode) mode_alignment = 256; if (COMPLEX_MODE_P (mode)) mode_alignment /= 2; /* Misaligned fields are always returned in memory. */ if (bit_offset % mode_alignment) return 0; } /* Classification of atomic types. */ switch (mode) { case DImode: case SImode: case HImode: case QImode: case CSImode: case CHImode: case CQImode: if (bit_offset + GET_MODE_BITSIZE (mode) <= 32) classes[0] = X86_64_INTEGERSI_CLASS; else classes[0] = X86_64_INTEGER_CLASS; return 1; case CDImode: case TImode: classes[0] = classes[1] = X86_64_INTEGER_CLASS; return 2; case CTImode: classes[0] = classes[1] = X86_64_INTEGER_CLASS; classes[2] = classes[3] = X86_64_INTEGER_CLASS; return 4; case SFmode: if (!(bit_offset % 64)) classes[0] = X86_64_SSESF_CLASS; else classes[0] = X86_64_SSE_CLASS; return 1; case DFmode: classes[0] = X86_64_SSEDF_CLASS; return 1; case XFmode: classes[0] = X86_64_X87_CLASS; classes[1] = X86_64_X87UP_CLASS; return 2; case TFmode: case TCmode: return 0; case XCmode: classes[0] = X86_64_X87_CLASS; classes[1] = X86_64_X87UP_CLASS; classes[2] = X86_64_X87_CLASS; classes[3] = X86_64_X87UP_CLASS; return 4; case DCmode: classes[0] = X86_64_SSEDF_CLASS; classes[1] = X86_64_SSEDF_CLASS; return 2; case SCmode: classes[0] = X86_64_SSE_CLASS; return 1; case V4SFmode: case V4SImode: case V16QImode: case V8HImode: case V2DFmode: case V2DImode: classes[0] = X86_64_SSE_CLASS; classes[1] = X86_64_SSEUP_CLASS; return 2; case V2SFmode: case V2SImode: case V4HImode: case V8QImode: return 0; case BLKmode: case VOIDmode: return 0; default: abort (); } } /* Examine the argument and return set number of register required in each class. Return 0 iff parameter should be passed in memory. */ static int examine_argument (enum machine_mode mode, tree type, int in_return, int *int_nregs, int *sse_nregs) { enum x86_64_reg_class class[MAX_CLASSES]; int n = classify_argument (mode, type, class, 0); *int_nregs = 0; *sse_nregs = 0; if (!n) return 0; for (n--; n >= 0; n--) switch (class[n]) { case X86_64_INTEGER_CLASS: case X86_64_INTEGERSI_CLASS: (*int_nregs)++; break; case X86_64_SSE_CLASS: case X86_64_SSESF_CLASS: case X86_64_SSEDF_CLASS: (*sse_nregs)++; break; case X86_64_NO_CLASS: case X86_64_SSEUP_CLASS: break; case X86_64_X87_CLASS: case X86_64_X87UP_CLASS: if (!in_return) return 0; break; case X86_64_MEMORY_CLASS: abort (); } return 1; } /* Construct container for the argument used by GCC interface. See FUNCTION_ARG for the detailed description. */ static rtx construct_container (enum machine_mode mode, tree type, int in_return, int nintregs, int nsseregs, const int * intreg, int sse_regno) { enum machine_mode tmpmode; int bytes = (mode == BLKmode) ? int_size_in_bytes (type) : (int) GET_MODE_SIZE (mode); enum x86_64_reg_class class[MAX_CLASSES]; int n; int i; int nexps = 0; int needed_sseregs, needed_intregs; rtx exp[MAX_CLASSES]; rtx ret; n = classify_argument (mode, type, class, 0); if (TARGET_DEBUG_ARG) { if (!n) fprintf (stderr, "Memory class\n"); else { fprintf (stderr, "Classes:"); for (i = 0; i < n; i++) { fprintf (stderr, " %s", x86_64_reg_class_name[class[i]]); } fprintf (stderr, "\n"); } } if (!n) return NULL; if (!examine_argument (mode, type, in_return, &needed_intregs, &needed_sseregs)) return NULL; if (needed_intregs > nintregs || needed_sseregs > nsseregs) return NULL; /* First construct simple cases. Avoid SCmode, since we want to use single register to pass this type. */ if (n == 1 && mode != SCmode) switch (class[0]) { case X86_64_INTEGER_CLASS: case X86_64_INTEGERSI_CLASS: return gen_rtx_REG (mode, intreg[0]); case X86_64_SSE_CLASS: case X86_64_SSESF_CLASS: case X86_64_SSEDF_CLASS: return gen_rtx_REG (mode, SSE_REGNO (sse_regno)); case X86_64_X87_CLASS: return gen_rtx_REG (mode, FIRST_STACK_REG); case X86_64_NO_CLASS: /* Zero sized array, struct or class. */ return NULL; default: abort (); } if (n == 2 && class[0] == X86_64_SSE_CLASS && class[1] == X86_64_SSEUP_CLASS && mode != BLKmode) return gen_rtx_REG (mode, SSE_REGNO (sse_regno)); if (n == 2 && class[0] == X86_64_X87_CLASS && class[1] == X86_64_X87UP_CLASS) return gen_rtx_REG (XFmode, FIRST_STACK_REG); if (n == 2 && class[0] == X86_64_INTEGER_CLASS && class[1] == X86_64_INTEGER_CLASS && (mode == CDImode || mode == TImode || mode == TFmode) && intreg[0] + 1 == intreg[1]) return gen_rtx_REG (mode, intreg[0]); if (n == 4 && class[0] == X86_64_X87_CLASS && class[1] == X86_64_X87UP_CLASS && class[2] == X86_64_X87_CLASS && class[3] == X86_64_X87UP_CLASS && mode != BLKmode) return gen_rtx_REG (XCmode, FIRST_STACK_REG); /* Otherwise figure out the entries of the PARALLEL. */ for (i = 0; i < n; i++) { switch (class[i]) { case X86_64_NO_CLASS: break; case X86_64_INTEGER_CLASS: case X86_64_INTEGERSI_CLASS: /* Merge TImodes on aligned occasions here too. */ if (i * 8 + 8 > bytes) tmpmode = mode_for_size ((bytes - i * 8) * BITS_PER_UNIT, MODE_INT, 0); else if (class[i] == X86_64_INTEGERSI_CLASS) tmpmode = SImode; else tmpmode = DImode; /* We've requested 24 bytes we don't have mode for. Use DImode. */ if (tmpmode == BLKmode) tmpmode = DImode; exp [nexps++] = gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (tmpmode, *intreg), GEN_INT (i*8)); intreg++; break; case X86_64_SSESF_CLASS: exp [nexps++] = gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (SFmode, SSE_REGNO (sse_regno)), GEN_INT (i*8)); sse_regno++; break; case X86_64_SSEDF_CLASS: exp [nexps++] = gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (DFmode, SSE_REGNO (sse_regno)), GEN_INT (i*8)); sse_regno++; break; case X86_64_SSE_CLASS: if (i < n - 1 && class[i + 1] == X86_64_SSEUP_CLASS) tmpmode = TImode; else tmpmode = DImode; exp [nexps++] = gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (tmpmode, SSE_REGNO (sse_regno)), GEN_INT (i*8)); if (tmpmode == TImode) i++; sse_regno++; break; default: abort (); } } ret = gen_rtx_PARALLEL (mode, rtvec_alloc (nexps)); for (i = 0; i < nexps; i++) XVECEXP (ret, 0, i) = exp [i]; return ret; } /* 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 (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) : (int) GET_MODE_SIZE (mode); int words = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD; if (TARGET_DEBUG_ARG) fprintf (stderr, "function_adv (sz=%d, wds=%2d, nregs=%d, ssenregs=%d, mode=%s, named=%d)\n\n", words, cum->words, cum->nregs, cum->sse_nregs, GET_MODE_NAME (mode), named); if (TARGET_64BIT) { int int_nregs, sse_nregs; if (!examine_argument (mode, type, 0, &int_nregs, &sse_nregs)) cum->words += words; else if (sse_nregs <= cum->sse_nregs && int_nregs <= cum->nregs) { cum->nregs -= int_nregs; cum->sse_nregs -= sse_nregs; cum->regno += int_nregs; cum->sse_regno += sse_nregs; } else cum->words += words; } else { if (TARGET_SSE && SSE_REG_MODE_P (mode) && (!type || !AGGREGATE_TYPE_P (type))) { cum->sse_words += words; cum->sse_nregs -= 1; cum->sse_regno += 1; if (cum->sse_nregs <= 0) { cum->sse_nregs = 0; cum->sse_regno = 0; } } else if (TARGET_MMX && MMX_REG_MODE_P (mode) && (!type || !AGGREGATE_TYPE_P (type))) { cum->mmx_words += words; cum->mmx_nregs -= 1; cum->mmx_regno += 1; if (cum->mmx_nregs <= 0) { cum->mmx_nregs = 0; cum->mmx_regno = 0; } } else { cum->words += words; cum->nregs -= words; cum->regno += words; if (cum->nregs <= 0) { cum->nregs = 0; cum->regno = 0; } } } return; } /* A subroutine of function_arg. We want to pass a parameter whose nominal type is MODE in REGNO. We try to minimize ABI variation, so MODE may not actually be valid for REGNO with the current ISA. In this case, ALT_MODE is used instead. It must be the same size as MODE, and must be known to be valid for REGNO. Finally, ORIG_MODE is the original mode of the parameter, as seen by the type system. This may be different from MODE when we're mucking with things minimizing ABI variations. Returns a REG or a PARALLEL as appropriate. */ static rtx gen_reg_or_parallel (enum machine_mode mode, enum machine_mode alt_mode, enum machine_mode orig_mode, unsigned int regno) { rtx tmp; if (HARD_REGNO_MODE_OK (regno, mode)) tmp = gen_rtx_REG (mode, regno); else { tmp = gen_rtx_REG (alt_mode, regno); tmp = gen_rtx_EXPR_LIST (VOIDmode, tmp, const0_rtx); tmp = gen_rtx_PARALLEL (orig_mode, gen_rtvec (1, tmp)); } return tmp; } /* 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). */ rtx function_arg (CUMULATIVE_ARGS *cum, enum machine_mode orig_mode, tree type, int named) { enum machine_mode mode = orig_mode; rtx ret = NULL_RTX; int bytes = (mode == BLKmode) ? int_size_in_bytes (type) : (int) GET_MODE_SIZE (mode); int words = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD; static bool warnedsse, warnedmmx; /* Handle a hidden AL argument containing number of registers for varargs x86-64 functions. For i386 ABI just return constm1_rtx to avoid any AL settings. */ if (mode == VOIDmode) { if (TARGET_64BIT) return GEN_INT (cum->maybe_vaarg ? (cum->sse_nregs < 0 ? SSE_REGPARM_MAX : cum->sse_regno) : -1); else return constm1_rtx; } if (TARGET_64BIT) ret = construct_container (mode, type, 0, cum->nregs, cum->sse_nregs, &x86_64_int_parameter_registers [cum->regno], cum->sse_regno); else switch (mode) { /* For now, pass fp/complex values on the stack. */ default: break; case BLKmode: if (bytes < 0) break; /* FALLTHRU */ case DImode: case SImode: case HImode: case QImode: if (words <= cum->nregs) { int regno = cum->regno; /* Fastcall allocates the first two DWORD (SImode) or smaller arguments to ECX and EDX. */ if (cum->fastcall) { if (mode == BLKmode || mode == DImode) break; /* ECX not EAX is the first allocated register. */ if (regno == 0) regno = 2; } ret = gen_rtx_REG (mode, regno); } break; case TImode: case V16QImode: case V8HImode: case V4SImode: case V2DImode: case V4SFmode: case V2DFmode: if (!type || !AGGREGATE_TYPE_P (type)) { if (!TARGET_SSE && !warnedmmx && cum->warn_sse) { warnedsse = true; warning ("SSE vector argument without SSE enabled " "changes the ABI"); } if (cum->sse_nregs) ret = gen_reg_or_parallel (mode, TImode, orig_mode, cum->sse_regno + FIRST_SSE_REG); } break; case V8QImode: case V4HImode: case V2SImode: case V2SFmode: if (!type || !AGGREGATE_TYPE_P (type)) { if (!TARGET_MMX && !warnedmmx && cum->warn_mmx) { warnedmmx = true; warning ("MMX vector argument without MMX enabled " "changes the ABI"); } if (cum->mmx_nregs) ret = gen_reg_or_parallel (mode, DImode, orig_mode, cum->mmx_regno + FIRST_MMX_REG); } break; } if (TARGET_DEBUG_ARG) { fprintf (stderr, "function_arg (size=%d, wds=%2d, nregs=%d, mode=%4s, named=%d, ", words, cum->words, cum->nregs, GET_MODE_NAME (mode), named); if (ret) print_simple_rtl (stderr, ret); else fprintf (stderr, ", stack"); fprintf (stderr, " )\n"); } return ret; } /* A C expression that indicates when an argument must be passed by reference. If nonzero for an argument, a copy of that argument is made in memory and a pointer to the argument is passed instead of the argument itself. The pointer is passed in whatever way is appropriate for passing a pointer to that type. */ int function_arg_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED, enum machine_mode mode ATTRIBUTE_UNUSED, tree type, int named ATTRIBUTE_UNUSED) { if (!TARGET_64BIT) return 0; if (type && int_size_in_bytes (type) == -1) { if (TARGET_DEBUG_ARG) fprintf (stderr, "function_arg_pass_by_reference\n"); return 1; } return 0; } /* Return true when TYPE should be 128bit aligned for 32bit argument passing ABI */ static bool contains_128bit_aligned_vector_p (tree type) { enum machine_mode mode = TYPE_MODE (type); if (SSE_REG_MODE_P (mode) && (!TYPE_USER_ALIGN (type) || TYPE_ALIGN (type) > 128)) return true; if (TYPE_ALIGN (type) < 128) return false; if (AGGREGATE_TYPE_P (type)) { /* Walk the aggregates recursively. */ if (TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE || TREE_CODE (type) == QUAL_UNION_TYPE) { tree field; if (TYPE_BINFO (type) != NULL && TYPE_BINFO_BASETYPES (type) != NULL) { tree bases = TYPE_BINFO_BASETYPES (type); int n_bases = TREE_VEC_LENGTH (bases); int i; for (i = 0; i < n_bases; ++i) { tree binfo = TREE_VEC_ELT (bases, i); tree type = BINFO_TYPE (binfo); if (contains_128bit_aligned_vector_p (type)) return true; } } /* And now merge the fields of structure. */ for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) { if (TREE_CODE (field) == FIELD_DECL && contains_128bit_aligned_vector_p (TREE_TYPE (field))) return true; } } /* Just for use if some languages passes arrays by value. */ else if (TREE_CODE (type) == ARRAY_TYPE) { if (contains_128bit_aligned_vector_p (TREE_TYPE (type))) return true; } else abort (); } return false; } /* Gives the alignment boundary, in bits, of an argument with the specified mode and type. */ int ix86_function_arg_boundary (enum machine_mode mode, tree type) { int align; if (type) align = TYPE_ALIGN (type); else align = GET_MODE_ALIGNMENT (mode); if (align < PARM_BOUNDARY) align = PARM_BOUNDARY; if (!TARGET_64BIT) { /* i386 ABI defines all arguments to be 4 byte aligned. We have to make an exception for SSE modes since these require 128bit alignment. The handling here differs from field_alignment. ICC aligns MMX arguments to 4 byte boundaries, while structure fields are aligned to 8 byte boundaries. */ if (!type) { if (!SSE_REG_MODE_P (mode)) align = PARM_BOUNDARY; } else { if (!contains_128bit_aligned_vector_p (type)) align = PARM_BOUNDARY; } } if (align > 128) align = 128; return align; } /* Return true if N is a possible register number of function value. */ bool ix86_function_value_regno_p (int regno) { if (!TARGET_64BIT) { return ((regno) == 0 || ((regno) == FIRST_FLOAT_REG && TARGET_FLOAT_RETURNS_IN_80387) || ((regno) == FIRST_SSE_REG && TARGET_SSE)); } return ((regno) == 0 || (regno) == FIRST_FLOAT_REG || ((regno) == FIRST_SSE_REG && TARGET_SSE) || ((regno) == FIRST_FLOAT_REG && TARGET_FLOAT_RETURNS_IN_80387)); } /* Define how to find the value returned by a function. VALTYPE is the data type of the value (as a tree). If the precise function being called is known, FUNC is its FUNCTION_DECL; otherwise, FUNC is 0. */ rtx ix86_function_value (tree valtype) { if (TARGET_64BIT) { rtx ret = construct_container (TYPE_MODE (valtype), valtype, 1, REGPARM_MAX, SSE_REGPARM_MAX, x86_64_int_return_registers, 0); /* For zero sized structures, construct_container return NULL, but we need to keep rest of compiler happy by returning meaningful value. */ if (!ret) ret = gen_rtx_REG (TYPE_MODE (valtype), 0); return ret; } else return gen_rtx_REG (TYPE_MODE (valtype), ix86_value_regno (TYPE_MODE (valtype))); } /* Return false iff type is returned in memory. */ int ix86_return_in_memory (tree type) { int needed_intregs, needed_sseregs, size; enum machine_mode mode = TYPE_MODE (type); if (TARGET_64BIT) return !examine_argument (mode, type, 1, &needed_intregs, &needed_sseregs); if (mode == BLKmode) return 1; size = int_size_in_bytes (type); if (MS_AGGREGATE_RETURN && AGGREGATE_TYPE_P (type) && size <= 8) return 0; if (VECTOR_MODE_P (mode) || mode == TImode) { /* User-created vectors small enough to fit in EAX. */ if (size < 8) return 0; /* MMX/3dNow values are returned on the stack, since we've got to EMMS/FEMMS before returning. */ if (size == 8) return 1; /* SSE values are returned in XMM0. */ /* ??? Except when it doesn't exist? We have a choice of either (1) being abi incompatible with a -march switch, or (2) generating an error here. Given no good solution, I think the safest thing is one warning. The user won't be able to use -Werror, but.... */ if (size == 16) { static bool warned; if (TARGET_SSE) return 0; if (!warned) { warned = true; warning ("SSE vector return without SSE enabled " "changes the ABI"); } return 1; } } if (mode == XFmode) return 0; if (size > 12) return 1; return 0; } /* Define how to find the value returned by a library function assuming the value has mode MODE. */ rtx ix86_libcall_value (enum machine_mode mode) { if (TARGET_64BIT) { switch (mode) { case SFmode: case SCmode: case DFmode: case DCmode: return gen_rtx_REG (mode, FIRST_SSE_REG); case XFmode: case XCmode: return gen_rtx_REG (mode, FIRST_FLOAT_REG); case TFmode: case TCmode: return NULL; default: return gen_rtx_REG (mode, 0); } } else return gen_rtx_REG (mode, ix86_value_regno (mode)); } /* Given a mode, return the register to use for a return value. */ static int ix86_value_regno (enum machine_mode mode) { /* Floating point return values in %st(0). */ if (GET_MODE_CLASS (mode) == MODE_FLOAT && TARGET_FLOAT_RETURNS_IN_80387) return FIRST_FLOAT_REG; /* 16-byte vector modes in %xmm0. See ix86_return_in_memory for where we prevent this case when sse is not available. */ if (mode == TImode || (VECTOR_MODE_P (mode) && GET_MODE_SIZE (mode) == 16)) return FIRST_SSE_REG; /* Everything else in %eax. */ return 0; } /* Create the va_list data type. */ static tree ix86_build_builtin_va_list (void) { tree f_gpr, f_fpr, f_ovf, f_sav, record, type_decl; /* For i386 we use plain pointer to argument area. */ if (!TARGET_64BIT) return build_pointer_type (char_type_node); record = (*lang_hooks.types.make_type) (RECORD_TYPE); type_decl = build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record); f_gpr = build_decl (FIELD_DECL, get_identifier ("gp_offset"), unsigned_type_node); f_fpr = build_decl (FIELD_DECL, get_identifier ("fp_offset"), unsigned_type_node); f_ovf = build_decl (FIELD_DECL, get_identifier ("overflow_arg_area"), ptr_type_node); f_sav = build_decl (FIELD_DECL, get_identifier ("reg_save_area"), ptr_type_node); DECL_FIELD_CONTEXT (f_gpr) = record; DECL_FIELD_CONTEXT (f_fpr) = record; DECL_FIELD_CONTEXT (f_ovf) = record; DECL_FIELD_CONTEXT (f_sav) = record; TREE_CHAIN (record) = type_decl; TYPE_NAME (record) = type_decl; TYPE_FIELDS (record) = f_gpr; TREE_CHAIN (f_gpr) = f_fpr; TREE_CHAIN (f_fpr) = f_ovf; TREE_CHAIN (f_ovf) = f_sav; layout_type (record); /* The correct type is an array type of one element. */ return build_array_type (record, build_index_type (size_zero_node)); } /* Perform any needed actions needed for a function that is receiving a variable number of arguments. CUM is as above. MODE and TYPE are the mode and type of the current parameter. PRETEND_SIZE is a variable that should be set to the amount of stack that must be pushed by the prolog to pretend that our caller pushed it. Normally, this macro will push all remaining incoming registers on the stack and set PRETEND_SIZE to the length of the registers pushed. */ void ix86_setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, int *pretend_size ATTRIBUTE_UNUSED, int no_rtl) { CUMULATIVE_ARGS next_cum; rtx save_area = NULL_RTX, mem; rtx label; rtx label_ref; rtx tmp_reg; rtx nsse_reg; int set; tree fntype; int stdarg_p; int i; if (!TARGET_64BIT) return; /* Indicate to allocate space on the stack for varargs save area. */ ix86_save_varrargs_registers = 1; cfun->stack_alignment_needed = 128; fntype = TREE_TYPE (current_function_decl); stdarg_p = (TYPE_ARG_TYPES (fntype) != 0 && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node)); /* For varargs, we do not want to skip the dummy va_dcl argument. For stdargs, we do want to skip the last named argument. */ next_cum = *cum; if (stdarg_p) function_arg_advance (&next_cum, mode, type, 1); if (!no_rtl) save_area = frame_pointer_rtx; set = get_varargs_alias_set (); for (i = next_cum.regno; i < ix86_regparm; i++) { mem = gen_rtx_MEM (Pmode, plus_constant (save_area, i * UNITS_PER_WORD)); set_mem_alias_set (mem, set); emit_move_insn (mem, gen_rtx_REG (Pmode, x86_64_int_parameter_registers[i])); } if (next_cum.sse_nregs) { /* Now emit code to save SSE registers. The AX parameter contains number of SSE parameter registers used to call this function. We use sse_prologue_save insn template that produces computed jump across SSE saves. We need some preparation work to get this working. */ label = gen_label_rtx (); label_ref = gen_rtx_LABEL_REF (Pmode, label); /* Compute address to jump to : label - 5*eax + nnamed_sse_arguments*5 */ tmp_reg = gen_reg_rtx (Pmode); nsse_reg = gen_reg_rtx (Pmode); emit_insn (gen_zero_extendqidi2 (nsse_reg, gen_rtx_REG (QImode, 0))); emit_insn (gen_rtx_SET (VOIDmode, tmp_reg, gen_rtx_MULT (Pmode, nsse_reg, GEN_INT (4)))); if (next_cum.sse_regno) emit_move_insn (nsse_reg, gen_rtx_CONST (DImode, gen_rtx_PLUS (DImode, label_ref, GEN_INT (next_cum.sse_regno * 4)))); else emit_move_insn (nsse_reg, label_ref); emit_insn (gen_subdi3 (nsse_reg, nsse_reg, tmp_reg)); /* Compute address of memory block we save into. We always use pointer pointing 127 bytes after first byte to store - this is needed to keep instruction size limited by 4 bytes. */ tmp_reg = gen_reg_rtx (Pmode); emit_insn (gen_rtx_SET (VOIDmode, tmp_reg, plus_constant (save_area, 8 * REGPARM_MAX + 127))); mem = gen_rtx_MEM (BLKmode, plus_constant (tmp_reg, -127)); set_mem_alias_set (mem, set); set_mem_align (mem, BITS_PER_WORD); /* And finally do the dirty job! */ emit_insn (gen_sse_prologue_save (mem, nsse_reg, GEN_INT (next_cum.sse_regno), label)); } } /* Implement va_start. */ void ix86_va_start (tree valist, rtx nextarg) { HOST_WIDE_INT words, n_gpr, n_fpr; tree f_gpr, f_fpr, f_ovf, f_sav; tree gpr, fpr, ovf, sav, t; /* Only 64bit target needs something special. */ if (!TARGET_64BIT) { std_expand_builtin_va_start (valist, nextarg); return; } f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node)); f_fpr = TREE_CHAIN (f_gpr); f_ovf = TREE_CHAIN (f_fpr); f_sav = TREE_CHAIN (f_ovf); valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist); gpr = build (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr); fpr = build (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr); ovf = build (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf); sav = build (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav); /* Count number of gp and fp argument registers used. */ words = current_function_args_info.words; n_gpr = current_function_args_info.regno; n_fpr = current_function_args_info.sse_regno; if (TARGET_DEBUG_ARG) fprintf (stderr, "va_start: words = %d, n_gpr = %d, n_fpr = %d\n", (int) words, (int) n_gpr, (int) n_fpr); t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr, build_int_2 (n_gpr * 8, 0)); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); t = build (MODIFY_EXPR, TREE_TYPE (fpr), fpr, build_int_2 (n_fpr * 16 + 8*REGPARM_MAX, 0)); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); /* Find the overflow area. */ t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx); if (words != 0) t = build (PLUS_EXPR, TREE_TYPE (ovf), t, build_int_2 (words * UNITS_PER_WORD, 0)); t = build (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); /* Find the register save area. Prologue of the function save it right above stack frame. */ t = make_tree (TREE_TYPE (sav), frame_pointer_rtx); t = build (MODIFY_EXPR, TREE_TYPE (sav), sav, t); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); } /* Implement va_arg. */ rtx ix86_va_arg (tree valist, tree type) { static const int intreg[6] = { 0, 1, 2, 3, 4, 5 }; tree f_gpr, f_fpr, f_ovf, f_sav; tree gpr, fpr, ovf, sav, t; int size, rsize; rtx lab_false, lab_over = NULL_RTX; rtx addr_rtx, r; rtx container; int indirect_p = 0; /* Only 64bit target needs something special. */ if (!TARGET_64BIT) { return std_expand_builtin_va_arg (valist, type); } f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node)); f_fpr = TREE_CHAIN (f_gpr); f_ovf = TREE_CHAIN (f_fpr); f_sav = TREE_CHAIN (f_ovf); valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist); gpr = build (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr); fpr = build (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr); ovf = build (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf); sav = build (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav); size = int_size_in_bytes (type); if (size == -1) { /* Passed by reference. */ indirect_p = 1; type = build_pointer_type (type); size = int_size_in_bytes (type); } rsize = (size + UNITS_PER_WORD - 1) / UNITS_PER_WORD; container = construct_container (TYPE_MODE (type), type, 0, REGPARM_MAX, SSE_REGPARM_MAX, intreg, 0); /* * Pull the value out of the saved registers ... */ addr_rtx = gen_reg_rtx (Pmode); if (container) { rtx int_addr_rtx, sse_addr_rtx; int needed_intregs, needed_sseregs; int need_temp; lab_over = gen_label_rtx (); lab_false = gen_label_rtx (); examine_argument (TYPE_MODE (type), type, 0, &needed_intregs, &needed_sseregs); need_temp = ((needed_intregs && TYPE_ALIGN (type) > 64) || TYPE_ALIGN (type) > 128); /* In case we are passing structure, verify that it is consecutive block on the register save area. If not we need to do moves. */ if (!need_temp && !REG_P (container)) { /* Verify that all registers are strictly consecutive */ if (SSE_REGNO_P (REGNO (XEXP (XVECEXP (container, 0, 0), 0)))) { int i; for (i = 0; i < XVECLEN (container, 0) && !need_temp; i++) { rtx slot = XVECEXP (container, 0, i); if (REGNO (XEXP (slot, 0)) != FIRST_SSE_REG + (unsigned int) i || INTVAL (XEXP (slot, 1)) != i * 16) need_temp = 1; } } else { int i; for (i = 0; i < XVECLEN (container, 0) && !need_temp; i++) { rtx slot = XVECEXP (container, 0, i); if (REGNO (XEXP (slot, 0)) != (unsigned int) i || INTVAL (XEXP (slot, 1)) != i * 8) need_temp = 1; } } } if (!need_temp) { int_addr_rtx = addr_rtx; sse_addr_rtx = addr_rtx; } else { int_addr_rtx = gen_reg_rtx (Pmode); sse_addr_rtx = gen_reg_rtx (Pmode); } /* First ensure that we fit completely in registers. */ if (needed_intregs) { emit_cmp_and_jump_insns (expand_expr (gpr, NULL_RTX, SImode, EXPAND_NORMAL), GEN_INT ((REGPARM_MAX - needed_intregs + 1) * 8), GE, const1_rtx, SImode, 1, lab_false); } if (needed_sseregs) { emit_cmp_and_jump_insns (expand_expr (fpr, NULL_RTX, SImode, EXPAND_NORMAL), GEN_INT ((SSE_REGPARM_MAX - needed_sseregs + 1) * 16 + REGPARM_MAX * 8), GE, const1_rtx, SImode, 1, lab_false); } /* Compute index to start of area used for integer regs. */ if (needed_intregs) { t = build (PLUS_EXPR, ptr_type_node, sav, gpr); r = expand_expr (t, int_addr_rtx, Pmode, EXPAND_NORMAL); if (r != int_addr_rtx) emit_move_insn (int_addr_rtx, r); } if (needed_sseregs) { t = build (PLUS_EXPR, ptr_type_node, sav, fpr); r = expand_expr (t, sse_addr_rtx, Pmode, EXPAND_NORMAL); if (r != sse_addr_rtx) emit_move_insn (sse_addr_rtx, r); } if (need_temp) { int i; rtx mem; rtx x; /* Never use the memory itself, as it has the alias set. */ x = XEXP (assign_temp (type, 0, 1, 0), 0); mem = gen_rtx_MEM (BLKmode, x); force_operand (x, addr_rtx); set_mem_alias_set (mem, get_varargs_alias_set ()); set_mem_align (mem, BITS_PER_UNIT); for (i = 0; i < XVECLEN (container, 0); i++) { rtx slot = XVECEXP (container, 0, i); rtx reg = XEXP (slot, 0); enum machine_mode mode = GET_MODE (reg); rtx src_addr; rtx src_mem; int src_offset; rtx dest_mem; if (SSE_REGNO_P (REGNO (reg))) { src_addr = sse_addr_rtx; src_offset = (REGNO (reg) - FIRST_SSE_REG) * 16; } else { src_addr = int_addr_rtx; src_offset = REGNO (reg) * 8; } src_mem = gen_rtx_MEM (mode, src_addr); set_mem_alias_set (src_mem, get_varargs_alias_set ()); src_mem = adjust_address (src_mem, mode, src_offset); dest_mem = adjust_address (mem, mode, INTVAL (XEXP (slot, 1))); emit_move_insn (dest_mem, src_mem); } } if (needed_intregs) { t = build (PLUS_EXPR, TREE_TYPE (gpr), gpr, build_int_2 (needed_intregs * 8, 0)); t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr, t); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); } if (needed_sseregs) { t = build (PLUS_EXPR, TREE_TYPE (fpr), fpr, build_int_2 (needed_sseregs * 16, 0)); t = build (MODIFY_EXPR, TREE_TYPE (fpr), fpr, t); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); } emit_jump_insn (gen_jump (lab_over)); emit_barrier (); emit_label (lab_false); } /* ... otherwise out of the overflow area. */ /* Care for on-stack alignment if needed. */ if (FUNCTION_ARG_BOUNDARY (VOIDmode, type) <= 64) t = ovf; else { HOST_WIDE_INT align = FUNCTION_ARG_BOUNDARY (VOIDmode, type) / 8; t = build (PLUS_EXPR, TREE_TYPE (ovf), ovf, build_int_2 (align - 1, 0)); t = build (BIT_AND_EXPR, TREE_TYPE (t), t, build_int_2 (-align, -1)); } t = save_expr (t); r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL); if (r != addr_rtx) emit_move_insn (addr_rtx, r); t = build (PLUS_EXPR, TREE_TYPE (t), t, build_int_2 (rsize * UNITS_PER_WORD, 0)); t = build (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); if (container) emit_label (lab_over); if (indirect_p) { r = gen_rtx_MEM (Pmode, addr_rtx); set_mem_alias_set (r, get_varargs_alias_set ()); emit_move_insn (addr_rtx, r); } return addr_rtx; } /* Return nonzero if OP is either a i387 or SSE fp register. */ int any_fp_register_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return ANY_FP_REG_P (op); } /* Return nonzero if OP is an i387 fp register. */ int fp_register_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return FP_REG_P (op); } /* Return nonzero if OP is a non-fp register_operand. */ int register_and_not_any_fp_reg_operand (rtx op, enum machine_mode mode) { return register_operand (op, mode) && !ANY_FP_REG_P (op); } /* Return nonzero if OP is a register operand other than an i387 fp register. */ int register_and_not_fp_reg_operand (rtx op, enum machine_mode mode) { return register_operand (op, mode) && !FP_REG_P (op); } /* Return nonzero if OP is general operand representable on x86_64. */ int x86_64_general_operand (rtx op, enum machine_mode mode) { if (!TARGET_64BIT) return general_operand (op, mode); if (nonimmediate_operand (op, mode)) return 1; return x86_64_sign_extended_value (op); } /* Return nonzero if OP is general operand representable on x86_64 as either sign extended or zero extended constant. */ int x86_64_szext_general_operand (rtx op, enum machine_mode mode) { if (!TARGET_64BIT) return general_operand (op, mode); if (nonimmediate_operand (op, mode)) return 1; return x86_64_sign_extended_value (op) || x86_64_zero_extended_value (op); } /* Return nonzero if OP is nonmemory operand representable on x86_64. */ int x86_64_nonmemory_operand (rtx op, enum machine_mode mode) { if (!TARGET_64BIT) return nonmemory_operand (op, mode); if (register_operand (op, mode)) return 1; return x86_64_sign_extended_value (op); } /* Return nonzero if OP is nonmemory operand acceptable by movabs patterns. */ int x86_64_movabs_operand (rtx op, enum machine_mode mode) { if (!TARGET_64BIT || !flag_pic) return nonmemory_operand (op, mode); if (register_operand (op, mode) || x86_64_sign_extended_value (op)) return 1; if (CONSTANT_P (op) && !symbolic_reference_mentioned_p (op)) return 1; return 0; } /* Return nonzero if OPNUM's MEM should be matched in movabs* patterns. */ int ix86_check_movabs (rtx insn, int opnum) { rtx set, mem; set = PATTERN (insn); if (GET_CODE (set) == PARALLEL) set = XVECEXP (set, 0, 0); if (GET_CODE (set) != SET) abort (); mem = XEXP (set, opnum); while (GET_CODE (mem) == SUBREG) mem = SUBREG_REG (mem); if (GET_CODE (mem) != MEM) abort (); return (volatile_ok || !MEM_VOLATILE_P (mem)); } /* Return nonzero if OP is nonmemory operand representable on x86_64. */ int x86_64_szext_nonmemory_operand (rtx op, enum machine_mode mode) { if (!TARGET_64BIT) return nonmemory_operand (op, mode); if (register_operand (op, mode)) return 1; return x86_64_sign_extended_value (op) || x86_64_zero_extended_value (op); } /* Return nonzero if OP is immediate operand representable on x86_64. */ int x86_64_immediate_operand (rtx op, enum machine_mode mode) { if (!TARGET_64BIT) return immediate_operand (op, mode); return x86_64_sign_extended_value (op); } /* Return nonzero if OP is immediate operand representable on x86_64. */ int x86_64_zext_immediate_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return x86_64_zero_extended_value (op); } /* Return nonzero if OP is CONST_INT >= 1 and <= 31 (a valid operand for shift & compare patterns, as shifting by 0 does not change flags), else return zero. */ int const_int_1_31_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return (GET_CODE (op) == CONST_INT && INTVAL (op) >= 1 && INTVAL (op) <= 31); } /* Returns 1 if OP is either a symbol reference or a sum of a symbol reference and a constant. */ int symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { switch (GET_CODE (op)) { case SYMBOL_REF: case LABEL_REF: return 1; case CONST: op = XEXP (op, 0); if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF || (GET_CODE (op) == UNSPEC && (XINT (op, 1) == UNSPEC_GOT || XINT (op, 1) == UNSPEC_GOTOFF || XINT (op, 1) == UNSPEC_GOTPCREL))) return 1; if (GET_CODE (op) != PLUS || GET_CODE (XEXP (op, 1)) != CONST_INT) return 0; op = XEXP (op, 0); if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF) return 1; /* Only @GOTOFF gets offsets. */ if (GET_CODE (op) != UNSPEC || XINT (op, 1) != UNSPEC_GOTOFF) return 0; op = XVECEXP (op, 0, 0); if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF) return 1; return 0; default: return 0; } } /* Return true if the operand contains a @GOT or @GOTOFF reference. */ int pic_symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { if (GET_CODE (op) != CONST) return 0; op = XEXP (op, 0); if (TARGET_64BIT) { if (GET_CODE (op) == UNSPEC && XINT (op, 1) == UNSPEC_GOTPCREL) return 1; if (GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 0)) == UNSPEC && XINT (XEXP (op, 0), 1) == UNSPEC_GOTPCREL) return 1; } else { if (GET_CODE (op) == UNSPEC) return 1; if (GET_CODE (op) != PLUS || GET_CODE (XEXP (op, 1)) != CONST_INT) return 0; op = XEXP (op, 0); if (GET_CODE (op) == UNSPEC) return 1; } return 0; } /* Return true if OP is a symbolic operand that resolves locally. */ static int local_symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { if (GET_CODE (op) == CONST && GET_CODE (XEXP (op, 0)) == PLUS && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT) op = XEXP (XEXP (op, 0), 0); if (GET_CODE (op) == LABEL_REF) return 1; if (GET_CODE (op) != SYMBOL_REF) return 0; if (SYMBOL_REF_LOCAL_P (op)) return 1; /* There is, however, a not insubstantial body of code in the rest of the compiler that assumes it can just stick the results of ASM_GENERATE_INTERNAL_LABEL in a symbol_ref and have done. */ /* ??? This is a hack. Should update the body of the compiler to always create a DECL an invoke targetm.encode_section_info. */ if (strncmp (XSTR (op, 0), internal_label_prefix, internal_label_prefix_len) == 0) return 1; return 0; } /* Test for various thread-local symbols. */ int tls_symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { if (GET_CODE (op) != SYMBOL_REF) return 0; return SYMBOL_REF_TLS_MODEL (op); } static inline int tls_symbolic_operand_1 (rtx op, enum tls_model kind) { if (GET_CODE (op) != SYMBOL_REF) return 0; return SYMBOL_REF_TLS_MODEL (op) == kind; } int global_dynamic_symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return tls_symbolic_operand_1 (op, TLS_MODEL_GLOBAL_DYNAMIC); } int local_dynamic_symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return tls_symbolic_operand_1 (op, TLS_MODEL_LOCAL_DYNAMIC); } int initial_exec_symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return tls_symbolic_operand_1 (op, TLS_MODEL_INITIAL_EXEC); } int local_exec_symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return tls_symbolic_operand_1 (op, TLS_MODEL_LOCAL_EXEC); } /* Test for a valid operand for a call instruction. Don't allow the arg pointer register or virtual regs since they may decay into reg + const, which the patterns can't handle. */ int call_insn_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { /* Disallow indirect through a virtual register. This leads to compiler aborts when trying to eliminate them. */ if (GET_CODE (op) == REG && (op == arg_pointer_rtx || op == frame_pointer_rtx || (REGNO (op) >= FIRST_PSEUDO_REGISTER && REGNO (op) <= LAST_VIRTUAL_REGISTER))) return 0; /* Disallow `call 1234'. Due to varying assembler lameness this gets either rejected or translated to `call .+1234'. */ if (GET_CODE (op) == CONST_INT) return 0; /* Explicitly allow SYMBOL_REF even if pic. */ if (GET_CODE (op) == SYMBOL_REF) return 1; /* Otherwise we can allow any general_operand in the address. */ return general_operand (op, Pmode); } /* Test for a valid operand for a call instruction. Don't allow the arg pointer register or virtual regs since they may decay into reg + const, which the patterns can't handle. */ int sibcall_insn_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { /* Disallow indirect through a virtual register. This leads to compiler aborts when trying to eliminate them. */ if (GET_CODE (op) == REG && (op == arg_pointer_rtx || op == frame_pointer_rtx || (REGNO (op) >= FIRST_PSEUDO_REGISTER && REGNO (op) <= LAST_VIRTUAL_REGISTER))) return 0; /* Explicitly allow SYMBOL_REF even if pic. */ if (GET_CODE (op) == SYMBOL_REF) return 1; /* Otherwise we can only allow register operands. */ return register_operand (op, Pmode); } int constant_call_address_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { if (GET_CODE (op) == CONST && GET_CODE (XEXP (op, 0)) == PLUS && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT) op = XEXP (XEXP (op, 0), 0); return GET_CODE (op) == SYMBOL_REF; } /* Match exactly zero and one. */ int const0_operand (rtx op, enum machine_mode mode) { return op == CONST0_RTX (mode); } int const1_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return op == const1_rtx; } /* Match 2, 4, or 8. Used for leal multiplicands. */ int const248_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return (GET_CODE (op) == CONST_INT && (INTVAL (op) == 2 || INTVAL (op) == 4 || INTVAL (op) == 8)); } int const_0_to_3_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return (GET_CODE (op) == CONST_INT && INTVAL (op) >= 0 && INTVAL (op) < 4); } int const_0_to_7_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return (GET_CODE (op) == CONST_INT && INTVAL (op) >= 0 && INTVAL (op) < 8); } int const_0_to_15_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return (GET_CODE (op) == CONST_INT && INTVAL (op) >= 0 && INTVAL (op) < 16); } int const_0_to_255_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return (GET_CODE (op) == CONST_INT && INTVAL (op) >= 0 && INTVAL (op) < 256); } /* True if this is a constant appropriate for an increment or decrement. */ int incdec_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { /* On Pentium4, the inc and dec operations causes extra dependency on flag registers, since carry flag is not set. */ if (TARGET_PENTIUM4 && !optimize_size) return 0; return op == const1_rtx || op == constm1_rtx; } /* Return nonzero if OP is acceptable as operand of DImode shift expander. */ int shiftdi_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { if (TARGET_64BIT) return nonimmediate_operand (op, mode); else return register_operand (op, mode); } /* Return false if this is the stack pointer, or any other fake register eliminable to the stack pointer. Otherwise, this is a register operand. This is used to prevent esp from being used as an index reg. Which would only happen in pathological cases. */ int reg_no_sp_operand (rtx op, enum machine_mode mode) { rtx t = op; if (GET_CODE (t) == SUBREG) t = SUBREG_REG (t); if (t == stack_pointer_rtx || t == arg_pointer_rtx || t == frame_pointer_rtx) return 0; return register_operand (op, mode); } int mmx_reg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return MMX_REG_P (op); } /* Return false if this is any eliminable register. Otherwise general_operand. */ int general_no_elim_operand (rtx op, enum machine_mode mode) { rtx t = op; if (GET_CODE (t) == SUBREG) t = SUBREG_REG (t); if (t == arg_pointer_rtx || t == frame_pointer_rtx || t == virtual_incoming_args_rtx || t == virtual_stack_vars_rtx || t == virtual_stack_dynamic_rtx) return 0; if (REG_P (t) && REGNO (t) >= FIRST_VIRTUAL_REGISTER && REGNO (t) <= LAST_VIRTUAL_REGISTER) return 0; return general_operand (op, mode); } /* Return false if this is any eliminable register. Otherwise register_operand or const_int. */ int nonmemory_no_elim_operand (rtx op, enum machine_mode mode) { rtx t = op; if (GET_CODE (t) == SUBREG) t = SUBREG_REG (t); if (t == arg_pointer_rtx || t == frame_pointer_rtx || t == virtual_incoming_args_rtx || t == virtual_stack_vars_rtx || t == virtual_stack_dynamic_rtx) return 0; return GET_CODE (op) == CONST_INT || register_operand (op, mode); } /* Return false if this is any eliminable register or stack register, otherwise work like register_operand. */ int index_register_operand (rtx op, enum machine_mode mode) { rtx t = op; if (GET_CODE (t) == SUBREG) t = SUBREG_REG (t); if (!REG_P (t)) return 0; if (t == arg_pointer_rtx || t == frame_pointer_rtx || t == virtual_incoming_args_rtx || t == virtual_stack_vars_rtx || t == virtual_stack_dynamic_rtx || REGNO (t) == STACK_POINTER_REGNUM) return 0; return general_operand (op, mode); } /* Return true if op is a Q_REGS class register. */ int q_regs_operand (rtx op, enum machine_mode mode) { if (mode != VOIDmode && GET_MODE (op) != mode) return 0; if (GET_CODE (op) == SUBREG) op = SUBREG_REG (op); return ANY_QI_REG_P (op); } /* Return true if op is an flags register. */ int flags_reg_operand (rtx op, enum machine_mode mode) { if (mode != VOIDmode && GET_MODE (op) != mode) return 0; return REG_P (op) && REGNO (op) == FLAGS_REG && GET_MODE (op) != VOIDmode; } /* Return true if op is a NON_Q_REGS class register. */ int non_q_regs_operand (rtx op, enum machine_mode mode) { if (mode != VOIDmode && GET_MODE (op) != mode) return 0; if (GET_CODE (op) == SUBREG) op = SUBREG_REG (op); return NON_QI_REG_P (op); } int zero_extended_scalar_load_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { unsigned n_elts; if (GET_CODE (op) != MEM) return 0; op = maybe_get_pool_constant (op); if (!op) return 0; if (GET_CODE (op) != CONST_VECTOR) return 0; n_elts = (GET_MODE_SIZE (GET_MODE (op)) / GET_MODE_SIZE (GET_MODE_INNER (GET_MODE (op)))); for (n_elts--; n_elts > 0; n_elts--) { rtx elt = CONST_VECTOR_ELT (op, n_elts); if (elt != CONST0_RTX (GET_MODE_INNER (GET_MODE (op)))) return 0; } return 1; } /* Return 1 when OP is operand acceptable for standard SSE move. */ int vector_move_operand (rtx op, enum machine_mode mode) { if (nonimmediate_operand (op, mode)) return 1; if (GET_MODE (op) != mode && mode != VOIDmode) return 0; return (op == CONST0_RTX (GET_MODE (op))); } /* Return true if op if a valid address, and does not contain a segment override. */ int no_seg_address_operand (rtx op, enum machine_mode mode) { struct ix86_address parts; if (! address_operand (op, mode)) return 0; if (! ix86_decompose_address (op, &parts)) abort (); return parts.seg == SEG_DEFAULT; } /* Return 1 if OP is a comparison that can be used in the CMPSS/CMPPS insns. */ int sse_comparison_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { enum rtx_code code = GET_CODE (op); switch (code) { /* Operations supported directly. */ case EQ: case LT: case LE: case UNORDERED: case NE: case UNGE: case UNGT: case ORDERED: return 1; /* These are equivalent to ones above in non-IEEE comparisons. */ case UNEQ: case UNLT: case UNLE: case LTGT: case GE: case GT: return !TARGET_IEEE_FP; default: return 0; } } /* Return 1 if OP is a valid comparison operator in valid mode. */ int ix86_comparison_operator (rtx op, enum machine_mode mode) { enum machine_mode inmode; enum rtx_code code = GET_CODE (op); if (mode != VOIDmode && GET_MODE (op) != mode) return 0; if (GET_RTX_CLASS (code) != '<') return 0; inmode = GET_MODE (XEXP (op, 0)); if (inmode == CCFPmode || inmode == CCFPUmode) { enum rtx_code second_code, bypass_code; ix86_fp_comparison_codes (code, &bypass_code, &code, &second_code); return (bypass_code == NIL && second_code == NIL); } switch (code) { case EQ: case NE: return 1; case LT: case GE: if (inmode == CCmode || inmode == CCGCmode || inmode == CCGOCmode || inmode == CCNOmode) return 1; return 0; case LTU: case GTU: case LEU: case ORDERED: case UNORDERED: case GEU: if (inmode == CCmode) return 1; return 0; case GT: case LE: if (inmode == CCmode || inmode == CCGCmode || inmode == CCNOmode) return 1; return 0; default: return 0; } } /* Return 1 if OP is a valid comparison operator testing carry flag to be set. */ int ix86_carry_flag_operator (rtx op, enum machine_mode mode) { enum machine_mode inmode; enum rtx_code code = GET_CODE (op); if (mode != VOIDmode && GET_MODE (op) != mode) return 0; if (GET_RTX_CLASS (code) != '<') return 0; inmode = GET_MODE (XEXP (op, 0)); if (GET_CODE (XEXP (op, 0)) != REG || REGNO (XEXP (op, 0)) != 17 || XEXP (op, 1) != const0_rtx) return 0; if (inmode == CCFPmode || inmode == CCFPUmode) { enum rtx_code second_code, bypass_code; ix86_fp_comparison_codes (code, &bypass_code, &code, &second_code); if (bypass_code != NIL || second_code != NIL) return 0; code = ix86_fp_compare_code_to_integer (code); } else if (inmode != CCmode) return 0; return code == LTU; } /* Return 1 if OP is a comparison operator that can be issued by fcmov. */ int fcmov_comparison_operator (rtx op, enum machine_mode mode) { enum machine_mode inmode; enum rtx_code code = GET_CODE (op); if (mode != VOIDmode && GET_MODE (op) != mode) return 0; if (GET_RTX_CLASS (code) != '<') return 0; inmode = GET_MODE (XEXP (op, 0)); if (inmode == CCFPmode || inmode == CCFPUmode) { enum rtx_code second_code, bypass_code; ix86_fp_comparison_codes (code, &bypass_code, &code, &second_code); if (bypass_code != NIL || second_code != NIL) return 0; code = ix86_fp_compare_code_to_integer (code); } /* i387 supports just limited amount of conditional codes. */ switch (code) { case LTU: case GTU: case LEU: case GEU: if (inmode == CCmode || inmode == CCFPmode || inmode == CCFPUmode) return 1; return 0; case ORDERED: case UNORDERED: case EQ: case NE: return 1; default: return 0; } } /* Return 1 if OP is a binary operator that can be promoted to wider mode. */ int promotable_binary_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { switch (GET_CODE (op)) { case MULT: /* Modern CPUs have same latency for HImode and SImode multiply, but 386 and 486 do HImode multiply faster. */ return ix86_tune > PROCESSOR_I486; case PLUS: case AND: case IOR: case XOR: case ASHIFT: return 1; default: return 0; } } /* Nearly general operand, but accept any const_double, since we wish to be able to drop them into memory rather than have them get pulled into registers. */ int cmp_fp_expander_operand (rtx op, enum machine_mode mode) { if (mode != VOIDmode && mode != GET_MODE (op)) return 0; if (GET_CODE (op) == CONST_DOUBLE) return 1; return general_operand (op, mode); } /* Match an SI or HImode register for a zero_extract. */ int ext_register_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { int regno; if ((!TARGET_64BIT || GET_MODE (op) != DImode) && GET_MODE (op) != SImode && GET_MODE (op) != HImode) return 0; if (!register_operand (op, VOIDmode)) return 0; /* Be careful to accept only registers having upper parts. */ regno = REG_P (op) ? REGNO (op) : REGNO (SUBREG_REG (op)); return (regno > LAST_VIRTUAL_REGISTER || regno < 4); } /* Return 1 if this is a valid binary floating-point operation. OP is the expression matched, and MODE is its mode. */ int binary_fp_operator (rtx op, enum machine_mode mode) { if (mode != VOIDmode && mode != GET_MODE (op)) return 0; switch (GET_CODE (op)) { case PLUS: case MINUS: case MULT: case DIV: return GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT; default: return 0; } } int mult_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return GET_CODE (op) == MULT; } int div_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return GET_CODE (op) == DIV; } int arith_or_logical_operator (rtx op, enum machine_mode mode) { return ((mode == VOIDmode || GET_MODE (op) == mode) && (GET_RTX_CLASS (GET_CODE (op)) == 'c' || GET_RTX_CLASS (GET_CODE (op)) == '2')); } /* Returns 1 if OP is memory operand with a displacement. */ int memory_displacement_operand (rtx op, enum machine_mode mode) { struct ix86_address parts; if (! memory_operand (op, mode)) return 0; if (! ix86_decompose_address (XEXP (op, 0), &parts)) abort (); return parts.disp != NULL_RTX; } /* To avoid problems when jump re-emits comparisons like testqi_ext_ccno_0, re-recognize the operand to avoid a copy_to_mode_reg that will fail. ??? It seems likely that this will only work because cmpsi is an expander, and no actual insns use this. */ int cmpsi_operand (rtx op, enum machine_mode mode) { if (nonimmediate_operand (op, mode)) return 1; if (GET_CODE (op) == AND && GET_MODE (op) == SImode && GET_CODE (XEXP (op, 0)) == ZERO_EXTRACT && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT && GET_CODE (XEXP (XEXP (op, 0), 2)) == CONST_INT && INTVAL (XEXP (XEXP (op, 0), 1)) == 8 && INTVAL (XEXP (XEXP (op, 0), 2)) == 8 && GET_CODE (XEXP (op, 1)) == CONST_INT) return 1; return 0; } /* Returns 1 if OP is memory operand that can not be represented by the modRM array. */ int long_memory_operand (rtx op, enum machine_mode mode) { if (! memory_operand (op, mode)) return 0; return memory_address_length (op) != 0; } /* Return nonzero if the rtx is known aligned. */ int aligned_operand (rtx op, enum machine_mode mode) { struct ix86_address parts; if (!general_operand (op, mode)) return 0; /* Registers and immediate operands are always "aligned". */ if (GET_CODE (op) != MEM) return 1; /* Don't even try to do any aligned optimizations with volatiles. */ if (MEM_VOLATILE_P (op)) return 0; op = XEXP (op, 0); /* Pushes and pops are only valid on the stack pointer. */ if (GET_CODE (op) == PRE_DEC || GET_CODE (op) == POST_INC) return 1; /* Decode the address. */ if (! ix86_decompose_address (op, &parts)) abort (); /* Look for some component that isn't known to be aligned. */ if (parts.index) { if (parts.scale < 4 && REGNO_POINTER_ALIGN (REGNO (parts.index)) < 32) return 0; } if (parts.base) { if (REGNO_POINTER_ALIGN (REGNO (parts.base)) < 32) return 0; } if (parts.disp) { if (GET_CODE (parts.disp) != CONST_INT || (INTVAL (parts.disp) & 3) != 0) return 0; } /* Didn't find one -- this must be an aligned address. */ return 1; } int compare_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return GET_CODE (op) == COMPARE; } /* Initialize the table of extra 80387 mathematical constants. */ static void init_ext_80387_constants (void) { static const char * cst[5] = { "0.3010299956639811952256464283594894482", /* 0: fldlg2 */ "0.6931471805599453094286904741849753009", /* 1: fldln2 */ "1.4426950408889634073876517827983434472", /* 2: fldl2e */ "3.3219280948873623478083405569094566090", /* 3: fldl2t */ "3.1415926535897932385128089594061862044", /* 4: fldpi */ }; int i; for (i = 0; i < 5; i++) { real_from_string (&ext_80387_constants_table[i], cst[i]); /* Ensure each constant is rounded to XFmode precision. */ real_convert (&ext_80387_constants_table[i], XFmode, &ext_80387_constants_table[i]); } ext_80387_constants_init = 1; } /* Return true if the constant is something that can be loaded with a special instruction. */ int standard_80387_constant_p (rtx x) { if (GET_CODE (x) != CONST_DOUBLE || !FLOAT_MODE_P (GET_MODE (x))) return -1; if (x == CONST0_RTX (GET_MODE (x))) return 1; if (x == CONST1_RTX (GET_MODE (x))) return 2; /* For XFmode constants, try to find a special 80387 instruction on those CPUs that benefit from them. */ if (GET_MODE (x) == XFmode && x86_ext_80387_constants & TUNEMASK) { REAL_VALUE_TYPE r; int i; if (! ext_80387_constants_init) init_ext_80387_constants (); REAL_VALUE_FROM_CONST_DOUBLE (r, x); for (i = 0; i < 5; i++) if (real_identical (&r, &ext_80387_constants_table[i])) return i + 3; } return 0; } /* Return the opcode of the special instruction to be used to load the constant X. */ const char * standard_80387_constant_opcode (rtx x) { switch (standard_80387_constant_p (x)) { case 1: return "fldz"; case 2: return "fld1"; case 3: return "fldlg2"; case 4: return "fldln2"; case 5: return "fldl2e"; case 6: return "fldl2t"; case 7: return "fldpi"; } abort (); } /* Return the CONST_DOUBLE representing the 80387 constant that is loaded by the specified special instruction. The argument IDX matches the return value from standard_80387_constant_p. */ rtx standard_80387_constant_rtx (int idx) { int i; if (! ext_80387_constants_init) init_ext_80387_constants (); switch (idx) { case 3: case 4: case 5: case 6: case 7: i = idx - 3; break; default: abort (); } return CONST_DOUBLE_FROM_REAL_VALUE (ext_80387_constants_table[i], XFmode); } /* Return 1 if X is FP constant we can load to SSE register w/o using memory. */ int standard_sse_constant_p (rtx x) { if (x == const0_rtx) return 1; return (x == CONST0_RTX (GET_MODE (x))); } /* Returns 1 if OP contains a symbol reference */ int symbolic_reference_mentioned_p (rtx op) { const char *fmt; int i; if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF) return 1; fmt = GET_RTX_FORMAT (GET_CODE (op)); for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--) { if (fmt[i] == 'E') { int j; for (j = XVECLEN (op, i) - 1; j >= 0; j--) if (symbolic_reference_mentioned_p (XVECEXP (op, i, j))) return 1; } else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i))) return 1; } return 0; } /* Return 1 if it is appropriate to emit `ret' instructions in the body of a function. Do this only if the epilogue is simple, needing a couple of insns. Prior to reloading, we can't tell how many registers must be saved, so return 0 then. Return 0 if there is no frame marker to de-allocate. If NON_SAVING_SETJMP is defined and true, then it is not possible for the epilogue to be simple, so return 0. This is a special case since NON_SAVING_SETJMP will not cause regs_ever_live to change until final, but jump_optimize may need to know sooner if a `return' is OK. */ int ix86_can_use_return_insn_p (void) { struct ix86_frame frame; #ifdef NON_SAVING_SETJMP if (NON_SAVING_SETJMP && current_function_calls_setjmp) return 0; #endif if (! reload_completed || frame_pointer_needed) return 0; /* Don't allow more than 32 pop, since that's all we can do with one instruction. */ if (current_function_pops_args && current_function_args_size >= 32768) return 0; ix86_compute_frame_layout (&frame); return frame.to_allocate == 0 && frame.nregs == 0; } /* Return 1 if VALUE can be stored in the sign extended immediate field. */ int x86_64_sign_extended_value (rtx value) { switch (GET_CODE (value)) { /* CONST_DOUBLES never match, since HOST_BITS_PER_WIDE_INT is known to be at least 32 and this all acceptable constants are represented as CONST_INT. */ case CONST_INT: if (HOST_BITS_PER_WIDE_INT == 32) return 1; else { HOST_WIDE_INT val = trunc_int_for_mode (INTVAL (value), DImode); return trunc_int_for_mode (val, SImode) == val; } break; /* For certain code models, the symbolic references are known to fit. in CM_SMALL_PIC model we know it fits if it is local to the shared library. Don't count TLS SYMBOL_REFs here, since they should fit only if inside of UNSPEC handled below. */ case SYMBOL_REF: /* TLS symbols are not constant. */ if (tls_symbolic_operand (value, Pmode)) return false; return (ix86_cmodel == CM_SMALL || ix86_cmodel == CM_KERNEL); /* For certain code models, the code is near as well. */ case LABEL_REF: return (ix86_cmodel == CM_SMALL || ix86_cmodel == CM_MEDIUM || ix86_cmodel == CM_KERNEL); /* We also may accept the offsetted memory references in certain special cases. */ case CONST: if (GET_CODE (XEXP (value, 0)) == UNSPEC) switch (XINT (XEXP (value, 0), 1)) { case UNSPEC_GOTPCREL: case UNSPEC_DTPOFF: case UNSPEC_GOTNTPOFF: case UNSPEC_NTPOFF: return 1; default: break; } if (GET_CODE (XEXP (value, 0)) == PLUS) { rtx op1 = XEXP (XEXP (value, 0), 0); rtx op2 = XEXP (XEXP (value, 0), 1); HOST_WIDE_INT offset; if (ix86_cmodel == CM_LARGE) return 0; if (GET_CODE (op2) != CONST_INT) return 0; offset = trunc_int_for_mode (INTVAL (op2), DImode); switch (GET_CODE (op1)) { case SYMBOL_REF: /* For CM_SMALL assume that latest object is 16MB before end of 31bits boundary. We may also accept pretty large negative constants knowing that all objects are in the positive half of address space. */ if (ix86_cmodel == CM_SMALL && offset < 16*1024*1024 && trunc_int_for_mode (offset, SImode) == offset) return 1; /* For CM_KERNEL we know that all object resist in the negative half of 32bits address space. We may not accept negative offsets, since they may be just off and we may accept pretty large positive ones. */ if (ix86_cmodel == CM_KERNEL && offset > 0 && trunc_int_for_mode (offset, SImode) == offset) return 1; break; case LABEL_REF: /* These conditions are similar to SYMBOL_REF ones, just the constraints for code models differ. */ if ((ix86_cmodel == CM_SMALL || ix86_cmodel == CM_MEDIUM) && offset < 16*1024*1024 && trunc_int_for_mode (offset, SImode) == offset) return 1; if (ix86_cmodel == CM_KERNEL && offset > 0 && trunc_int_for_mode (offset, SImode) == offset) return 1; break; case UNSPEC: switch (XINT (op1, 1)) { case UNSPEC_DTPOFF: case UNSPEC_NTPOFF: if (offset > 0 && trunc_int_for_mode (offset, SImode) == offset) return 1; } break; default: return 0; } } return 0; default: return 0; } } /* Return 1 if VALUE can be stored in the zero extended immediate field. */ int x86_64_zero_extended_value (rtx value) { switch (GET_CODE (value)) { case CONST_DOUBLE: if (HOST_BITS_PER_WIDE_INT == 32) return (GET_MODE (value) == VOIDmode && !CONST_DOUBLE_HIGH (value)); else return 0; case CONST_INT: if (HOST_BITS_PER_WIDE_INT == 32) return INTVAL (value) >= 0; else return !(INTVAL (value) & ~(HOST_WIDE_INT) 0xffffffff); break; /* For certain code models, the symbolic references are known to fit. */ case SYMBOL_REF: /* TLS symbols are not constant. */ if (tls_symbolic_operand (value, Pmode)) return false; return ix86_cmodel == CM_SMALL; /* For certain code models, the code is near as well. */ case LABEL_REF: return ix86_cmodel == CM_SMALL || ix86_cmodel == CM_MEDIUM; /* We also may accept the offsetted memory references in certain special cases. */ case CONST: if (GET_CODE (XEXP (value, 0)) == PLUS) { rtx op1 = XEXP (XEXP (value, 0), 0); rtx op2 = XEXP (XEXP (value, 0), 1); if (ix86_cmodel == CM_LARGE) return 0; switch (GET_CODE (op1)) { case SYMBOL_REF: return 0; /* For small code model we may accept pretty large positive offsets, since one bit is available for free. Negative offsets are limited by the size of NULL pointer area specified by the ABI. */ if (ix86_cmodel == CM_SMALL && GET_CODE (op2) == CONST_INT && trunc_int_for_mode (INTVAL (op2), DImode) > -0x10000 && (trunc_int_for_mode (INTVAL (op2), SImode) == INTVAL (op2))) return 1; /* ??? For the kernel, we may accept adjustment of -0x10000000, since we know that it will just convert negative address space to positive, but perhaps this is not worthwhile. */ break; case LABEL_REF: /* These conditions are similar to SYMBOL_REF ones, just the constraints for code models differ. */ if ((ix86_cmodel == CM_SMALL || ix86_cmodel == CM_MEDIUM) && GET_CODE (op2) == CONST_INT && trunc_int_for_mode (INTVAL (op2), DImode) > -0x10000 && (trunc_int_for_mode (INTVAL (op2), SImode) == INTVAL (op2))) return 1; break; default: return 0; } } return 0; default: return 0; } } /* Value should be nonzero if functions must have frame pointers. Zero means the frame pointer need not be set up (and parms may be accessed via the stack pointer) in functions that seem suitable. */ int ix86_frame_pointer_required (void) { /* If we accessed previous frames, then the generated code expects to be able to access the saved ebp value in our frame. */ if (cfun->machine->accesses_prev_frame) return 1; /* Several x86 os'es need a frame pointer for other reasons, usually pertaining to setjmp. */ if (SUBTARGET_FRAME_POINTER_REQUIRED) return 1; /* In override_options, TARGET_OMIT_LEAF_FRAME_POINTER turns off the frame pointer by default. Turn it back on now if we've not got a leaf function. */ if (TARGET_OMIT_LEAF_FRAME_POINTER && (!current_function_is_leaf)) return 1; if (current_function_profile) return 1; return 0; } /* Record that the current function accesses previous call frames. */ void ix86_setup_frame_addresses (void) { cfun->machine->accesses_prev_frame = 1; } #if defined(HAVE_GAS_HIDDEN) && defined(SUPPORTS_ONE_ONLY) # define USE_HIDDEN_LINKONCE 1 #else # define USE_HIDDEN_LINKONCE 0 #endif static int pic_labels_used; /* Fills in the label name that should be used for a pc thunk for the given register. */ static void get_pc_thunk_name (char name[32], unsigned int regno) { if (USE_HIDDEN_LINKONCE) sprintf (name, "__i686.get_pc_thunk.%s", reg_names[regno]); else ASM_GENERATE_INTERNAL_LABEL (name, "LPR", regno); } /* This function generates code for -fpic that loads %ebx with the return address of the caller and then returns. */ void ix86_file_end (void) { rtx xops[2]; int regno; for (regno = 0; regno < 8; ++regno) { char name[32]; if (! ((pic_labels_used >> regno) & 1)) continue; get_pc_thunk_name (name, regno); if (USE_HIDDEN_LINKONCE) { tree decl; decl = build_decl (FUNCTION_DECL, get_identifier (name), error_mark_node); TREE_PUBLIC (decl) = 1; TREE_STATIC (decl) = 1; DECL_ONE_ONLY (decl) = 1; (*targetm.asm_out.unique_section) (decl, 0); named_section (decl, NULL, 0); (*targetm.asm_out.globalize_label) (asm_out_file, name); fputs ("\t.hidden\t", asm_out_file); assemble_name (asm_out_file, name); fputc ('\n', asm_out_file); ASM_DECLARE_FUNCTION_NAME (asm_out_file, name, decl); } else { text_section (); ASM_OUTPUT_LABEL (asm_out_file, name); } xops[0] = gen_rtx_REG (SImode, regno); xops[1] = gen_rtx_MEM (SImode, stack_pointer_rtx); output_asm_insn ("mov{l}\t{%1, %0|%0, %1}", xops); output_asm_insn ("ret", xops); } if (NEED_INDICATE_EXEC_STACK) file_end_indicate_exec_stack (); } /* Emit code for the SET_GOT patterns. */ const char * output_set_got (rtx dest) { rtx xops[3]; xops[0] = dest; xops[1] = gen_rtx_SYMBOL_REF (Pmode, GOT_SYMBOL_NAME); if (! TARGET_DEEP_BRANCH_PREDICTION || !flag_pic) { xops[2] = gen_rtx_LABEL_REF (Pmode, gen_label_rtx ()); if (!flag_pic) output_asm_insn ("mov{l}\t{%2, %0|%0, %2}", xops); else output_asm_insn ("call\t%a2", xops); #if TARGET_MACHO /* Output the "canonical" label name ("Lxx$pb") here too. This is what will be referred to by the Mach-O PIC subsystem. */ ASM_OUTPUT_LABEL (asm_out_file, machopic_function_base_name ()); #endif (*targetm.asm_out.internal_label) (asm_out_file, "L", CODE_LABEL_NUMBER (XEXP (xops[2], 0))); if (flag_pic) output_asm_insn ("pop{l}\t%0", xops); } else { char name[32]; get_pc_thunk_name (name, REGNO (dest)); pic_labels_used |= 1 << REGNO (dest); xops[2] = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name)); xops[2] = gen_rtx_MEM (QImode, xops[2]); output_asm_insn ("call\t%X2", xops); } if (!flag_pic || TARGET_DEEP_BRANCH_PREDICTION) output_asm_insn ("add{l}\t{%1, %0|%0, %1}", xops); else if (!TARGET_MACHO) - output_asm_insn ("add{l}\t{%1+[.-%a2], %0|%0, %a1+(.-%a2)}", xops); + output_asm_insn ("add{l}\t{%1+[.-%a2], %0|%0, %1+(.-%a2)}", xops); return ""; } /* Generate an "push" pattern for input ARG. */ static rtx gen_push (rtx arg) { return gen_rtx_SET (VOIDmode, gen_rtx_MEM (Pmode, gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx)), arg); } /* Return >= 0 if there is an unused call-clobbered register available for the entire function. */ static unsigned int ix86_select_alt_pic_regnum (void) { if (current_function_is_leaf && !current_function_profile) { int i; for (i = 2; i >= 0; --i) if (!regs_ever_live[i]) return i; } return INVALID_REGNUM; } /* Return 1 if we need to save REGNO. */ static int ix86_save_reg (unsigned int regno, int maybe_eh_return) { if (pic_offset_table_rtx && regno == REAL_PIC_OFFSET_TABLE_REGNUM && (regs_ever_live[REAL_PIC_OFFSET_TABLE_REGNUM] || current_function_profile || current_function_calls_eh_return || current_function_uses_const_pool)) { if (ix86_select_alt_pic_regnum () != INVALID_REGNUM) return 0; return 1; } if (current_function_calls_eh_return && maybe_eh_return) { unsigned i; for (i = 0; ; i++) { unsigned test = EH_RETURN_DATA_REGNO (i); if (test == INVALID_REGNUM) break; if (test == regno) return 1; } } return (regs_ever_live[regno] && !call_used_regs[regno] && !fixed_regs[regno] && (regno != HARD_FRAME_POINTER_REGNUM || !frame_pointer_needed)); } /* Return number of registers to be saved on the stack. */ static int ix86_nsaved_regs (void) { int nregs = 0; int regno; for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno--) if (ix86_save_reg (regno, true)) nregs++; return nregs; } /* Return the offset between two registers, one to be eliminated, and the other its replacement, at the start of a routine. */ HOST_WIDE_INT ix86_initial_elimination_offset (int from, int to) { struct ix86_frame frame; ix86_compute_frame_layout (&frame); if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM) return frame.hard_frame_pointer_offset; else if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM) return frame.hard_frame_pointer_offset - frame.frame_pointer_offset; else { if (to != STACK_POINTER_REGNUM) abort (); else if (from == ARG_POINTER_REGNUM) return frame.stack_pointer_offset; else if (from != FRAME_POINTER_REGNUM) abort (); else return frame.stack_pointer_offset - frame.frame_pointer_offset; } } /* Fill structure ix86_frame about frame of currently computed function. */ static void ix86_compute_frame_layout (struct ix86_frame *frame) { HOST_WIDE_INT total_size; int stack_alignment_needed = cfun->stack_alignment_needed / BITS_PER_UNIT; HOST_WIDE_INT offset; int preferred_alignment = cfun->preferred_stack_boundary / BITS_PER_UNIT; HOST_WIDE_INT size = get_frame_size (); frame->nregs = ix86_nsaved_regs (); total_size = size; /* During reload iteration the amount of registers saved can change. Recompute the value as needed. Do not recompute when amount of registers didn't change as reload does mutiple calls to the function and does not expect the decision to change within single iteration. */ if (!optimize_size && cfun->machine->use_fast_prologue_epilogue_nregs != frame->nregs) { int count = frame->nregs; cfun->machine->use_fast_prologue_epilogue_nregs = count; /* The fast prologue uses move instead of push to save registers. This is significantly longer, but also executes faster as modern hardware can execute the moves in parallel, but can't do that for push/pop. Be careful about choosing what prologue to emit: When function takes many instructions to execute we may use slow version as well as in case function is known to be outside hot spot (this is known with feedback only). Weight the size of function by number of registers to save as it is cheap to use one or two push instructions but very slow to use many of them. */ if (count) count = (count - 1) * FAST_PROLOGUE_INSN_COUNT; if (cfun->function_frequency < FUNCTION_FREQUENCY_NORMAL || (flag_branch_probabilities && cfun->function_frequency < FUNCTION_FREQUENCY_HOT)) cfun->machine->use_fast_prologue_epilogue = false; else cfun->machine->use_fast_prologue_epilogue = !expensive_function_p (count); } if (TARGET_PROLOGUE_USING_MOVE && cfun->machine->use_fast_prologue_epilogue) frame->save_regs_using_mov = true; else frame->save_regs_using_mov = false; /* Skip return address and saved base pointer. */ offset = frame_pointer_needed ? UNITS_PER_WORD * 2 : UNITS_PER_WORD; frame->hard_frame_pointer_offset = offset; /* Do some sanity checking of stack_alignment_needed and preferred_alignment, since i386 port is the only using those features that may break easily. */ if (size && !stack_alignment_needed) abort (); if (preferred_alignment < STACK_BOUNDARY / BITS_PER_UNIT) abort (); if (preferred_alignment > PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT) abort (); if (stack_alignment_needed > PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT) abort (); if (stack_alignment_needed < STACK_BOUNDARY / BITS_PER_UNIT) stack_alignment_needed = STACK_BOUNDARY / BITS_PER_UNIT; /* Register save area */ offset += frame->nregs * UNITS_PER_WORD; /* Va-arg area */ if (ix86_save_varrargs_registers) { offset += X86_64_VARARGS_SIZE; frame->va_arg_size = X86_64_VARARGS_SIZE; } else frame->va_arg_size = 0; /* Align start of frame for local function. */ frame->padding1 = ((offset + stack_alignment_needed - 1) & -stack_alignment_needed) - offset; offset += frame->padding1; /* Frame pointer points here. */ frame->frame_pointer_offset = offset; offset += size; /* Add outgoing arguments area. Can be skipped if we eliminated all the function calls as dead code. Skipping is however impossible when function calls alloca. Alloca expander assumes that last current_function_outgoing_args_size of stack frame are unused. */ if (ACCUMULATE_OUTGOING_ARGS && (!current_function_is_leaf || current_function_calls_alloca)) { offset += current_function_outgoing_args_size; frame->outgoing_arguments_size = current_function_outgoing_args_size; } else frame->outgoing_arguments_size = 0; /* Align stack boundary. Only needed if we're calling another function or using alloca. */ if (!current_function_is_leaf || current_function_calls_alloca) frame->padding2 = ((offset + preferred_alignment - 1) & -preferred_alignment) - offset; else frame->padding2 = 0; offset += frame->padding2; /* We've reached end of stack frame. */ frame->stack_pointer_offset = offset; /* Size prologue needs to allocate. */ frame->to_allocate = (size + frame->padding1 + frame->padding2 + frame->outgoing_arguments_size + frame->va_arg_size); if ((!frame->to_allocate && frame->nregs <= 1) || (TARGET_64BIT && frame->to_allocate >= (HOST_WIDE_INT) 0x80000000)) frame->save_regs_using_mov = false; if (TARGET_RED_ZONE && current_function_sp_is_unchanging && current_function_is_leaf) { frame->red_zone_size = frame->to_allocate; if (frame->save_regs_using_mov) frame->red_zone_size += frame->nregs * UNITS_PER_WORD; if (frame->red_zone_size > RED_ZONE_SIZE - RED_ZONE_RESERVE) frame->red_zone_size = RED_ZONE_SIZE - RED_ZONE_RESERVE; } else frame->red_zone_size = 0; frame->to_allocate -= frame->red_zone_size; frame->stack_pointer_offset -= frame->red_zone_size; #if 0 fprintf (stderr, "nregs: %i\n", frame->nregs); fprintf (stderr, "size: %i\n", size); fprintf (stderr, "alignment1: %i\n", stack_alignment_needed); fprintf (stderr, "padding1: %i\n", frame->padding1); fprintf (stderr, "va_arg: %i\n", frame->va_arg_size); fprintf (stderr, "padding2: %i\n", frame->padding2); fprintf (stderr, "to_allocate: %i\n", frame->to_allocate); fprintf (stderr, "red_zone_size: %i\n", frame->red_zone_size); fprintf (stderr, "frame_pointer_offset: %i\n", frame->frame_pointer_offset); fprintf (stderr, "hard_frame_pointer_offset: %i\n", frame->hard_frame_pointer_offset); fprintf (stderr, "stack_pointer_offset: %i\n", frame->stack_pointer_offset); #endif } /* Emit code to save registers in the prologue. */ static void ix86_emit_save_regs (void) { int regno; rtx insn; for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno--) if (ix86_save_reg (regno, true)) { insn = emit_insn (gen_push (gen_rtx_REG (Pmode, regno))); RTX_FRAME_RELATED_P (insn) = 1; } } /* Emit code to save registers using MOV insns. First register is restored from POINTER + OFFSET. */ static void ix86_emit_save_regs_using_mov (rtx pointer, HOST_WIDE_INT offset) { int regno; rtx insn; for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) if (ix86_save_reg (regno, true)) { insn = emit_move_insn (adjust_address (gen_rtx_MEM (Pmode, pointer), Pmode, offset), gen_rtx_REG (Pmode, regno)); RTX_FRAME_RELATED_P (insn) = 1; offset += UNITS_PER_WORD; } } /* Expand prologue or epilogue stack adjustment. The pattern exist to put a dependency on all ebp-based memory accesses. STYLE should be negative if instructions should be marked as frame related, zero if %r11 register is live and cannot be freely used and positive otherwise. */ static void pro_epilogue_adjust_stack (rtx dest, rtx src, rtx offset, int style) { rtx insn; if (! TARGET_64BIT) insn = emit_insn (gen_pro_epilogue_adjust_stack_1 (dest, src, offset)); else if (x86_64_immediate_operand (offset, DImode)) insn = emit_insn (gen_pro_epilogue_adjust_stack_rex64 (dest, src, offset)); else { rtx r11; /* r11 is used by indirect sibcall return as well, set before the epilogue and used after the epilogue. ATM indirect sibcall shouldn't be used together with huge frame sizes in one function because of the frame_size check in sibcall.c. */ if (style == 0) abort (); r11 = gen_rtx_REG (DImode, FIRST_REX_INT_REG + 3 /* R11 */); insn = emit_insn (gen_rtx_SET (DImode, r11, offset)); if (style < 0) RTX_FRAME_RELATED_P (insn) = 1; insn = emit_insn (gen_pro_epilogue_adjust_stack_rex64_2 (dest, src, r11, offset)); } if (style < 0) RTX_FRAME_RELATED_P (insn) = 1; } /* Expand the prologue into a bunch of separate insns. */ void ix86_expand_prologue (void) { rtx insn; bool pic_reg_used; struct ix86_frame frame; HOST_WIDE_INT allocate; ix86_compute_frame_layout (&frame); /* Note: AT&T enter does NOT have reversed args. Enter is probably slower on all targets. Also sdb doesn't like it. */ if (frame_pointer_needed) { insn = emit_insn (gen_push (hard_frame_pointer_rtx)); RTX_FRAME_RELATED_P (insn) = 1; insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx); RTX_FRAME_RELATED_P (insn) = 1; } allocate = frame.to_allocate; if (!frame.save_regs_using_mov) ix86_emit_save_regs (); else allocate += frame.nregs * UNITS_PER_WORD; /* When using red zone we may start register saving before allocating the stack frame saving one cycle of the prologue. */ if (TARGET_RED_ZONE && frame.save_regs_using_mov) ix86_emit_save_regs_using_mov (frame_pointer_needed ? hard_frame_pointer_rtx : stack_pointer_rtx, -frame.nregs * UNITS_PER_WORD); if (allocate == 0) ; else if (! TARGET_STACK_PROBE || allocate < CHECK_STACK_LIMIT) pro_epilogue_adjust_stack (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (-allocate), -1); else { /* Only valid for Win32. */ rtx eax = gen_rtx_REG (SImode, 0); bool eax_live = ix86_eax_live_at_start_p (); if (TARGET_64BIT) abort (); if (eax_live) { emit_insn (gen_push (eax)); allocate -= 4; } insn = emit_move_insn (eax, GEN_INT (allocate)); RTX_FRAME_RELATED_P (insn) = 1; insn = emit_insn (gen_allocate_stack_worker (eax)); RTX_FRAME_RELATED_P (insn) = 1; if (eax_live) { rtx t = plus_constant (stack_pointer_rtx, allocate); emit_move_insn (eax, gen_rtx_MEM (SImode, t)); } } if (frame.save_regs_using_mov && !TARGET_RED_ZONE) { if (!frame_pointer_needed || !frame.to_allocate) ix86_emit_save_regs_using_mov (stack_pointer_rtx, frame.to_allocate); else ix86_emit_save_regs_using_mov (hard_frame_pointer_rtx, -frame.nregs * UNITS_PER_WORD); } pic_reg_used = false; if (pic_offset_table_rtx && (regs_ever_live[REAL_PIC_OFFSET_TABLE_REGNUM] || current_function_profile)) { unsigned int alt_pic_reg_used = ix86_select_alt_pic_regnum (); if (alt_pic_reg_used != INVALID_REGNUM) REGNO (pic_offset_table_rtx) = alt_pic_reg_used; pic_reg_used = true; } if (pic_reg_used) { insn = emit_insn (gen_set_got (pic_offset_table_rtx)); /* Even with accurate pre-reload life analysis, we can wind up deleting all references to the pic register after reload. Consider if cross-jumping unifies two sides of a branch controlled by a comparison vs the only read from a global. In which case, allow the set_got to be deleted, though we're too late to do anything about the ebx save in the prologue. */ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, NULL); } /* Prevent function calls from be scheduled before the call to mcount. In the pic_reg_used case, make sure that the got load isn't deleted. */ if (current_function_profile) emit_insn (gen_blockage (pic_reg_used ? pic_offset_table_rtx : const0_rtx)); } /* Emit code to restore saved registers using MOV insns. First register is restored from POINTER + OFFSET. */ static void ix86_emit_restore_regs_using_mov (rtx pointer, HOST_WIDE_INT offset, int maybe_eh_return) { int regno; rtx base_address = gen_rtx_MEM (Pmode, pointer); for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) if (ix86_save_reg (regno, maybe_eh_return)) { /* Ensure that adjust_address won't be forced to produce pointer out of range allowed by x86-64 instruction set. */ if (TARGET_64BIT && offset != trunc_int_for_mode (offset, SImode)) { rtx r11; r11 = gen_rtx_REG (DImode, FIRST_REX_INT_REG + 3 /* R11 */); emit_move_insn (r11, GEN_INT (offset)); emit_insn (gen_adddi3 (r11, r11, pointer)); base_address = gen_rtx_MEM (Pmode, r11); offset = 0; } emit_move_insn (gen_rtx_REG (Pmode, regno), adjust_address (base_address, Pmode, offset)); offset += UNITS_PER_WORD; } } /* Restore function stack, frame, and registers. */ void ix86_expand_epilogue (int style) { int regno; int sp_valid = !frame_pointer_needed || current_function_sp_is_unchanging; struct ix86_frame frame; HOST_WIDE_INT offset; ix86_compute_frame_layout (&frame); /* Calculate start of saved registers relative to ebp. Special care must be taken for the normal return case of a function using eh_return: the eax and edx registers are marked as saved, but not restored along this path. */ offset = frame.nregs; if (current_function_calls_eh_return && style != 2) offset -= 2; offset *= -UNITS_PER_WORD; /* If we're only restoring one register and sp is not valid then using a move instruction to restore the register since it's less work than reloading sp and popping the register. The default code result in stack adjustment using add/lea instruction, while this code results in LEAVE instruction (or discrete equivalent), so it is profitable in some other cases as well. Especially when there are no registers to restore. We also use this code when TARGET_USE_LEAVE and there is exactly one register to pop. This heuristic may need some tuning in future. */ if ((!sp_valid && frame.nregs <= 1) || (TARGET_EPILOGUE_USING_MOVE && cfun->machine->use_fast_prologue_epilogue && (frame.nregs > 1 || frame.to_allocate)) || (frame_pointer_needed && !frame.nregs && frame.to_allocate) || (frame_pointer_needed && TARGET_USE_LEAVE && cfun->machine->use_fast_prologue_epilogue && frame.nregs == 1) || current_function_calls_eh_return) { /* Restore registers. We can use ebp or esp to address the memory locations. If both are available, default to ebp, since offsets are known to be small. Only exception is esp pointing directly to the end of block of saved registers, where we may simplify addressing mode. */ if (!frame_pointer_needed || (sp_valid && !frame.to_allocate)) ix86_emit_restore_regs_using_mov (stack_pointer_rtx, frame.to_allocate, style == 2); else ix86_emit_restore_regs_using_mov (hard_frame_pointer_rtx, offset, style == 2); /* eh_return epilogues need %ecx added to the stack pointer. */ if (style == 2) { rtx tmp, sa = EH_RETURN_STACKADJ_RTX; if (frame_pointer_needed) { tmp = gen_rtx_PLUS (Pmode, hard_frame_pointer_rtx, sa); tmp = plus_constant (tmp, UNITS_PER_WORD); emit_insn (gen_rtx_SET (VOIDmode, sa, tmp)); tmp = gen_rtx_MEM (Pmode, hard_frame_pointer_rtx); emit_move_insn (hard_frame_pointer_rtx, tmp); pro_epilogue_adjust_stack (stack_pointer_rtx, sa, const0_rtx, style); } else { tmp = gen_rtx_PLUS (Pmode, stack_pointer_rtx, sa); tmp = plus_constant (tmp, (frame.to_allocate + frame.nregs * UNITS_PER_WORD)); emit_insn (gen_rtx_SET (VOIDmode, stack_pointer_rtx, tmp)); } } else if (!frame_pointer_needed) pro_epilogue_adjust_stack (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (frame.to_allocate + frame.nregs * UNITS_PER_WORD), style); /* If not an i386, mov & pop is faster than "leave". */ else if (TARGET_USE_LEAVE || optimize_size || !cfun->machine->use_fast_prologue_epilogue) emit_insn (TARGET_64BIT ? gen_leave_rex64 () : gen_leave ()); else { pro_epilogue_adjust_stack (stack_pointer_rtx, hard_frame_pointer_rtx, const0_rtx, style); if (TARGET_64BIT) emit_insn (gen_popdi1 (hard_frame_pointer_rtx)); else emit_insn (gen_popsi1 (hard_frame_pointer_rtx)); } } else { /* First step is to deallocate the stack frame so that we can pop the registers. */ if (!sp_valid) { if (!frame_pointer_needed) abort (); pro_epilogue_adjust_stack (stack_pointer_rtx, hard_frame_pointer_rtx, GEN_INT (offset), style); } else if (frame.to_allocate) pro_epilogue_adjust_stack (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (frame.to_allocate), style); for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) if (ix86_save_reg (regno, false)) { if (TARGET_64BIT) emit_insn (gen_popdi1 (gen_rtx_REG (Pmode, regno))); else emit_insn (gen_popsi1 (gen_rtx_REG (Pmode, regno))); } if (frame_pointer_needed) { /* Leave results in shorter dependency chains on CPUs that are able to grok it fast. */ if (TARGET_USE_LEAVE) emit_insn (TARGET_64BIT ? gen_leave_rex64 () : gen_leave ()); else if (TARGET_64BIT) emit_insn (gen_popdi1 (hard_frame_pointer_rtx)); else emit_insn (gen_popsi1 (hard_frame_pointer_rtx)); } } /* Sibcall epilogues don't want a return instruction. */ if (style == 0) return; if (current_function_pops_args && current_function_args_size) { rtx popc = GEN_INT (current_function_pops_args); /* i386 can only pop 64K bytes. If asked to pop more, pop return address, do explicit add, and jump indirectly to the caller. */ if (current_function_pops_args >= 65536) { rtx ecx = gen_rtx_REG (SImode, 2); /* There is no "pascal" calling convention in 64bit ABI. */ if (TARGET_64BIT) abort (); emit_insn (gen_popsi1 (ecx)); emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, popc)); emit_jump_insn (gen_return_indirect_internal (ecx)); } else emit_jump_insn (gen_return_pop_internal (popc)); } else emit_jump_insn (gen_return_internal ()); } /* Reset from the function's potential modifications. */ static void ix86_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED, HOST_WIDE_INT size ATTRIBUTE_UNUSED) { if (pic_offset_table_rtx) REGNO (pic_offset_table_rtx) = REAL_PIC_OFFSET_TABLE_REGNUM; } /* Extract the parts of an RTL expression that is a valid memory address for an instruction. Return 0 if the structure of the address is grossly off. Return -1 if the address contains ASHIFT, so it is not strictly valid, but still used for computing length of lea instruction. */ static int ix86_decompose_address (rtx addr, struct ix86_address *out) { rtx base = NULL_RTX; rtx index = NULL_RTX; rtx disp = NULL_RTX; HOST_WIDE_INT scale = 1; rtx scale_rtx = NULL_RTX; int retval = 1; enum ix86_address_seg seg = SEG_DEFAULT; if (GET_CODE (addr) == REG || GET_CODE (addr) == SUBREG) base = addr; else if (GET_CODE (addr) == PLUS) { rtx addends[4], op; int n = 0, i; op = addr; do { if (n >= 4) return 0; addends[n++] = XEXP (op, 1); op = XEXP (op, 0); } while (GET_CODE (op) == PLUS); if (n >= 4) return 0; addends[n] = op; for (i = n; i >= 0; --i) { op = addends[i]; switch (GET_CODE (op)) { case MULT: if (index) return 0; index = XEXP (op, 0); scale_rtx = XEXP (op, 1); break; case UNSPEC: if (XINT (op, 1) == UNSPEC_TP && TARGET_TLS_DIRECT_SEG_REFS && seg == SEG_DEFAULT) seg = TARGET_64BIT ? SEG_FS : SEG_GS; else return 0; break; case REG: case SUBREG: if (!base) base = op; else if (!index) index = op; else return 0; break; case CONST: case CONST_INT: case SYMBOL_REF: case LABEL_REF: if (disp) return 0; disp = op; break; default: return 0; } } } else if (GET_CODE (addr) == MULT) { index = XEXP (addr, 0); /* index*scale */ scale_rtx = XEXP (addr, 1); } else if (GET_CODE (addr) == ASHIFT) { rtx tmp; /* We're called for lea too, which implements ashift on occasion. */ index = XEXP (addr, 0); tmp = XEXP (addr, 1); if (GET_CODE (tmp) != CONST_INT) return 0; scale = INTVAL (tmp); if ((unsigned HOST_WIDE_INT) scale > 3) return 0; scale = 1 << scale; retval = -1; } else disp = addr; /* displacement */ /* Extract the integral value of scale. */ if (scale_rtx) { if (GET_CODE (scale_rtx) != CONST_INT) return 0; scale = INTVAL (scale_rtx); } /* Allow arg pointer and stack pointer as index if there is not scaling. */ if (base && index && scale == 1 && (index == arg_pointer_rtx || index == frame_pointer_rtx || (REG_P (index) && REGNO (index) == STACK_POINTER_REGNUM))) { rtx tmp = base; base = index; index = tmp; } /* Special case: %ebp cannot be encoded as a base without a displacement. */ if ((base == hard_frame_pointer_rtx || base == frame_pointer_rtx || base == arg_pointer_rtx) && !disp) disp = const0_rtx; /* Special case: on K6, [%esi] makes the instruction vector decoded. Avoid this by transforming to [%esi+0]. */ if (ix86_tune == PROCESSOR_K6 && !optimize_size && base && !index && !disp && REG_P (base) && REGNO_REG_CLASS (REGNO (base)) == SIREG) disp = const0_rtx; /* Special case: encode reg+reg instead of reg*2. */ if (!base && index && scale && scale == 2) base = index, scale = 1; /* Special case: scaling cannot be encoded without base or displacement. */ if (!base && !disp && index && scale != 1) disp = const0_rtx; out->base = base; out->index = index; out->disp = disp; out->scale = scale; out->seg = seg; return retval; } /* Return cost of the memory address x. For i386, it is better to use a complex address than let gcc copy the address into a reg and make a new pseudo. But not if the address requires to two regs - that would mean more pseudos with longer lifetimes. */ static int ix86_address_cost (rtx x) { struct ix86_address parts; int cost = 1; if (!ix86_decompose_address (x, &parts)) abort (); /* More complex memory references are better. */ if (parts.disp && parts.disp != const0_rtx) cost--; if (parts.seg != SEG_DEFAULT) cost--; /* Attempt to minimize number of registers in the address. */ if ((parts.base && (!REG_P (parts.base) || REGNO (parts.base) >= FIRST_PSEUDO_REGISTER)) || (parts.index && (!REG_P (parts.index) || REGNO (parts.index) >= FIRST_PSEUDO_REGISTER))) cost++; if (parts.base && (!REG_P (parts.base) || REGNO (parts.base) >= FIRST_PSEUDO_REGISTER) && parts.index && (!REG_P (parts.index) || REGNO (parts.index) >= FIRST_PSEUDO_REGISTER) && parts.base != parts.index) cost++; /* AMD-K6 don't like addresses with ModR/M set to 00_xxx_100b, since it's predecode logic can't detect the length of instructions and it degenerates to vector decoded. Increase cost of such addresses here. The penalty is minimally 2 cycles. It may be worthwhile to split such addresses or even refuse such addresses at all. Following addressing modes are affected: [base+scale*index] [scale*index+disp] [base+index] The first and last case may be avoidable by explicitly coding the zero in memory address, but I don't have AMD-K6 machine handy to check this theory. */ if (TARGET_K6 && ((!parts.disp && parts.base && parts.index && parts.scale != 1) || (parts.disp && !parts.base && parts.index && parts.scale != 1) || (!parts.disp && parts.base && parts.index && parts.scale == 1))) cost += 10; return cost; } /* If X is a machine specific address (i.e. a symbol or label being referenced as a displacement from the GOT implemented using an UNSPEC), then return the base term. Otherwise return X. */ rtx ix86_find_base_term (rtx x) { rtx term; if (TARGET_64BIT) { if (GET_CODE (x) != CONST) return x; term = XEXP (x, 0); if (GET_CODE (term) == PLUS && (GET_CODE (XEXP (term, 1)) == CONST_INT || GET_CODE (XEXP (term, 1)) == CONST_DOUBLE)) term = XEXP (term, 0); if (GET_CODE (term) != UNSPEC || XINT (term, 1) != UNSPEC_GOTPCREL) return x; term = XVECEXP (term, 0, 0); if (GET_CODE (term) != SYMBOL_REF && GET_CODE (term) != LABEL_REF) return x; return term; } term = ix86_delegitimize_address (x); if (GET_CODE (term) != SYMBOL_REF && GET_CODE (term) != LABEL_REF) return x; return term; } /* Determine if a given RTX is a valid constant. We already know this satisfies CONSTANT_P. */ bool legitimate_constant_p (rtx x) { switch (GET_CODE (x)) { case CONST: x = XEXP (x, 0); if (GET_CODE (x) == PLUS) { if (GET_CODE (XEXP (x, 1)) != CONST_INT) return false; x = XEXP (x, 0); } /* Only some unspecs are valid as "constants". */ if (GET_CODE (x) == UNSPEC) switch (XINT (x, 1)) { case UNSPEC_TPOFF: case UNSPEC_NTPOFF: return local_exec_symbolic_operand (XVECEXP (x, 0, 0), Pmode); case UNSPEC_DTPOFF: return local_dynamic_symbolic_operand (XVECEXP (x, 0, 0), Pmode); default: return false; } /* We must have drilled down to a symbol. */ if (!symbolic_operand (x, Pmode)) return false; /* FALLTHRU */ case SYMBOL_REF: /* TLS symbols are never valid. */ if (tls_symbolic_operand (x, Pmode)) return false; break; default: break; } /* Otherwise we handle everything else in the move patterns. */ return true; } /* Determine if it's legal to put X into the constant pool. This is not possible for the address of thread-local symbols, which is checked above. */ static bool ix86_cannot_force_const_mem (rtx x) { return !legitimate_constant_p (x); } /* Determine if a given RTX is a valid constant address. */ bool constant_address_p (rtx x) { return CONSTANT_P (x) && legitimate_address_p (Pmode, x, 1); } /* Nonzero if the constant value X is a legitimate general operand when generating PIC code. It is given that flag_pic is on and that X satisfies CONSTANT_P or is a CONST_DOUBLE. */ bool legitimate_pic_operand_p (rtx x) { rtx inner; switch (GET_CODE (x)) { case CONST: inner = XEXP (x, 0); /* Only some unspecs are valid as "constants". */ if (GET_CODE (inner) == UNSPEC) switch (XINT (inner, 1)) { case UNSPEC_TPOFF: return local_exec_symbolic_operand (XVECEXP (inner, 0, 0), Pmode); default: return false; } /* FALLTHRU */ case SYMBOL_REF: case LABEL_REF: return legitimate_pic_address_disp_p (x); default: return true; } } /* Determine if a given CONST RTX is a valid memory displacement in PIC mode. */ int legitimate_pic_address_disp_p (rtx disp) { bool saw_plus; /* In 64bit mode we can allow direct addresses of symbols and labels when they are not dynamic symbols. */ if (TARGET_64BIT) { /* TLS references should always be enclosed in UNSPEC. */ if (tls_symbolic_operand (disp, GET_MODE (disp))) return 0; if (GET_CODE (disp) == SYMBOL_REF && ix86_cmodel == CM_SMALL_PIC && SYMBOL_REF_LOCAL_P (disp)) return 1; if (GET_CODE (disp) == LABEL_REF) return 1; if (GET_CODE (disp) == CONST && GET_CODE (XEXP (disp, 0)) == PLUS) { rtx op0 = XEXP (XEXP (disp, 0), 0); rtx op1 = XEXP (XEXP (disp, 0), 1); /* TLS references should always be enclosed in UNSPEC. */ if (tls_symbolic_operand (op0, GET_MODE (op0))) return 0; if (((GET_CODE (op0) == SYMBOL_REF && ix86_cmodel == CM_SMALL_PIC && SYMBOL_REF_LOCAL_P (op0)) || GET_CODE (op0) == LABEL_REF) && GET_CODE (op1) == CONST_INT && INTVAL (op1) < 16*1024*1024 && INTVAL (op1) >= -16*1024*1024) return 1; } } if (GET_CODE (disp) != CONST) return 0; disp = XEXP (disp, 0); if (TARGET_64BIT) { /* We are unsafe to allow PLUS expressions. This limit allowed distance of GOT tables. We should not need these anyway. */ if (GET_CODE (disp) != UNSPEC || XINT (disp, 1) != UNSPEC_GOTPCREL) return 0; if (GET_CODE (XVECEXP (disp, 0, 0)) != SYMBOL_REF && GET_CODE (XVECEXP (disp, 0, 0)) != LABEL_REF) return 0; return 1; } saw_plus = false; if (GET_CODE (disp) == PLUS) { if (GET_CODE (XEXP (disp, 1)) != CONST_INT) return 0; disp = XEXP (disp, 0); saw_plus = true; } /* Allow {LABEL | SYMBOL}_REF - SYMBOL_REF-FOR-PICBASE for Mach-O. */ if (TARGET_MACHO && GET_CODE (disp) == MINUS) { if (GET_CODE (XEXP (disp, 0)) == LABEL_REF || GET_CODE (XEXP (disp, 0)) == SYMBOL_REF) if (GET_CODE (XEXP (disp, 1)) == SYMBOL_REF) { const char *sym_name = XSTR (XEXP (disp, 1), 0); if (! strcmp (sym_name, "")) return 1; } } if (GET_CODE (disp) != UNSPEC) return 0; switch (XINT (disp, 1)) { case UNSPEC_GOT: if (saw_plus) return false; return GET_CODE (XVECEXP (disp, 0, 0)) == SYMBOL_REF; case UNSPEC_GOTOFF: if (GET_CODE (XVECEXP (disp, 0, 0)) == SYMBOL_REF || GET_CODE (XVECEXP (disp, 0, 0)) == LABEL_REF) return local_symbolic_operand (XVECEXP (disp, 0, 0), Pmode); return false; case UNSPEC_GOTTPOFF: case UNSPEC_GOTNTPOFF: case UNSPEC_INDNTPOFF: if (saw_plus) return false; return initial_exec_symbolic_operand (XVECEXP (disp, 0, 0), Pmode); case UNSPEC_NTPOFF: return local_exec_symbolic_operand (XVECEXP (disp, 0, 0), Pmode); case UNSPEC_DTPOFF: return local_dynamic_symbolic_operand (XVECEXP (disp, 0, 0), Pmode); } return 0; } /* 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. It only recognizes address in canonical form. LEGITIMIZE_ADDRESS should convert common non-canonical forms to canonical form so that they will be recognized. */ int legitimate_address_p (enum machine_mode mode, rtx addr, int strict) { struct ix86_address parts; rtx base, index, disp; HOST_WIDE_INT scale; const char *reason = NULL; rtx reason_rtx = 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 (ix86_decompose_address (addr, &parts) <= 0) { reason = "decomposition failed"; goto report_error; } base = parts.base; index = parts.index; disp = parts.disp; scale = parts.scale; /* 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) { reason_rtx = base; if (GET_CODE (base) != REG) { reason = "base is not a register"; goto report_error; } if (GET_MODE (base) != Pmode) { reason = "base is not in Pmode"; goto report_error; } if ((strict && ! REG_OK_FOR_BASE_STRICT_P (base)) || (! strict && ! REG_OK_FOR_BASE_NONSTRICT_P (base))) { reason = "base is not valid"; goto report_error; } } /* 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 (index) { reason_rtx = index; if (GET_CODE (index) != REG) { reason = "index is not a register"; goto report_error; } if (GET_MODE (index) != Pmode) { reason = "index is not in Pmode"; goto report_error; } if ((strict && ! REG_OK_FOR_INDEX_STRICT_P (index)) || (! strict && ! REG_OK_FOR_INDEX_NONSTRICT_P (index))) { reason = "index is not valid"; goto report_error; } } /* Validate scale factor. */ if (scale != 1) { reason_rtx = GEN_INT (scale); if (!index) { reason = "scale without index"; goto report_error; } if (scale != 2 && scale != 4 && scale != 8) { reason = "scale is not a valid multiplier"; goto report_error; } } /* Validate displacement. */ if (disp) { reason_rtx = disp; if (GET_CODE (disp) == CONST && GET_CODE (XEXP (disp, 0)) == UNSPEC) switch (XINT (XEXP (disp, 0), 1)) { case UNSPEC_GOT: case UNSPEC_GOTOFF: case UNSPEC_GOTPCREL: if (!flag_pic) abort (); goto is_legitimate_pic; case UNSPEC_GOTTPOFF: case UNSPEC_GOTNTPOFF: case UNSPEC_INDNTPOFF: case UNSPEC_NTPOFF: case UNSPEC_DTPOFF: break; default: reason = "invalid address unspec"; goto report_error; } else if (flag_pic && (SYMBOLIC_CONST (disp) #if TARGET_MACHO && !machopic_operand_p (disp) #endif )) { is_legitimate_pic: if (TARGET_64BIT && (index || base)) { /* foo@dtpoff(%rX) is ok. */ if (GET_CODE (disp) != CONST || GET_CODE (XEXP (disp, 0)) != PLUS || GET_CODE (XEXP (XEXP (disp, 0), 0)) != UNSPEC || GET_CODE (XEXP (XEXP (disp, 0), 1)) != CONST_INT || (XINT (XEXP (XEXP (disp, 0), 0), 1) != UNSPEC_DTPOFF && XINT (XEXP (XEXP (disp, 0), 0), 1) != UNSPEC_NTPOFF)) { reason = "non-constant pic memory reference"; goto report_error; } } else if (! legitimate_pic_address_disp_p (disp)) { reason = "displacement is an invalid pic construct"; goto report_error; } /* This code used to verify that a symbolic pic displacement includes the pic_offset_table_rtx register. While this is good idea, unfortunately these constructs may be created by "adds using lea" optimization for incorrect code like: int a; int foo(int i) { return *(&a+i); } This code is nonsensical, but results in addressing GOT table with pic_offset_table_rtx base. We can't just refuse it easily, since it gets matched by "addsi3" pattern, that later gets split to lea in the case output register differs from input. While this can be handled by separate addsi pattern for this case that never results in lea, this seems to be easier and correct fix for crash to disable this test. */ } else if (GET_CODE (disp) != LABEL_REF && GET_CODE (disp) != CONST_INT && (GET_CODE (disp) != CONST || !legitimate_constant_p (disp)) && (GET_CODE (disp) != SYMBOL_REF || !legitimate_constant_p (disp))) { reason = "displacement is not constant"; goto report_error; } else if (TARGET_64BIT && !x86_64_sign_extended_value (disp)) { reason = "displacement is out of range"; goto report_error; } } /* Everything looks valid. */ if (TARGET_DEBUG_ADDR) fprintf (stderr, "Success.\n"); return TRUE; report_error: if (TARGET_DEBUG_ADDR) { fprintf (stderr, "Error: %s\n", reason); debug_rtx (reason_rtx); } return FALSE; } /* Return an unique alias set for the GOT. */ static HOST_WIDE_INT ix86_GOT_alias_set (void) { static HOST_WIDE_INT set = -1; if (set == -1) set = new_alias_set (); return set; } /* Return a legitimate reference for ORIG (an address) using the register REG. If REG is 0, a new pseudo is generated. There are two 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, constant pool addresses, and code labels compute the address as an offset from the GOT, whose base is in the PIC reg. Static data objects have SYMBOL_FLAG_LOCAL set to differentiate them from global data objects. The returned address is the PIC reg + an unspec constant. GO_IF_LEGITIMATE_ADDRESS rejects symbolic references unless the PIC reg also appears in the address. */ rtx legitimize_pic_address (rtx orig, rtx reg) { rtx addr = orig; rtx new = orig; rtx base; #if TARGET_MACHO if (reg == 0) reg = gen_reg_rtx (Pmode); /* Use the generic Mach-O PIC machinery. */ return machopic_legitimize_pic_address (orig, GET_MODE (orig), reg); #endif if (TARGET_64BIT && legitimate_pic_address_disp_p (addr)) new = addr; else if (!TARGET_64BIT && local_symbolic_operand (addr, Pmode)) { /* This symbol may be referenced via a displacement from the PIC base address (@GOTOFF). */ if (reload_in_progress) regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; if (GET_CODE (addr) == CONST) addr = XEXP (addr, 0); if (GET_CODE (addr) == PLUS) { new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, XEXP (addr, 0)), UNSPEC_GOTOFF); new = gen_rtx_PLUS (Pmode, new, XEXP (addr, 1)); } else new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTOFF); new = gen_rtx_CONST (Pmode, new); new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new); if (reg != 0) { emit_move_insn (reg, new); new = reg; } } else if (GET_CODE (addr) == SYMBOL_REF) { if (TARGET_64BIT) { new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTPCREL); new = gen_rtx_CONST (Pmode, new); new = gen_rtx_MEM (Pmode, new); RTX_UNCHANGING_P (new) = 1; set_mem_alias_set (new, ix86_GOT_alias_set ()); if (reg == 0) reg = gen_reg_rtx (Pmode); /* Use directly gen_movsi, otherwise the address is loaded into register for CSE. We don't want to CSE this addresses, instead we CSE addresses from the GOT table, so skip this. */ emit_insn (gen_movsi (reg, new)); new = reg; } else { /* This symbol must be referenced via a load from the Global Offset Table (@GOT). */ if (reload_in_progress) regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT); new = gen_rtx_CONST (Pmode, new); new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new); new = gen_rtx_MEM (Pmode, new); RTX_UNCHANGING_P (new) = 1; set_mem_alias_set (new, ix86_GOT_alias_set ()); if (reg == 0) reg = gen_reg_rtx (Pmode); emit_move_insn (reg, new); new = reg; } } else { if (GET_CODE (addr) == CONST) { addr = XEXP (addr, 0); /* We must match stuff we generate before. Assume the only unspecs that can get here are ours. Not that we could do anything with them anyway.... */ if (GET_CODE (addr) == UNSPEC || (GET_CODE (addr) == PLUS && GET_CODE (XEXP (addr, 0)) == UNSPEC)) return orig; if (GET_CODE (addr) != PLUS) abort (); } if (GET_CODE (addr) == PLUS) { rtx op0 = XEXP (addr, 0), op1 = XEXP (addr, 1); /* Check first to see if this is a constant offset from a @GOTOFF symbol reference. */ if (local_symbolic_operand (op0, Pmode) && GET_CODE (op1) == CONST_INT) { if (!TARGET_64BIT) { if (reload_in_progress) regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op0), UNSPEC_GOTOFF); new = gen_rtx_PLUS (Pmode, new, op1); new = gen_rtx_CONST (Pmode, new); new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new); if (reg != 0) { emit_move_insn (reg, new); new = reg; } } else { if (INTVAL (op1) < -16*1024*1024 || INTVAL (op1) >= 16*1024*1024) new = gen_rtx_PLUS (Pmode, op0, force_reg (Pmode, op1)); } } else { base = legitimize_pic_address (XEXP (addr, 0), reg); new = legitimize_pic_address (XEXP (addr, 1), base == reg ? NULL_RTX : reg); if (GET_CODE (new) == CONST_INT) new = plus_constant (base, INTVAL (new)); else { if (GET_CODE (new) == PLUS && CONSTANT_P (XEXP (new, 1))) { base = gen_rtx_PLUS (Pmode, base, XEXP (new, 0)); new = XEXP (new, 1); } new = gen_rtx_PLUS (Pmode, base, new); } } } } return new; } /* Load the thread pointer. If TO_REG is true, force it into a register. */ static rtx get_thread_pointer (int to_reg) { rtx tp, reg, insn; tp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TP); if (!to_reg) return tp; reg = gen_reg_rtx (Pmode); insn = gen_rtx_SET (VOIDmode, reg, tp); insn = emit_insn (insn); return reg; } /* A subroutine of legitimize_address and ix86_expand_move. FOR_MOV is false if we expect this to be used for a memory address and true if we expect to load the address into a register. */ static rtx legitimize_tls_address (rtx x, enum tls_model model, int for_mov) { rtx dest, base, off, pic; int type; switch (model) { case TLS_MODEL_GLOBAL_DYNAMIC: dest = gen_reg_rtx (Pmode); if (TARGET_64BIT) { rtx rax = gen_rtx_REG (Pmode, 0), insns; start_sequence (); emit_call_insn (gen_tls_global_dynamic_64 (rax, x)); insns = get_insns (); end_sequence (); emit_libcall_block (insns, dest, rax, x); } else emit_insn (gen_tls_global_dynamic_32 (dest, x)); break; case TLS_MODEL_LOCAL_DYNAMIC: base = gen_reg_rtx (Pmode); if (TARGET_64BIT) { rtx rax = gen_rtx_REG (Pmode, 0), insns, note; start_sequence (); emit_call_insn (gen_tls_local_dynamic_base_64 (rax)); insns = get_insns (); end_sequence (); note = gen_rtx_EXPR_LIST (VOIDmode, const0_rtx, NULL); note = gen_rtx_EXPR_LIST (VOIDmode, ix86_tls_get_addr (), note); emit_libcall_block (insns, base, rax, note); } else emit_insn (gen_tls_local_dynamic_base_32 (base)); off = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), UNSPEC_DTPOFF); off = gen_rtx_CONST (Pmode, off); return gen_rtx_PLUS (Pmode, base, off); case TLS_MODEL_INITIAL_EXEC: if (TARGET_64BIT) { pic = NULL; type = UNSPEC_GOTNTPOFF; } else if (flag_pic) { if (reload_in_progress) regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; pic = pic_offset_table_rtx; type = TARGET_GNU_TLS ? UNSPEC_GOTNTPOFF : UNSPEC_GOTTPOFF; } else if (!TARGET_GNU_TLS) { pic = gen_reg_rtx (Pmode); emit_insn (gen_set_got (pic)); type = UNSPEC_GOTTPOFF; } else { pic = NULL; type = UNSPEC_INDNTPOFF; } off = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), type); off = gen_rtx_CONST (Pmode, off); if (pic) off = gen_rtx_PLUS (Pmode, pic, off); off = gen_rtx_MEM (Pmode, off); RTX_UNCHANGING_P (off) = 1; set_mem_alias_set (off, ix86_GOT_alias_set ()); if (TARGET_64BIT || TARGET_GNU_TLS) { base = get_thread_pointer (for_mov || !TARGET_TLS_DIRECT_SEG_REFS); off = force_reg (Pmode, off); return gen_rtx_PLUS (Pmode, base, off); } else { base = get_thread_pointer (true); dest = gen_reg_rtx (Pmode); emit_insn (gen_subsi3 (dest, base, off)); } break; case TLS_MODEL_LOCAL_EXEC: off = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), (TARGET_64BIT || TARGET_GNU_TLS) ? UNSPEC_NTPOFF : UNSPEC_TPOFF); off = gen_rtx_CONST (Pmode, off); if (TARGET_64BIT || TARGET_GNU_TLS) { base = get_thread_pointer (for_mov || !TARGET_TLS_DIRECT_SEG_REFS); return gen_rtx_PLUS (Pmode, base, off); } else { base = get_thread_pointer (true); dest = gen_reg_rtx (Pmode); emit_insn (gen_subsi3 (dest, base, off)); } break; default: abort (); } return dest; } /* 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 (rtx x, rtx oldx ATTRIBUTE_UNUSED, 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); } log = tls_symbolic_operand (x, mode); if (log) return legitimize_tls_address (x, log, false); 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; rtx other = NULL_RTX; 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) { rtx temp = gen_reg_rtx (Pmode); 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) { rtx temp = gen_reg_rtx (Pmode); 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 expressions. FILE is the stdio stream to write to, X is the rtx, and CODE is the operand print code from the output string. */ static void output_pic_addr_const (FILE *file, rtx x, int code) { char buf[256]; switch (GET_CODE (x)) { case PC: if (flag_pic) putc ('.', file); else abort (); break; case SYMBOL_REF: assemble_name (file, XSTR (x, 0)); if (!TARGET_MACHO && code == 'P' && ! SYMBOL_REF_LOCAL_P (x)) fputs ("@PLT", file); break; case LABEL_REF: x = XEXP (x, 0); /* FALLTHRU */ case CODE_LABEL: ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x)); assemble_name (asm_out_file, buf); break; case CONST_INT: fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x)); break; case CONST: /* This used to output parentheses around the expression, but that does not work on the 386 (either ATT or BSD assembler). */ output_pic_addr_const (file, XEXP (x, 0), code); break; case CONST_DOUBLE: if (GET_MODE (x) == VOIDmode) { /* We can use %d if the number is <32 bits and positive. */ if (CONST_DOUBLE_HIGH (x) || CONST_DOUBLE_LOW (x) < 0) fprintf (file, "0x%lx%08lx", (unsigned long) CONST_DOUBLE_HIGH (x), (unsigned long) CONST_DOUBLE_LOW (x)); else fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_DOUBLE_LOW (x)); } else /* We can't handle floating point constants; PRINT_OPERAND must handle them. */ output_operand_lossage ("floating constant misused"); break; case PLUS: /* Some assemblers need integer constants to appear first. */ if (GET_CODE (XEXP (x, 0)) == CONST_INT) { output_pic_addr_const (file, XEXP (x, 0), code); putc ('+', file); output_pic_addr_const (file, XEXP (x, 1), code); } else if (GET_CODE (XEXP (x, 1)) == CONST_INT) { output_pic_addr_const (file, XEXP (x, 1), code); putc ('+', file); output_pic_addr_const (file, XEXP (x, 0), code); } else abort (); break; case MINUS: if (!TARGET_MACHO) putc (ASSEMBLER_DIALECT == ASM_INTEL ? '(' : '[', file); output_pic_addr_const (file, XEXP (x, 0), code); putc ('-', file); output_pic_addr_const (file, XEXP (x, 1), code); if (!TARGET_MACHO) putc (ASSEMBLER_DIALECT == ASM_INTEL ? ')' : ']', file); break; case UNSPEC: if (XVECLEN (x, 0) != 1) abort (); output_pic_addr_const (file, XVECEXP (x, 0, 0), code); switch (XINT (x, 1)) { case UNSPEC_GOT: fputs ("@GOT", file); break; case UNSPEC_GOTOFF: fputs ("@GOTOFF", file); break; case UNSPEC_GOTPCREL: fputs ("@GOTPCREL(%rip)", file); break; case UNSPEC_GOTTPOFF: /* FIXME: This might be @TPOFF in Sun ld too. */ fputs ("@GOTTPOFF", file); break; case UNSPEC_TPOFF: fputs ("@TPOFF", file); break; case UNSPEC_NTPOFF: if (TARGET_64BIT) fputs ("@TPOFF", file); else fputs ("@NTPOFF", file); break; case UNSPEC_DTPOFF: fputs ("@DTPOFF", file); break; case UNSPEC_GOTNTPOFF: if (TARGET_64BIT) fputs ("@GOTTPOFF(%rip)", file); else fputs ("@GOTNTPOFF", file); break; case UNSPEC_INDNTPOFF: fputs ("@INDNTPOFF", file); break; default: output_operand_lossage ("invalid UNSPEC as operand"); break; } break; default: output_operand_lossage ("invalid expression as operand"); } } /* This is called from dwarfout.c via ASM_OUTPUT_DWARF_ADDR_CONST. We need to handle our special PIC relocations. */ void i386_dwarf_output_addr_const (FILE *file, rtx x) { #ifdef ASM_QUAD fprintf (file, "%s", TARGET_64BIT ? ASM_QUAD : ASM_LONG); #else if (TARGET_64BIT) abort (); fprintf (file, "%s", ASM_LONG); #endif if (flag_pic) output_pic_addr_const (file, x, '\0'); else output_addr_const (file, x); fputc ('\n', file); } /* This is called from dwarf2out.c via ASM_OUTPUT_DWARF_DTPREL. We need to emit DTP-relative relocations. */ void i386_output_dwarf_dtprel (FILE *file, int size, rtx x) { fputs (ASM_LONG, file); output_addr_const (file, x); fputs ("@DTPOFF", file); switch (size) { case 4: break; case 8: fputs (", 0", file); break; default: abort (); } } /* In the name of slightly smaller debug output, and to cater to general assembler losage, recognize PIC+GOTOFF and turn it back into a direct symbol reference. */ static rtx ix86_delegitimize_address (rtx orig_x) { rtx x = orig_x, y; if (GET_CODE (x) == MEM) x = XEXP (x, 0); if (TARGET_64BIT) { if (GET_CODE (x) != CONST || GET_CODE (XEXP (x, 0)) != UNSPEC || XINT (XEXP (x, 0), 1) != UNSPEC_GOTPCREL || GET_CODE (orig_x) != MEM) return orig_x; return XVECEXP (XEXP (x, 0), 0, 0); } if (GET_CODE (x) != PLUS || GET_CODE (XEXP (x, 1)) != CONST) return orig_x; if (GET_CODE (XEXP (x, 0)) == REG && REGNO (XEXP (x, 0)) == PIC_OFFSET_TABLE_REGNUM) /* %ebx + GOT/GOTOFF */ y = NULL; else if (GET_CODE (XEXP (x, 0)) == PLUS) { /* %ebx + %reg * scale + GOT/GOTOFF */ y = XEXP (x, 0); if (GET_CODE (XEXP (y, 0)) == REG && REGNO (XEXP (y, 0)) == PIC_OFFSET_TABLE_REGNUM) y = XEXP (y, 1); else if (GET_CODE (XEXP (y, 1)) == REG && REGNO (XEXP (y, 1)) == PIC_OFFSET_TABLE_REGNUM) y = XEXP (y, 0); else return orig_x; if (GET_CODE (y) != REG && GET_CODE (y) != MULT && GET_CODE (y) != ASHIFT) return orig_x; } else return orig_x; x = XEXP (XEXP (x, 1), 0); if (GET_CODE (x) == UNSPEC && ((XINT (x, 1) == UNSPEC_GOT && GET_CODE (orig_x) == MEM) || (XINT (x, 1) == UNSPEC_GOTOFF && GET_CODE (orig_x) != MEM))) { if (y) return gen_rtx_PLUS (Pmode, y, XVECEXP (x, 0, 0)); return XVECEXP (x, 0, 0); } if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == UNSPEC && GET_CODE (XEXP (x, 1)) == CONST_INT && ((XINT (XEXP (x, 0), 1) == UNSPEC_GOT && GET_CODE (orig_x) == MEM) || (XINT (XEXP (x, 0), 1) == UNSPEC_GOTOFF && GET_CODE (orig_x) != MEM))) { x = gen_rtx_PLUS (VOIDmode, XVECEXP (XEXP (x, 0), 0, 0), XEXP (x, 1)); if (y) return gen_rtx_PLUS (Pmode, y, x); return x; } return orig_x; } static void put_condition_code (enum rtx_code code, enum machine_mode mode, int reverse, int fp, FILE *file) { const char *suffix; if (mode == CCFPmode || mode == CCFPUmode) { enum rtx_code second_code, bypass_code; ix86_fp_comparison_codes (code, &bypass_code, &code, &second_code); if (bypass_code != NIL || second_code != NIL) abort (); code = ix86_fp_compare_code_to_integer (code); mode = CCmode; } if (reverse) code = reverse_condition (code); switch (code) { case EQ: suffix = "e"; break; case NE: suffix = "ne"; break; case GT: if (mode != CCmode && mode != CCNOmode && mode != CCGCmode) abort (); suffix = "g"; break; case GTU: /* ??? Use "nbe" instead of "a" for fcmov losage on some assemblers. Those same assemblers have the same but opposite losage on cmov. */ if (mode != CCmode) abort (); suffix = fp ? "nbe" : "a"; break; case LT: if (mode == CCNOmode || mode == CCGOCmode) suffix = "s"; else if (mode == CCmode || mode == CCGCmode) suffix = "l"; else abort (); break; case LTU: if (mode != CCmode) abort (); suffix = "b"; break; case GE: if (mode == CCNOmode || mode == CCGOCmode) suffix = "ns"; else if (mode == CCmode || mode == CCGCmode) suffix = "ge"; else abort (); break; case GEU: /* ??? As above. */ if (mode != CCmode) abort (); suffix = fp ? "nb" : "ae"; break; case LE: if (mode != CCmode && mode != CCGCmode && mode != CCNOmode) abort (); suffix = "le"; break; case LEU: if (mode != CCmode) abort (); suffix = "be"; break; case UNORDERED: suffix = fp ? "u" : "p"; break; case ORDERED: suffix = fp ? "nu" : "np"; break; default: abort (); } fputs (suffix, file); } /* Print the name of register X to FILE based on its machine mode and number. If CODE is 'w', pretend the mode is HImode. If CODE is 'b', pretend the mode is QImode. If CODE is 'k', pretend the mode is SImode. If CODE is 'q', pretend the mode is DImode. If CODE is 'h', pretend the reg is the `high' byte register. If CODE is 'y', print "st(0)" instead of "st", if the reg is stack op. */ void print_reg (rtx x, int code, FILE *file) { if (REGNO (x) == ARG_POINTER_REGNUM || REGNO (x) == FRAME_POINTER_REGNUM || REGNO (x) == FLAGS_REG || REGNO (x) == FPSR_REG) abort (); if (ASSEMBLER_DIALECT == ASM_ATT || USER_LABEL_PREFIX[0] == 0) putc ('%', file); if (code == 'w' || MMX_REG_P (x)) code = 2; else if (code == 'b') code = 1; else if (code == 'k') code = 4; else if (code == 'q') code = 8; else if (code == 'y') code = 3; else if (code == 'h') code = 0; else code = GET_MODE_SIZE (GET_MODE (x)); /* Irritatingly, AMD extended registers use different naming convention from the normal registers. */ if (REX_INT_REG_P (x)) { if (!TARGET_64BIT) abort (); switch (code) { case 0: error ("extended registers have no high halves"); break; case 1: fprintf (file, "r%ib", REGNO (x) - FIRST_REX_INT_REG + 8); break; case 2: fprintf (file, "r%iw", REGNO (x) - FIRST_REX_INT_REG + 8); break; case 4: fprintf (file, "r%id", REGNO (x) - FIRST_REX_INT_REG + 8); break; case 8: fprintf (file, "r%i", REGNO (x) - FIRST_REX_INT_REG + 8); break; default: error ("unsupported operand size for extended register"); break; } return; } switch (code) { case 3: if (STACK_TOP_P (x)) { fputs ("st(0)", file); break; } /* FALLTHRU */ case 8: case 4: case 12: if (! ANY_FP_REG_P (x)) putc (code == 8 && TARGET_64BIT ? 'r' : 'e', file); /* FALLTHRU */ case 16: case 2: normal: fputs (hi_reg_name[REGNO (x)], file); break; case 1: if (REGNO (x) >= ARRAY_SIZE (qi_reg_name)) goto normal; fputs (qi_reg_name[REGNO (x)], file); break; case 0: if (REGNO (x) >= ARRAY_SIZE (qi_high_reg_name)) goto normal; fputs (qi_high_reg_name[REGNO (x)], file); break; default: abort (); } } /* Locate some local-dynamic symbol still in use by this function so that we can print its name in some tls_local_dynamic_base pattern. */ static const char * get_some_local_dynamic_name (void) { rtx insn; if (cfun->machine->some_ld_name) return cfun->machine->some_ld_name; for (insn = get_insns (); insn ; insn = NEXT_INSN (insn)) if (INSN_P (insn) && for_each_rtx (&PATTERN (insn), get_some_local_dynamic_name_1, 0)) return cfun->machine->some_ld_name; abort (); } static int get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED) { rtx x = *px; if (GET_CODE (x) == SYMBOL_REF && local_dynamic_symbolic_operand (x, Pmode)) { cfun->machine->some_ld_name = XSTR (x, 0); return 1; } return 0; } /* Meaning of CODE: L,W,B,Q,S,T -- print the opcode suffix for specified size of operand. C -- print opcode suffix for set/cmov insn. c -- like C, but print reversed condition F,f -- likewise, but for floating-point. O -- if HAVE_AS_IX86_CMOV_SUN_SYNTAX, expand to "w.", "l." or "q.", otherwise nothing R -- print the prefix for register names. z -- print the opcode suffix for the size of the current operand. * -- print a star (in certain assembler syntax) A -- print an absolute memory reference. w -- print the operand as if it's a "word" (HImode) even if it isn't. s -- print a shift double count, followed by the assemblers argument delimiter. b -- print the QImode name of the register for the indicated operand. %b0 would print %al if operands[0] is reg 0. w -- likewise, print the HImode name of the register. k -- likewise, print the SImode name of the register. q -- likewise, print the DImode 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. D -- print condition for SSE cmp instruction. P -- if PIC, print an @PLT suffix. X -- don't print any sort of PIC '@' suffix for a symbol. & -- print some in-use local-dynamic symbol name. */ void print_operand (FILE *file, rtx x, int code) { if (code) { switch (code) { case '*': if (ASSEMBLER_DIALECT == ASM_ATT) putc ('*', file); return; case '&': assemble_name (file, get_some_local_dynamic_name ()); return; case 'A': if (ASSEMBLER_DIALECT == ASM_ATT) putc ('*', file); else if (ASSEMBLER_DIALECT == ASM_INTEL) { /* Intel syntax. For absolute addresses, registers should not be surrounded by braces. */ if (GET_CODE (x) != REG) { putc ('[', file); PRINT_OPERAND (file, x, 0); putc (']', file); return; } } else abort (); PRINT_OPERAND (file, x, 0); return; case 'L': if (ASSEMBLER_DIALECT == ASM_ATT) putc ('l', file); return; case 'W': if (ASSEMBLER_DIALECT == ASM_ATT) putc ('w', file); return; case 'B': if (ASSEMBLER_DIALECT == ASM_ATT) putc ('b', file); return; case 'Q': if (ASSEMBLER_DIALECT == ASM_ATT) putc ('l', file); return; case 'S': if (ASSEMBLER_DIALECT == ASM_ATT) putc ('s', file); return; case 'T': if (ASSEMBLER_DIALECT == ASM_ATT) putc ('t', file); return; case 'z': /* 387 opcodes don't get size suffixes if the operands are registers. */ if (STACK_REG_P (x)) return; /* Likewise if using Intel opcodes. */ if (ASSEMBLER_DIALECT == ASM_INTEL) return; /* This is the size of op from size of operand. */ switch (GET_MODE_SIZE (GET_MODE (x))) { case 2: #ifdef HAVE_GAS_FILDS_FISTS putc ('s', file); #endif return; case 4: if (GET_MODE (x) == SFmode) { putc ('s', file); return; } else putc ('l', file); return; case 12: case 16: putc ('t', file); return; case 8: if (GET_MODE_CLASS (GET_MODE (x)) == MODE_INT) { #ifdef GAS_MNEMONICS putc ('q', file); #else putc ('l', file); putc ('l', file); #endif } else putc ('l', file); return; default: abort (); } case 'b': case 'w': case 'k': case 'q': case 'h': case 'y': case 'X': case 'P': break; case 's': if (GET_CODE (x) == CONST_INT || ! SHIFT_DOUBLE_OMITS_COUNT) { PRINT_OPERAND (file, x, 0); putc (',', file); } return; case 'D': /* Little bit of braindamage here. The SSE compare instructions does use completely different names for the comparisons that the fp conditional moves. */ switch (GET_CODE (x)) { case EQ: case UNEQ: fputs ("eq", file); break; case LT: case UNLT: fputs ("lt", file); break; case LE: case UNLE: fputs ("le", file); break; case UNORDERED: fputs ("unord", file); break; case NE: case LTGT: fputs ("neq", file); break; case UNGE: case GE: fputs ("nlt", file); break; case UNGT: case GT: fputs ("nle", file); break; case ORDERED: fputs ("ord", file); break; default: abort (); break; } return; case 'O': #ifdef HAVE_AS_IX86_CMOV_SUN_SYNTAX if (ASSEMBLER_DIALECT == ASM_ATT) { switch (GET_MODE (x)) { case HImode: putc ('w', file); break; case SImode: case SFmode: putc ('l', file); break; case DImode: case DFmode: putc ('q', file); break; default: abort (); } putc ('.', file); } #endif return; case 'C': put_condition_code (GET_CODE (x), GET_MODE (XEXP (x, 0)), 0, 0, file); return; case 'F': #ifdef HAVE_AS_IX86_CMOV_SUN_SYNTAX if (ASSEMBLER_DIALECT == ASM_ATT) putc ('.', file); #endif put_condition_code (GET_CODE (x), GET_MODE (XEXP (x, 0)), 0, 1, file); return; /* Like above, but reverse condition */ case 'c': /* Check to see if argument to %c is really a constant and not a condition code which needs to be reversed. */ if (GET_RTX_CLASS (GET_CODE (x)) != '<') { output_operand_lossage ("operand is neither a constant nor a condition code, invalid operand code 'c'"); return; } put_condition_code (GET_CODE (x), GET_MODE (XEXP (x, 0)), 1, 0, file); return; case 'f': #ifdef HAVE_AS_IX86_CMOV_SUN_SYNTAX if (ASSEMBLER_DIALECT == ASM_ATT) putc ('.', file); #endif put_condition_code (GET_CODE (x), GET_MODE (XEXP (x, 0)), 1, 1, file); return; case '+': { rtx x; if (!optimize || optimize_size || !TARGET_BRANCH_PREDICTION_HINTS) return; x = find_reg_note (current_output_insn, REG_BR_PROB, 0); if (x) { int pred_val = INTVAL (XEXP (x, 0)); if (pred_val < REG_BR_PROB_BASE * 45 / 100 || pred_val > REG_BR_PROB_BASE * 55 / 100) { int taken = pred_val > REG_BR_PROB_BASE / 2; int cputaken = final_forward_branch_p (current_output_insn) == 0; /* Emit hints only in the case default branch prediction heuristics would fail. */ if (taken != cputaken) { /* We use 3e (DS) prefix for taken branches and 2e (CS) prefix for not taken branches. */ if (taken) fputs ("ds ; ", file); else fputs ("cs ; ", file); } } } return; } default: output_operand_lossage ("invalid operand code `%c'", code); } } if (GET_CODE (x) == REG) print_reg (x, code, file); else if (GET_CODE (x) == MEM) { /* No `byte ptr' prefix for call instructions. */ if (ASSEMBLER_DIALECT == ASM_INTEL && code != 'X' && code != 'P') { const char * size; switch (GET_MODE_SIZE (GET_MODE (x))) { case 1: size = "BYTE"; break; case 2: size = "WORD"; break; case 4: size = "DWORD"; break; case 8: size = "QWORD"; break; case 12: size = "XWORD"; break; case 16: size = "XMMWORD"; break; default: abort (); } /* Check for explicit size override (codes 'b', 'w' and 'k') */ if (code == 'b') size = "BYTE"; else if (code == 'w') size = "WORD"; else if (code == 'k') size = "DWORD"; fputs (size, file); fputs (" PTR ", file); } x = XEXP (x, 0); /* Avoid (%rip) for call operands. */ if (CONSTANT_ADDRESS_P (x) && code == 'P' && GET_CODE (x) != CONST_INT) output_addr_const (file, x); else if (this_is_asm_operands && ! address_operand (x, VOIDmode)) output_operand_lossage ("invalid constraints for operand"); else output_address (x); } else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == SFmode) { REAL_VALUE_TYPE r; long l; REAL_VALUE_FROM_CONST_DOUBLE (r, x); REAL_VALUE_TO_TARGET_SINGLE (r, l); if (ASSEMBLER_DIALECT == ASM_ATT) putc ('$', file); fprintf (file, "0x%08lx", l); } /* These float cases don't actually occur as immediate operands. */ else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == DFmode) { char dstr[30]; real_to_decimal (dstr, CONST_DOUBLE_REAL_VALUE (x), sizeof (dstr), 0, 1); fprintf (file, "%s", dstr); } else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == XFmode) { char dstr[30]; real_to_decimal (dstr, CONST_DOUBLE_REAL_VALUE (x), sizeof (dstr), 0, 1); fprintf (file, "%s", dstr); } else { if (code != 'P') { if (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE) { if (ASSEMBLER_DIALECT == ASM_ATT) putc ('$', file); } else if (GET_CODE (x) == CONST || GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF) { if (ASSEMBLER_DIALECT == ASM_ATT) putc ('$', file); else fputs ("OFFSET FLAT:", file); } } if (GET_CODE (x) == CONST_INT) fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x)); else if (flag_pic) output_pic_addr_const (file, x, code); else output_addr_const (file, x); } } /* Print a memory operand whose address is ADDR. */ void print_operand_address (FILE *file, rtx addr) { struct ix86_address parts; rtx base, index, disp; int scale; if (! ix86_decompose_address (addr, &parts)) abort (); base = parts.base; index = parts.index; disp = parts.disp; scale = parts.scale; switch (parts.seg) { case SEG_DEFAULT: break; case SEG_FS: case SEG_GS: if (USER_LABEL_PREFIX[0] == 0) putc ('%', file); fputs ((parts.seg == SEG_FS ? "fs:" : "gs:"), file); break; default: abort (); } if (!base && !index) { /* Displacement only requires special attention. */ if (GET_CODE (disp) == CONST_INT) { if (ASSEMBLER_DIALECT == ASM_INTEL && parts.seg == SEG_DEFAULT) { if (USER_LABEL_PREFIX[0] == 0) putc ('%', file); fputs ("ds:", file); } fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (disp)); } else if (flag_pic) output_pic_addr_const (file, disp, 0); else output_addr_const (file, disp); /* Use one byte shorter RIP relative addressing for 64bit mode. */ if (TARGET_64BIT && ((GET_CODE (disp) == SYMBOL_REF && ! tls_symbolic_operand (disp, GET_MODE (disp))) || GET_CODE (disp) == LABEL_REF || (GET_CODE (disp) == CONST && GET_CODE (XEXP (disp, 0)) == PLUS && (GET_CODE (XEXP (XEXP (disp, 0), 0)) == SYMBOL_REF || GET_CODE (XEXP (XEXP (disp, 0), 0)) == LABEL_REF) && GET_CODE (XEXP (XEXP (disp, 0), 1)) == CONST_INT))) fputs ("(%rip)", file); } else { if (ASSEMBLER_DIALECT == ASM_ATT) { if (disp) { if (flag_pic) output_pic_addr_const (file, disp, 0); else if (GET_CODE (disp) == LABEL_REF) output_asm_label (disp); else output_addr_const (file, disp); } putc ('(', file); if (base) print_reg (base, 0, file); if (index) { putc (',', file); print_reg (index, 0, file); if (scale != 1) fprintf (file, ",%d", scale); } putc (')', file); } else { rtx offset = NULL_RTX; if (disp) { /* Pull out the offset of a symbol; print any symbol itself. */ if (GET_CODE (disp) == CONST && GET_CODE (XEXP (disp, 0)) == PLUS && GET_CODE (XEXP (XEXP (disp, 0), 1)) == CONST_INT) { offset = XEXP (XEXP (disp, 0), 1); disp = gen_rtx_CONST (VOIDmode, XEXP (XEXP (disp, 0), 0)); } if (flag_pic) output_pic_addr_const (file, disp, 0); else if (GET_CODE (disp) == LABEL_REF) output_asm_label (disp); else if (GET_CODE (disp) == CONST_INT) offset = disp; else output_addr_const (file, disp); } putc ('[', file); if (base) { print_reg (base, 0, file); if (offset) { if (INTVAL (offset) >= 0) putc ('+', file); fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (offset)); } } else if (offset) fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (offset)); else putc ('0', file); if (index) { putc ('+', file); print_reg (index, 0, file); if (scale != 1) fprintf (file, "*%d", scale); } putc (']', file); } } } bool output_addr_const_extra (FILE *file, rtx x) { rtx op; if (GET_CODE (x) != UNSPEC) return false; op = XVECEXP (x, 0, 0); switch (XINT (x, 1)) { case UNSPEC_GOTTPOFF: output_addr_const (file, op); /* FIXME: This might be @TPOFF in Sun ld. */ fputs ("@GOTTPOFF", file); break; case UNSPEC_TPOFF: output_addr_const (file, op); fputs ("@TPOFF", file); break; case UNSPEC_NTPOFF: output_addr_const (file, op); if (TARGET_64BIT) fputs ("@TPOFF", file); else fputs ("@NTPOFF", file); break; case UNSPEC_DTPOFF: output_addr_const (file, op); fputs ("@DTPOFF", file); break; case UNSPEC_GOTNTPOFF: output_addr_const (file, op); if (TARGET_64BIT) fputs ("@GOTTPOFF(%rip)", file); else fputs ("@GOTNTPOFF", file); break; case UNSPEC_INDNTPOFF: output_addr_const (file, op); fputs ("@INDNTPOFF", file); break; default: return false; } return true; } /* Split one or more DImode RTL references into pairs of SImode references. The RTL can be REG, offsettable MEM, integer constant, or CONST_DOUBLE. "operands" is a pointer to an array of DImode RTL to split and "num" is its length. lo_half and hi_half are output arrays that parallel "operands". */ void split_di (rtx operands[], int num, rtx lo_half[], rtx hi_half[]) { while (num--) { rtx op = operands[num]; /* simplify_subreg refuse to split volatile memory addresses, but we still have to handle it. */ if (GET_CODE (op) == MEM) { lo_half[num] = adjust_address (op, SImode, 0); hi_half[num] = adjust_address (op, SImode, 4); } else { lo_half[num] = simplify_gen_subreg (SImode, op, GET_MODE (op) == VOIDmode ? DImode : GET_MODE (op), 0); hi_half[num] = simplify_gen_subreg (SImode, op, GET_MODE (op) == VOIDmode ? DImode : GET_MODE (op), 4); } } } /* Split one or more TImode RTL references into pairs of SImode references. The RTL can be REG, offsettable MEM, integer constant, or CONST_DOUBLE. "operands" is a pointer to an array of DImode RTL to split and "num" is its length. lo_half and hi_half are output arrays that parallel "operands". */ void split_ti (rtx operands[], int num, rtx lo_half[], rtx hi_half[]) { while (num--) { rtx op = operands[num]; /* simplify_subreg refuse to split volatile memory addresses, but we still have to handle it. */ if (GET_CODE (op) == MEM) { lo_half[num] = adjust_address (op, DImode, 0); hi_half[num] = adjust_address (op, DImode, 8); } else { lo_half[num] = simplify_gen_subreg (DImode, op, TImode, 0); hi_half[num] = simplify_gen_subreg (DImode, op, TImode, 8); } } } /* Output code to perform a 387 binary operation in INSN, one of PLUS, MINUS, MULT or DIV. OPERANDS are the insn operands, where operands[3] is the expression of the binary operation. The output may either be emitted here, or returned to the caller, like all output_* functions. There is no guarantee that the operands are the same mode, as they might be within FLOAT or FLOAT_EXTEND expressions. */ #ifndef SYSV386_COMPAT /* Set to 1 for compatibility with brain-damaged assemblers. No-one wants to fix the assemblers because that causes incompatibility with gcc. No-one wants to fix gcc because that causes incompatibility with assemblers... You can use the option of -DSYSV386_COMPAT=0 if you recompile both gcc and gas this way. */ #define SYSV386_COMPAT 1 #endif const char * output_387_binary_op (rtx insn, rtx *operands) { static char buf[30]; const char *p; const char *ssep; int is_sse = SSE_REG_P (operands[0]) | SSE_REG_P (operands[1]) | SSE_REG_P (operands[2]); #ifdef ENABLE_CHECKING /* Even if we do not want to check the inputs, this documents input constraints. Which helps in understanding the following code. */ if (STACK_REG_P (operands[0]) && ((REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1]) && (STACK_REG_P (operands[2]) || GET_CODE (operands[2]) == MEM)) || (REG_P (operands[2]) && REGNO (operands[0]) == REGNO (operands[2]) && (STACK_REG_P (operands[1]) || GET_CODE (operands[1]) == MEM))) && (STACK_TOP_P (operands[1]) || STACK_TOP_P (operands[2]))) ; /* ok */ else if (!is_sse) abort (); #endif switch (GET_CODE (operands[3])) { case PLUS: if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT) p = "fiadd"; else p = "fadd"; ssep = "add"; break; case MINUS: if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT) p = "fisub"; else p = "fsub"; ssep = "sub"; break; case MULT: if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT) p = "fimul"; else p = "fmul"; ssep = "mul"; break; case DIV: if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT) p = "fidiv"; else p = "fdiv"; ssep = "div"; break; default: abort (); } if (is_sse) { strcpy (buf, ssep); if (GET_MODE (operands[0]) == SFmode) strcat (buf, "ss\t{%2, %0|%0, %2}"); else strcat (buf, "sd\t{%2, %0|%0, %2}"); return buf; } strcpy (buf, p); switch (GET_CODE (operands[3])) { case MULT: case PLUS: if (REG_P (operands[2]) && REGNO (operands[0]) == REGNO (operands[2])) { rtx temp = operands[2]; operands[2] = operands[1]; operands[1] = temp; } /* know operands[0] == operands[1]. */ if (GET_CODE (operands[2]) == MEM) { p = "%z2\t%2"; break; } if (find_regno_note (insn, REG_DEAD, REGNO (operands[2]))) { if (STACK_TOP_P (operands[0])) /* How is it that we are storing to a dead operand[2]? Well, presumably operands[1] is dead too. We can't store the result to st(0) as st(0) gets popped on this instruction. Instead store to operands[2] (which I think has to be st(1)). st(1) will be popped later. gcc <= 2.8.1 didn't have this check and generated assembly code that the Unixware assembler rejected. */ p = "p\t{%0, %2|%2, %0}"; /* st(1) = st(0) op st(1); pop */ else p = "p\t{%2, %0|%0, %2}"; /* st(r1) = st(r1) op st(0); pop */ break; } if (STACK_TOP_P (operands[0])) p = "\t{%y2, %0|%0, %y2}"; /* st(0) = st(0) op st(r2) */ else p = "\t{%2, %0|%0, %2}"; /* st(r1) = st(r1) op st(0) */ break; case MINUS: case DIV: if (GET_CODE (operands[1]) == MEM) { p = "r%z1\t%1"; break; } if (GET_CODE (operands[2]) == MEM) { p = "%z2\t%2"; break; } if (find_regno_note (insn, REG_DEAD, REGNO (operands[2]))) { #if SYSV386_COMPAT /* The SystemV/386 SVR3.2 assembler, and probably all AT&T derived assemblers, confusingly reverse the direction of the operation for fsub{r} and fdiv{r} when the destination register is not st(0). The Intel assembler doesn't have this brain damage. Read !SYSV386_COMPAT to figure out what the hardware really does. */ if (STACK_TOP_P (operands[0])) p = "{p\t%0, %2|rp\t%2, %0}"; else p = "{rp\t%2, %0|p\t%0, %2}"; #else if (STACK_TOP_P (operands[0])) /* As above for fmul/fadd, we can't store to st(0). */ p = "rp\t{%0, %2|%2, %0}"; /* st(1) = st(0) op st(1); pop */ else p = "p\t{%2, %0|%0, %2}"; /* st(r1) = st(r1) op st(0); pop */ #endif break; } if (find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) { #if SYSV386_COMPAT if (STACK_TOP_P (operands[0])) p = "{rp\t%0, %1|p\t%1, %0}"; else p = "{p\t%1, %0|rp\t%0, %1}"; #else if (STACK_TOP_P (operands[0])) p = "p\t{%0, %1|%1, %0}"; /* st(1) = st(1) op st(0); pop */ else p = "rp\t{%1, %0|%0, %1}"; /* st(r2) = st(0) op st(r2); pop */ #endif break; } if (STACK_TOP_P (operands[0])) { if (STACK_TOP_P (operands[1])) p = "\t{%y2, %0|%0, %y2}"; /* st(0) = st(0) op st(r2) */ else p = "r\t{%y1, %0|%0, %y1}"; /* st(0) = st(r1) op st(0) */ break; } else if (STACK_TOP_P (operands[1])) { #if SYSV386_COMPAT p = "{\t%1, %0|r\t%0, %1}"; #else p = "r\t{%1, %0|%0, %1}"; /* st(r2) = st(0) op st(r2) */ #endif } else { #if SYSV386_COMPAT p = "{r\t%2, %0|\t%0, %2}"; #else p = "\t{%2, %0|%0, %2}"; /* st(r1) = st(r1) op st(0) */ #endif } break; default: abort (); } strcat (buf, p); return buf; } /* Output code to initialize control word copies used by trunc?f?i patterns. NORMAL is set to current control word, while ROUND_DOWN is set to control word rounding downwards. */ void emit_i387_cw_initialization (rtx normal, rtx round_down) { rtx reg = gen_reg_rtx (HImode); emit_insn (gen_x86_fnstcw_1 (normal)); emit_move_insn (reg, normal); if (!TARGET_PARTIAL_REG_STALL && !optimize_size && !TARGET_64BIT) emit_insn (gen_movsi_insv_1 (reg, GEN_INT (0xc))); else emit_insn (gen_iorhi3 (reg, reg, GEN_INT (0xc00))); emit_move_insn (round_down, reg); } /* Output code for INSN to convert a float to a signed int. OPERANDS are the insn operands. The output may be [HSD]Imode and the input operand may be [SDX]Fmode. */ const char * output_fix_trunc (rtx insn, rtx *operands) { int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0; int dimode_p = GET_MODE (operands[0]) == DImode; /* Jump through a hoop or two for DImode, since the hardware has no non-popping instruction. We used to do this a different way, but that was somewhat fragile and broke with post-reload splitters. */ if (dimode_p && !stack_top_dies) output_asm_insn ("fld\t%y1", operands); if (!STACK_TOP_P (operands[1])) abort (); if (GET_CODE (operands[0]) != MEM) abort (); output_asm_insn ("fldcw\t%3", operands); if (stack_top_dies || dimode_p) output_asm_insn ("fistp%z0\t%0", operands); else output_asm_insn ("fist%z0\t%0", operands); output_asm_insn ("fldcw\t%2", operands); return ""; } /* Output code for INSN to compare OPERANDS. EFLAGS_P is 1 when fcomi should be used and 2 when fnstsw should be used. UNORDERED_P is true when fucom should be used. */ const char * output_fp_compare (rtx insn, rtx *operands, int eflags_p, int unordered_p) { int stack_top_dies; rtx cmp_op0 = operands[0]; rtx cmp_op1 = operands[1]; int is_sse = SSE_REG_P (operands[0]) | SSE_REG_P (operands[1]); if (eflags_p == 2) { cmp_op0 = cmp_op1; cmp_op1 = operands[2]; } if (is_sse) { if (GET_MODE (operands[0]) == SFmode) if (unordered_p) return "ucomiss\t{%1, %0|%0, %1}"; else return "comiss\t{%1, %0|%0, %1}"; else if (unordered_p) return "ucomisd\t{%1, %0|%0, %1}"; else return "comisd\t{%1, %0|%0, %1}"; } if (! STACK_TOP_P (cmp_op0)) abort (); stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0; if (STACK_REG_P (cmp_op1) && stack_top_dies && find_regno_note (insn, REG_DEAD, REGNO (cmp_op1)) && REGNO (cmp_op1) != FIRST_STACK_REG) { /* If both the top of the 387 stack dies, and the other operand is also a stack register that dies, then this must be a `fcompp' float compare */ if (eflags_p == 1) { /* There is no double popping fcomi variant. Fortunately, eflags is immune from the fstp's cc clobbering. */ if (unordered_p) output_asm_insn ("fucomip\t{%y1, %0|%0, %y1}", operands); else output_asm_insn ("fcomip\t{%y1, %0|%0, %y1}", operands); return "fstp\t%y0"; } else { if (eflags_p == 2) { if (unordered_p) return "fucompp\n\tfnstsw\t%0"; else return "fcompp\n\tfnstsw\t%0"; } else { if (unordered_p) return "fucompp"; else return "fcompp"; } } } else { /* Encoded here as eflags_p | intmode | unordered_p | stack_top_dies. */ static const char * const alt[24] = { "fcom%z1\t%y1", "fcomp%z1\t%y1", "fucom%z1\t%y1", "fucomp%z1\t%y1", "ficom%z1\t%y1", "ficomp%z1\t%y1", NULL, NULL, "fcomi\t{%y1, %0|%0, %y1}", "fcomip\t{%y1, %0|%0, %y1}", "fucomi\t{%y1, %0|%0, %y1}", "fucomip\t{%y1, %0|%0, %y1}", NULL, NULL, NULL, NULL, "fcom%z2\t%y2\n\tfnstsw\t%0", "fcomp%z2\t%y2\n\tfnstsw\t%0", "fucom%z2\t%y2\n\tfnstsw\t%0", "fucomp%z2\t%y2\n\tfnstsw\t%0", "ficom%z2\t%y2\n\tfnstsw\t%0", "ficomp%z2\t%y2\n\tfnstsw\t%0", NULL, NULL }; int mask; const char *ret; mask = eflags_p << 3; mask |= (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT) << 2; mask |= unordered_p << 1; mask |= stack_top_dies; if (mask >= 24) abort (); ret = alt[mask]; if (ret == NULL) abort (); return ret; } } void ix86_output_addr_vec_elt (FILE *file, int value) { const char *directive = ASM_LONG; if (TARGET_64BIT) { #ifdef ASM_QUAD directive = ASM_QUAD; #else abort (); #endif } fprintf (file, "%s%s%d\n", directive, LPREFIX, value); } void ix86_output_addr_diff_elt (FILE *file, int value, int rel) { if (TARGET_64BIT) fprintf (file, "%s%s%d-%s%d\n", ASM_LONG, LPREFIX, value, LPREFIX, rel); else if (HAVE_AS_GOTOFF_IN_DATA) fprintf (file, "%s%s%d@GOTOFF\n", ASM_LONG, LPREFIX, value); #if TARGET_MACHO else if (TARGET_MACHO) { fprintf (file, "%s%s%d-", ASM_LONG, LPREFIX, value); machopic_output_function_base_name (file); fprintf(file, "\n"); } #endif else asm_fprintf (file, "%s%U%s+[.-%s%d]\n", ASM_LONG, GOT_SYMBOL_NAME, LPREFIX, value); } /* Generate either "mov $0, reg" or "xor reg, reg", as appropriate for the target. */ void ix86_expand_clear (rtx dest) { rtx tmp; /* We play register width games, which are only valid after reload. */ if (!reload_completed) abort (); /* Avoid HImode and its attendant prefix byte. */ if (GET_MODE_SIZE (GET_MODE (dest)) < 4) dest = gen_rtx_REG (SImode, REGNO (dest)); tmp = gen_rtx_SET (VOIDmode, dest, const0_rtx); /* This predicate should match that for movsi_xor and movdi_xor_rex64. */ if (reload_completed && (!TARGET_USE_MOV0 || optimize_size)) { rtx clob = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, 17)); tmp = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, tmp, clob)); } emit_insn (tmp); } /* X is an unchanging MEM. If it is a constant pool reference, return the constant pool rtx, else NULL. */ static rtx maybe_get_pool_constant (rtx x) { x = ix86_delegitimize_address (XEXP (x, 0)); if (GET_CODE (x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (x)) return get_pool_constant (x); return NULL_RTX; } void ix86_expand_move (enum machine_mode mode, rtx operands[]) { int strict = (reload_in_progress || reload_completed); rtx op0, op1; enum tls_model model; op0 = operands[0]; op1 = operands[1]; model = tls_symbolic_operand (op1, Pmode); if (model) { op1 = legitimize_tls_address (op1, model, true); op1 = force_operand (op1, op0); if (op1 == op0) return; } if (flag_pic && mode == Pmode && symbolic_operand (op1, Pmode)) { #if TARGET_MACHO if (MACHOPIC_PURE) { rtx temp = ((reload_in_progress || ((op0 && GET_CODE (op0) == REG) && mode == Pmode)) ? op0 : gen_reg_rtx (Pmode)); op1 = machopic_indirect_data_reference (op1, temp); op1 = machopic_legitimize_pic_address (op1, mode, temp == op1 ? 0 : temp); } else if (MACHOPIC_INDIRECT) op1 = machopic_indirect_data_reference (op1, 0); if (op0 == op1) return; #else if (GET_CODE (op0) == MEM) op1 = force_reg (Pmode, op1); else { rtx temp = op0; if (GET_CODE (temp) != REG) temp = gen_reg_rtx (Pmode); temp = legitimize_pic_address (op1, temp); if (temp == op0) return; op1 = temp; } #endif /* TARGET_MACHO */ } else { if (GET_CODE (op0) == MEM && (PUSH_ROUNDING (GET_MODE_SIZE (mode)) != GET_MODE_SIZE (mode) || !push_operand (op0, mode)) && GET_CODE (op1) == MEM) op1 = force_reg (mode, op1); if (push_operand (op0, mode) && ! general_no_elim_operand (op1, mode)) op1 = copy_to_mode_reg (mode, op1); /* Force large constants in 64bit compilation into register to get them CSEed. */ if (TARGET_64BIT && mode == DImode && immediate_operand (op1, mode) && !x86_64_zero_extended_value (op1) && !register_operand (op0, mode) && optimize && !reload_completed && !reload_in_progress) op1 = copy_to_mode_reg (mode, op1); if (FLOAT_MODE_P (mode)) { /* If we are loading a floating point constant to a register, force the value to memory now, since we'll get better code out the back end. */ if (strict) ; else if (GET_CODE (op1) == CONST_DOUBLE) { op1 = validize_mem (force_const_mem (mode, op1)); if (!register_operand (op0, mode)) { rtx temp = gen_reg_rtx (mode); emit_insn (gen_rtx_SET (VOIDmode, temp, op1)); emit_move_insn (op0, temp); return; } } } } emit_insn (gen_rtx_SET (VOIDmode, op0, op1)); } void ix86_expand_vector_move (enum machine_mode mode, rtx operands[]) { /* Force constants other than zero into memory. We do not know how the instructions used to build constants modify the upper 64 bits of the register, once we have that information we may be able to handle some of them more efficiently. */ if ((reload_in_progress | reload_completed) == 0 && register_operand (operands[0], mode) && CONSTANT_P (operands[1]) && operands[1] != CONST0_RTX (mode)) operands[1] = validize_mem (force_const_mem (mode, operands[1])); /* Make operand1 a register if it isn't already. */ if (!no_new_pseudos && !register_operand (operands[0], mode) && !register_operand (operands[1], mode)) { rtx temp = force_reg (GET_MODE (operands[1]), operands[1]); emit_move_insn (operands[0], temp); return; } emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1])); } /* Attempt to expand a binary operator. Make the expansion closer to the actual machine, then just general_operand, which will allow 3 separate memory references (one output, two input) in a single insn. */ void ix86_expand_binary_operator (enum rtx_code code, enum machine_mode mode, rtx operands[]) { int matching_memory; rtx src1, src2, dst, op, clob; dst = operands[0]; src1 = operands[1]; src2 = operands[2]; /* Recognize = for commutative operators */ if (GET_RTX_CLASS (code) == 'c' && (rtx_equal_p (dst, src2) || immediate_operand (src1, mode))) { rtx temp = src1; src1 = src2; src2 = temp; } /* If the destination is memory, and we do not have matching source operands, do things in registers. */ matching_memory = 0; if (GET_CODE (dst) == MEM) { if (rtx_equal_p (dst, src1)) matching_memory = 1; else if (GET_RTX_CLASS (code) == 'c' && rtx_equal_p (dst, src2)) matching_memory = 2; else dst = gen_reg_rtx (mode); } /* Both source operands cannot be in memory. */ if (GET_CODE (src1) == MEM && GET_CODE (src2) == MEM) { if (matching_memory != 2) src2 = force_reg (mode, src2); else src1 = force_reg (mode, src1); } /* If the operation is not commutable, source 1 cannot be a constant or non-matching memory. */ if ((CONSTANT_P (src1) || (!matching_memory && GET_CODE (src1) == MEM)) && GET_RTX_CLASS (code) != 'c') src1 = force_reg (mode, src1); /* If optimizing, copy to regs to improve CSE */ if (optimize && ! no_new_pseudos) { if (GET_CODE (dst) == MEM) dst = gen_reg_rtx (mode); if (GET_CODE (src1) == MEM) src1 = force_reg (mode, src1); if (GET_CODE (src2) == MEM) src2 = force_reg (mode, src2); } /* Emit the instruction. */ op = gen_rtx_SET (VOIDmode, dst, gen_rtx_fmt_ee (code, mode, src1, src2)); if (reload_in_progress) { /* Reload doesn't know about the flags register, and doesn't know that it doesn't want to clobber it. We can only do this with PLUS. */ if (code != PLUS) abort (); emit_insn (op); } else { clob = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, FLAGS_REG)); emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, op, clob))); } /* Fix up the destination if needed. */ if (dst != operands[0]) emit_move_insn (operands[0], dst); } /* Return TRUE or FALSE depending on whether the binary operator meets the appropriate constraints. */ int ix86_binary_operator_ok (enum rtx_code code, enum machine_mode mode ATTRIBUTE_UNUSED, rtx operands[3]) { /* Both source operands cannot be in memory. */ if (GET_CODE (operands[1]) == MEM && GET_CODE (operands[2]) == MEM) return 0; /* If the operation is not commutable, source 1 cannot be a constant. */ if (CONSTANT_P (operands[1]) && GET_RTX_CLASS (code) != 'c') return 0; /* If the destination is memory, we must have a matching source operand. */ if (GET_CODE (operands[0]) == MEM && ! (rtx_equal_p (operands[0], operands[1]) || (GET_RTX_CLASS (code) == 'c' && rtx_equal_p (operands[0], operands[2])))) return 0; /* If the operation is not commutable and the source 1 is memory, we must have a matching destination. */ if (GET_CODE (operands[1]) == MEM && GET_RTX_CLASS (code) != 'c' && ! rtx_equal_p (operands[0], operands[1])) return 0; return 1; } /* Attempt to expand a unary operator. Make the expansion closer to the actual machine, then just general_operand, which will allow 2 separate memory references (one output, one input) in a single insn. */ void ix86_expand_unary_operator (enum rtx_code code, enum machine_mode mode, rtx operands[]) { int matching_memory; rtx src, dst, op, clob; dst = operands[0]; src = operands[1]; /* If the destination is memory, and we do not have matching source operands, do things in registers. */ matching_memory = 0; if (GET_CODE (dst) == MEM) { if (rtx_equal_p (dst, src)) matching_memory = 1; else dst = gen_reg_rtx (mode); } /* When source operand is memory, destination must match. */ if (!matching_memory && GET_CODE (src) == MEM) src = force_reg (mode, src); /* If optimizing, copy to regs to improve CSE */ if (optimize && ! no_new_pseudos) { if (GET_CODE (dst) == MEM) dst = gen_reg_rtx (mode); if (GET_CODE (src) == MEM) src = force_reg (mode, src); } /* Emit the instruction. */ op = gen_rtx_SET (VOIDmode, dst, gen_rtx_fmt_e (code, mode, src)); if (reload_in_progress || code == NOT) { /* Reload doesn't know about the flags register, and doesn't know that it doesn't want to clobber it. */ if (code != NOT) abort (); emit_insn (op); } else { clob = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, FLAGS_REG)); emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, op, clob))); } /* Fix up the destination if needed. */ if (dst != operands[0]) emit_move_insn (operands[0], dst); } /* Return TRUE or FALSE depending on whether the unary operator meets the appropriate constraints. */ int ix86_unary_operator_ok (enum rtx_code code ATTRIBUTE_UNUSED, enum machine_mode mode ATTRIBUTE_UNUSED, rtx operands[2] ATTRIBUTE_UNUSED) { /* If one of operands is memory, source and destination must match. */ if ((GET_CODE (operands[0]) == MEM || GET_CODE (operands[1]) == MEM) && ! rtx_equal_p (operands[0], operands[1])) return FALSE; return TRUE; } /* Return TRUE or FALSE depending on whether the first SET in INSN has source and destination with matching CC modes, and that the CC mode is at least as constrained as REQ_MODE. */ int ix86_match_ccmode (rtx insn, enum machine_mode req_mode) { rtx set; enum machine_mode set_mode; set = PATTERN (insn); if (GET_CODE (set) == PARALLEL) set = XVECEXP (set, 0, 0); if (GET_CODE (set) != SET) abort (); if (GET_CODE (SET_SRC (set)) != COMPARE) abort (); set_mode = GET_MODE (SET_DEST (set)); switch (set_mode) { case CCNOmode: if (req_mode != CCNOmode && (req_mode != CCmode || XEXP (SET_SRC (set), 1) != const0_rtx)) return 0; break; case CCmode: if (req_mode == CCGCmode) return 0; /* FALLTHRU */ case CCGCmode: if (req_mode == CCGOCmode || req_mode == CCNOmode) return 0; /* FALLTHRU */ case CCGOCmode: if (req_mode == CCZmode) return 0; /* FALLTHRU */ case CCZmode: break; default: abort (); } return (GET_MODE (SET_SRC (set)) == set_mode); } /* Generate insn patterns to do an integer compare of OPERANDS. */ static rtx ix86_expand_int_compare (enum rtx_code code, rtx op0, rtx op1) { enum machine_mode cmpmode; rtx tmp, flags; cmpmode = SELECT_CC_MODE (code, op0, op1); flags = gen_rtx_REG (cmpmode, FLAGS_REG); /* This is very simple, but making the interface the same as in the FP case makes the rest of the code easier. */ tmp = gen_rtx_COMPARE (cmpmode, op0, op1); emit_insn (gen_rtx_SET (VOIDmode, flags, tmp)); /* Return the test that should be put into the flags user, i.e. the bcc, scc, or cmov instruction. */ return gen_rtx_fmt_ee (code, VOIDmode, flags, const0_rtx); } /* Figure out whether to use ordered or unordered fp comparisons. Return the appropriate mode to use. */ enum machine_mode ix86_fp_compare_mode (enum rtx_code code ATTRIBUTE_UNUSED) { /* ??? In order to make all comparisons reversible, we do all comparisons non-trapping when compiling for IEEE. Once gcc is able to distinguish all forms trapping and nontrapping comparisons, we can make inequality comparisons trapping again, since it results in better code when using FCOM based compares. */ return TARGET_IEEE_FP ? CCFPUmode : CCFPmode; } enum machine_mode ix86_cc_mode (enum rtx_code code, rtx op0, rtx op1) { if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT) return ix86_fp_compare_mode (code); switch (code) { /* Only zero flag is needed. */ case EQ: /* ZF=0 */ case NE: /* ZF!=0 */ return CCZmode; /* Codes needing carry flag. */ case GEU: /* CF=0 */ case GTU: /* CF=0 & ZF=0 */ case LTU: /* CF=1 */ case LEU: /* CF=1 | ZF=1 */ return CCmode; /* Codes possibly doable only with sign flag when comparing against zero. */ case GE: /* SF=OF or SF=0 */ case LT: /* SF<>OF or SF=1 */ if (op1 == const0_rtx) return CCGOCmode; else /* For other cases Carry flag is not required. */ return CCGCmode; /* Codes doable only with sign flag when comparing against zero, but we miss jump instruction for it so we need to use relational tests against overflow that thus needs to be zero. */ case GT: /* ZF=0 & SF=OF */ case LE: /* ZF=1 | SF<>OF */ if (op1 == const0_rtx) return CCNOmode; else return CCGCmode; /* strcmp pattern do (use flags) and combine may ask us for proper mode. */ case USE: return CCmode; default: abort (); } } /* Return the fixed registers used for condition codes. */ static bool ix86_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2) { *p1 = FLAGS_REG; *p2 = FPSR_REG; return true; } /* If two condition code modes are compatible, return a condition code mode which is compatible with both. Otherwise, return VOIDmode. */ static enum machine_mode ix86_cc_modes_compatible (enum machine_mode m1, enum machine_mode m2) { if (m1 == m2) return m1; if (GET_MODE_CLASS (m1) != MODE_CC || GET_MODE_CLASS (m2) != MODE_CC) return VOIDmode; if ((m1 == CCGCmode && m2 == CCGOCmode) || (m1 == CCGOCmode && m2 == CCGCmode)) return CCGCmode; switch (m1) { default: abort (); case CCmode: case CCGCmode: case CCGOCmode: case CCNOmode: case CCZmode: switch (m2) { default: return VOIDmode; case CCmode: case CCGCmode: case CCGOCmode: case CCNOmode: case CCZmode: return CCmode; } case CCFPmode: case CCFPUmode: /* These are only compatible with themselves, which we already checked above. */ return VOIDmode; } } /* Return true if we should use an FCOMI instruction for this fp comparison. */ int ix86_use_fcomi_compare (enum rtx_code code ATTRIBUTE_UNUSED) { enum rtx_code swapped_code = swap_condition (code); return ((ix86_fp_comparison_cost (code) == ix86_fp_comparison_fcomi_cost (code)) || (ix86_fp_comparison_cost (swapped_code) == ix86_fp_comparison_fcomi_cost (swapped_code))); } /* Swap, force into registers, or otherwise massage the two operands to a fp comparison. The operands are updated in place; the new comparison code is returned. */ static enum rtx_code ix86_prepare_fp_compare_args (enum rtx_code code, rtx *pop0, rtx *pop1) { enum machine_mode fpcmp_mode = ix86_fp_compare_mode (code); rtx op0 = *pop0, op1 = *pop1; enum machine_mode op_mode = GET_MODE (op0); int is_sse = SSE_REG_P (op0) | SSE_REG_P (op1); /* All of the unordered compare instructions only work on registers. The same is true of the XFmode compare instructions. The same is true of the fcomi compare instructions. */ if (!is_sse && (fpcmp_mode == CCFPUmode || op_mode == XFmode || ix86_use_fcomi_compare (code))) { op0 = force_reg (op_mode, op0); op1 = force_reg (op_mode, op1); } else { /* %%% We only allow op1 in memory; op0 must be st(0). So swap things around if they appear profitable, otherwise force op0 into a register. */ if (standard_80387_constant_p (op0) == 0 || (GET_CODE (op0) == MEM && ! (standard_80387_constant_p (op1) == 0 || GET_CODE (op1) == MEM))) { rtx tmp; tmp = op0, op0 = op1, op1 = tmp; code = swap_condition (code); } if (GET_CODE (op0) != REG) op0 = force_reg (op_mode, op0); if (CONSTANT_P (op1)) { if (standard_80387_constant_p (op1)) op1 = force_reg (op_mode, op1); else op1 = validize_mem (force_const_mem (op_mode, op1)); } } /* Try to rearrange the comparison to make it cheaper. */ if (ix86_fp_comparison_cost (code) > ix86_fp_comparison_cost (swap_condition (code)) && (GET_CODE (op1) == REG || !no_new_pseudos)) { rtx tmp; tmp = op0, op0 = op1, op1 = tmp; code = swap_condition (code); if (GET_CODE (op0) != REG) op0 = force_reg (op_mode, op0); } *pop0 = op0; *pop1 = op1; return code; } /* Convert comparison codes we use to represent FP comparison to integer code that will result in proper branch. Return UNKNOWN if no such code is available. */ static enum rtx_code ix86_fp_compare_code_to_integer (enum rtx_code code) { switch (code) { case GT: return GTU; case GE: return GEU; case ORDERED: case UNORDERED: return code; break; case UNEQ: return EQ; break; case UNLT: return LTU; break; case UNLE: return LEU; break; case LTGT: return NE; break; default: return UNKNOWN; } } /* Split comparison code CODE into comparisons we can do using branch instructions. BYPASS_CODE is comparison code for branch that will branch around FIRST_CODE and SECOND_CODE. If some of branches is not required, set value to NIL. We never require more than two branches. */ static void ix86_fp_comparison_codes (enum rtx_code code, enum rtx_code *bypass_code, enum rtx_code *first_code, enum rtx_code *second_code) { *first_code = code; *bypass_code = NIL; *second_code = NIL; /* The fcomi comparison sets flags as follows: cmp ZF PF CF > 0 0 0 < 0 0 1 = 1 0 0 un 1 1 1 */ switch (code) { case GT: /* GTU - CF=0 & ZF=0 */ case GE: /* GEU - CF=0 */ case ORDERED: /* PF=0 */ case UNORDERED: /* PF=1 */ case UNEQ: /* EQ - ZF=1 */ case UNLT: /* LTU - CF=1 */ case UNLE: /* LEU - CF=1 | ZF=1 */ case LTGT: /* EQ - ZF=0 */ break; case LT: /* LTU - CF=1 - fails on unordered */ *first_code = UNLT; *bypass_code = UNORDERED; break; case LE: /* LEU - CF=1 | ZF=1 - fails on unordered */ *first_code = UNLE; *bypass_code = UNORDERED; break; case EQ: /* EQ - ZF=1 - fails on unordered */ *first_code = UNEQ; *bypass_code = UNORDERED; break; case NE: /* NE - ZF=0 - fails on unordered */ *first_code = LTGT; *second_code = UNORDERED; break; case UNGE: /* GEU - CF=0 - fails on unordered */ *first_code = GE; *second_code = UNORDERED; break; case UNGT: /* GTU - CF=0 & ZF=0 - fails on unordered */ *first_code = GT; *second_code = UNORDERED; break; default: abort (); } if (!TARGET_IEEE_FP) { *second_code = NIL; *bypass_code = NIL; } } /* Return cost of comparison done fcom + arithmetics operations on AX. All following functions do use number of instructions as a cost metrics. In future this should be tweaked to compute bytes for optimize_size and take into account performance of various instructions on various CPUs. */ static int ix86_fp_comparison_arithmetics_cost (enum rtx_code code) { if (!TARGET_IEEE_FP) return 4; /* The cost of code output by ix86_expand_fp_compare. */ switch (code) { case UNLE: case UNLT: case LTGT: case GT: case GE: case UNORDERED: case ORDERED: case UNEQ: return 4; break; case LT: case NE: case EQ: case UNGE: return 5; break; case LE: case UNGT: return 6; break; default: abort (); } } /* Return cost of comparison done using fcomi operation. See ix86_fp_comparison_arithmetics_cost for the metrics. */ static int ix86_fp_comparison_fcomi_cost (enum rtx_code code) { enum rtx_code bypass_code, first_code, second_code; /* Return arbitrarily high cost when instruction is not supported - this prevents gcc from using it. */ if (!TARGET_CMOVE) return 1024; ix86_fp_comparison_codes (code, &bypass_code, &first_code, &second_code); return (bypass_code != NIL || second_code != NIL) + 2; } /* Return cost of comparison done using sahf operation. See ix86_fp_comparison_arithmetics_cost for the metrics. */ static int ix86_fp_comparison_sahf_cost (enum rtx_code code) { enum rtx_code bypass_code, first_code, second_code; /* Return arbitrarily high cost when instruction is not preferred - this avoids gcc from using it. */ if (!TARGET_USE_SAHF && !optimize_size) return 1024; ix86_fp_comparison_codes (code, &bypass_code, &first_code, &second_code); return (bypass_code != NIL || second_code != NIL) + 3; } /* Compute cost of the comparison done using any method. See ix86_fp_comparison_arithmetics_cost for the metrics. */ static int ix86_fp_comparison_cost (enum rtx_code code) { int fcomi_cost, sahf_cost, arithmetics_cost = 1024; int min; fcomi_cost = ix86_fp_comparison_fcomi_cost (code); sahf_cost = ix86_fp_comparison_sahf_cost (code); min = arithmetics_cost = ix86_fp_comparison_arithmetics_cost (code); if (min > sahf_cost) min = sahf_cost; if (min > fcomi_cost) min = fcomi_cost; return min; } /* Generate insn patterns to do a floating point compare of OPERANDS. */ static rtx ix86_expand_fp_compare (enum rtx_code code, rtx op0, rtx op1, rtx scratch, rtx *second_test, rtx *bypass_test) { enum machine_mode fpcmp_mode, intcmp_mode; rtx tmp, tmp2; int cost = ix86_fp_comparison_cost (code); enum rtx_code bypass_code, first_code, second_code; fpcmp_mode = ix86_fp_compare_mode (code); code = ix86_prepare_fp_compare_args (code, &op0, &op1); if (second_test) *second_test = NULL_RTX; if (bypass_test) *bypass_test = NULL_RTX; ix86_fp_comparison_codes (code, &bypass_code, &first_code, &second_code); /* Do fcomi/sahf based test when profitable. */ if ((bypass_code == NIL || bypass_test) && (second_code == NIL || second_test) && ix86_fp_comparison_arithmetics_cost (code) > cost) { if (TARGET_CMOVE) { tmp = gen_rtx_COMPARE (fpcmp_mode, op0, op1); tmp = gen_rtx_SET (VOIDmode, gen_rtx_REG (fpcmp_mode, FLAGS_REG), tmp); emit_insn (tmp); } else { tmp = gen_rtx_COMPARE (fpcmp_mode, op0, op1); tmp2 = gen_rtx_UNSPEC (HImode, gen_rtvec (1, tmp), UNSPEC_FNSTSW); if (!scratch) scratch = gen_reg_rtx (HImode); emit_insn (gen_rtx_SET (VOIDmode, scratch, tmp2)); emit_insn (gen_x86_sahf_1 (scratch)); } /* The FP codes work out to act like unsigned. */ intcmp_mode = fpcmp_mode; code = first_code; if (bypass_code != NIL) *bypass_test = gen_rtx_fmt_ee (bypass_code, VOIDmode, gen_rtx_REG (intcmp_mode, FLAGS_REG), const0_rtx); if (second_code != NIL) *second_test = gen_rtx_fmt_ee (second_code, VOIDmode, gen_rtx_REG (intcmp_mode, FLAGS_REG), const0_rtx); } else { /* Sadness wrt reg-stack pops killing fpsr -- gotta get fnstsw first. */ tmp = gen_rtx_COMPARE (fpcmp_mode, op0, op1); tmp2 = gen_rtx_UNSPEC (HImode, gen_rtvec (1, tmp), UNSPEC_FNSTSW); if (!scratch) scratch = gen_reg_rtx (HImode); emit_insn (gen_rtx_SET (VOIDmode, scratch, tmp2)); /* In the unordered case, we have to check C2 for NaN's, which doesn't happen to work out to anything nice combination-wise. So do some bit twiddling on the value we've got in AH to come up with an appropriate set of condition codes. */ intcmp_mode = CCNOmode; switch (code) { case GT: case UNGT: if (code == GT || !TARGET_IEEE_FP) { emit_insn (gen_testqi_ext_ccno_0 (scratch, GEN_INT (0x45))); code = EQ; } else { emit_insn (gen_andqi_ext_0 (scratch, scratch, GEN_INT (0x45))); emit_insn (gen_addqi_ext_1 (scratch, scratch, constm1_rtx)); emit_insn (gen_cmpqi_ext_3 (scratch, GEN_INT (0x44))); intcmp_mode = CCmode; code = GEU; } break; case LT: case UNLT: if (code == LT && TARGET_IEEE_FP) { emit_insn (gen_andqi_ext_0 (scratch, scratch, GEN_INT (0x45))); emit_insn (gen_cmpqi_ext_3 (scratch, GEN_INT (0x01))); intcmp_mode = CCmode; code = EQ; } else { emit_insn (gen_testqi_ext_ccno_0 (scratch, GEN_INT (0x01))); code = NE; } break; case GE: case UNGE: if (code == GE || !TARGET_IEEE_FP) { emit_insn (gen_testqi_ext_ccno_0 (scratch, GEN_INT (0x05))); code = EQ; } else { emit_insn (gen_andqi_ext_0 (scratch, scratch, GEN_INT (0x45))); emit_insn (gen_xorqi_cc_ext_1 (scratch, scratch, GEN_INT (0x01))); code = NE; } break; case LE: case UNLE: if (code == LE && TARGET_IEEE_FP) { emit_insn (gen_andqi_ext_0 (scratch, scratch, GEN_INT (0x45))); emit_insn (gen_addqi_ext_1 (scratch, scratch, constm1_rtx)); emit_insn (gen_cmpqi_ext_3 (scratch, GEN_INT (0x40))); intcmp_mode = CCmode; code = LTU; } else { emit_insn (gen_testqi_ext_ccno_0 (scratch, GEN_INT (0x45))); code = NE; } break; case EQ: case UNEQ: if (code == EQ && TARGET_IEEE_FP) { emit_insn (gen_andqi_ext_0 (scratch, scratch, GEN_INT (0x45))); emit_insn (gen_cmpqi_ext_3 (scratch, GEN_INT (0x40))); intcmp_mode = CCmode; code = EQ; } else { emit_insn (gen_testqi_ext_ccno_0 (scratch, GEN_INT (0x40))); code = NE; break; } break; case NE: case LTGT: if (code == NE && TARGET_IEEE_FP) { emit_insn (gen_andqi_ext_0 (scratch, scratch, GEN_INT (0x45))); emit_insn (gen_xorqi_cc_ext_1 (scratch, scratch, GEN_INT (0x40))); code = NE; } else { emit_insn (gen_testqi_ext_ccno_0 (scratch, GEN_INT (0x40))); code = EQ; } break; case UNORDERED: emit_insn (gen_testqi_ext_ccno_0 (scratch, GEN_INT (0x04))); code = NE; break; case ORDERED: emit_insn (gen_testqi_ext_ccno_0 (scratch, GEN_INT (0x04))); code = EQ; break; default: abort (); } } /* Return the test that should be put into the flags user, i.e. the bcc, scc, or cmov instruction. */ return gen_rtx_fmt_ee (code, VOIDmode, gen_rtx_REG (intcmp_mode, FLAGS_REG), const0_rtx); } rtx ix86_expand_compare (enum rtx_code code, rtx *second_test, rtx *bypass_test) { rtx op0, op1, ret; op0 = ix86_compare_op0; op1 = ix86_compare_op1; if (second_test) *second_test = NULL_RTX; if (bypass_test) *bypass_test = NULL_RTX; if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT) ret = ix86_expand_fp_compare (code, op0, op1, NULL_RTX, second_test, bypass_test); else ret = ix86_expand_int_compare (code, op0, op1); return ret; } /* Return true if the CODE will result in nontrivial jump sequence. */ bool ix86_fp_jump_nontrivial_p (enum rtx_code code) { enum rtx_code bypass_code, first_code, second_code; if (!TARGET_CMOVE) return true; ix86_fp_comparison_codes (code, &bypass_code, &first_code, &second_code); return bypass_code != NIL || second_code != NIL; } void ix86_expand_branch (enum rtx_code code, rtx label) { rtx tmp; switch (GET_MODE (ix86_compare_op0)) { case QImode: case HImode: case SImode: simple: tmp = ix86_expand_compare (code, NULL, NULL); tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp, gen_rtx_LABEL_REF (VOIDmode, label), pc_rtx); emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp)); return; case SFmode: case DFmode: case XFmode: { rtvec vec; int use_fcomi; enum rtx_code bypass_code, first_code, second_code; code = ix86_prepare_fp_compare_args (code, &ix86_compare_op0, &ix86_compare_op1); ix86_fp_comparison_codes (code, &bypass_code, &first_code, &second_code); /* Check whether we will use the natural sequence with one jump. If so, we can expand jump early. Otherwise delay expansion by creating compound insn to not confuse optimizers. */ if (bypass_code == NIL && second_code == NIL && TARGET_CMOVE) { ix86_split_fp_branch (code, ix86_compare_op0, ix86_compare_op1, gen_rtx_LABEL_REF (VOIDmode, label), pc_rtx, NULL_RTX); } else { tmp = gen_rtx_fmt_ee (code, VOIDmode, ix86_compare_op0, ix86_compare_op1); tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp, gen_rtx_LABEL_REF (VOIDmode, label), pc_rtx); tmp = gen_rtx_SET (VOIDmode, pc_rtx, tmp); use_fcomi = ix86_use_fcomi_compare (code); vec = rtvec_alloc (3 + !use_fcomi); RTVEC_ELT (vec, 0) = tmp; RTVEC_ELT (vec, 1) = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCFPmode, 18)); RTVEC_ELT (vec, 2) = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCFPmode, 17)); if (! use_fcomi) RTVEC_ELT (vec, 3) = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (HImode)); emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, vec)); } return; } case DImode: if (TARGET_64BIT) goto simple; /* Expand DImode branch into multiple compare+branch. */ { rtx lo[2], hi[2], label2; enum rtx_code code1, code2, code3; if (CONSTANT_P (ix86_compare_op0) && ! CONSTANT_P (ix86_compare_op1)) { tmp = ix86_compare_op0; ix86_compare_op0 = ix86_compare_op1; ix86_compare_op1 = tmp; code = swap_condition (code); } split_di (&ix86_compare_op0, 1, lo+0, hi+0); split_di (&ix86_compare_op1, 1, lo+1, hi+1); /* When comparing for equality, we can use (hi0^hi1)|(lo0^lo1) to avoid two branches. This costs one extra insn, so disable when optimizing for size. */ if ((code == EQ || code == NE) && (!optimize_size || hi[1] == const0_rtx || lo[1] == const0_rtx)) { rtx xor0, xor1; xor1 = hi[0]; if (hi[1] != const0_rtx) xor1 = expand_binop (SImode, xor_optab, xor1, hi[1], NULL_RTX, 0, OPTAB_WIDEN); xor0 = lo[0]; if (lo[1] != const0_rtx) xor0 = expand_binop (SImode, xor_optab, xor0, lo[1], NULL_RTX, 0, OPTAB_WIDEN); tmp = expand_binop (SImode, ior_optab, xor1, xor0, NULL_RTX, 0, OPTAB_WIDEN); ix86_compare_op0 = tmp; ix86_compare_op1 = const0_rtx; ix86_expand_branch (code, label); return; } /* Otherwise, if we are doing less-than or greater-or-equal-than, op1 is a constant and the low word is zero, then we can just examine the high word. */ if (GET_CODE (hi[1]) == CONST_INT && lo[1] == const0_rtx) switch (code) { case LT: case LTU: case GE: case GEU: ix86_compare_op0 = hi[0]; ix86_compare_op1 = hi[1]; ix86_expand_branch (code, label); return; default: break; } /* Otherwise, we need two or three jumps. */ label2 = gen_label_rtx (); code1 = code; code2 = swap_condition (code); code3 = unsigned_condition (code); switch (code) { case LT: case GT: case LTU: case GTU: break; case LE: code1 = LT; code2 = GT; break; case GE: code1 = GT; code2 = LT; break; case LEU: code1 = LTU; code2 = GTU; break; case GEU: code1 = GTU; code2 = LTU; break; case EQ: code1 = NIL; code2 = NE; break; case NE: code2 = NIL; break; default: abort (); } /* * a < b => * if (hi(a) < hi(b)) goto true; * if (hi(a) > hi(b)) goto false; * if (lo(a) < lo(b)) goto true; * false: */ ix86_compare_op0 = hi[0]; ix86_compare_op1 = hi[1]; if (code1 != NIL) ix86_expand_branch (code1, label); if (code2 != NIL) ix86_expand_branch (code2, label2); ix86_compare_op0 = lo[0]; ix86_compare_op1 = lo[1]; ix86_expand_branch (code3, label); if (code2 != NIL) emit_label (label2); return; } default: abort (); } } /* Split branch based on floating point condition. */ void ix86_split_fp_branch (enum rtx_code code, rtx op1, rtx op2, rtx target1, rtx target2, rtx tmp) { rtx second, bypass; rtx label = NULL_RTX; rtx condition; int bypass_probability = -1, second_probability = -1, probability = -1; rtx i; if (target2 != pc_rtx) { rtx tmp = target2; code = reverse_condition_maybe_unordered (code); target2 = target1; target1 = tmp; } condition = ix86_expand_fp_compare (code, op1, op2, tmp, &second, &bypass); if (split_branch_probability >= 0) { /* Distribute the probabilities across the jumps. Assume the BYPASS and SECOND to be always test for UNORDERED. */ probability = split_branch_probability; /* Value of 1 is low enough to make no need for probability to be updated. Later we may run some experiments and see if unordered values are more frequent in practice. */ if (bypass) bypass_probability = 1; if (second) second_probability = 1; } if (bypass != NULL_RTX) { label = gen_label_rtx (); i = emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, gen_rtx_IF_THEN_ELSE (VOIDmode, bypass, gen_rtx_LABEL_REF (VOIDmode, label), pc_rtx))); if (bypass_probability >= 0) REG_NOTES (i) = gen_rtx_EXPR_LIST (REG_BR_PROB, GEN_INT (bypass_probability), REG_NOTES (i)); } i = emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, gen_rtx_IF_THEN_ELSE (VOIDmode, condition, target1, target2))); if (probability >= 0) REG_NOTES (i) = gen_rtx_EXPR_LIST (REG_BR_PROB, GEN_INT (probability), REG_NOTES (i)); if (second != NULL_RTX) { i = emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, gen_rtx_IF_THEN_ELSE (VOIDmode, second, target1, target2))); if (second_probability >= 0) REG_NOTES (i) = gen_rtx_EXPR_LIST (REG_BR_PROB, GEN_INT (second_probability), REG_NOTES (i)); } if (label != NULL_RTX) emit_label (label); } int ix86_expand_setcc (enum rtx_code code, rtx dest) { rtx ret, tmp, tmpreg, equiv; rtx second_test, bypass_test; if (GET_MODE (ix86_compare_op0) == DImode && !TARGET_64BIT) return 0; /* FAIL */ if (GET_MODE (dest) != QImode) abort (); ret = ix86_expand_compare (code, &second_test, &bypass_test); PUT_MODE (ret, QImode); tmp = dest; tmpreg = dest; emit_insn (gen_rtx_SET (VOIDmode, tmp, ret)); if (bypass_test || second_test) { rtx test = second_test; int bypass = 0; rtx tmp2 = gen_reg_rtx (QImode); if (bypass_test) { if (second_test) abort (); test = bypass_test; bypass = 1; PUT_CODE (test, reverse_condition_maybe_unordered (GET_CODE (test))); } PUT_MODE (test, QImode); emit_insn (gen_rtx_SET (VOIDmode, tmp2, test)); if (bypass) emit_insn (gen_andqi3 (tmp, tmpreg, tmp2)); else emit_insn (gen_iorqi3 (tmp, tmpreg, tmp2)); } /* Attach a REG_EQUAL note describing the comparison result. */ equiv = simplify_gen_relational (code, QImode, GET_MODE (ix86_compare_op0), ix86_compare_op0, ix86_compare_op1); set_unique_reg_note (get_last_insn (), REG_EQUAL, equiv); return 1; /* DONE */ } /* Expand comparison setting or clearing carry flag. Return true when successful and set pop for the operation. */ static bool ix86_expand_carry_flag_compare (enum rtx_code code, rtx op0, rtx op1, rtx *pop) { enum machine_mode mode = GET_MODE (op0) != VOIDmode ? GET_MODE (op0) : GET_MODE (op1); /* Do not handle DImode compares that go trought special path. Also we can't deal with FP compares yet. This is possible to add. */ if ((mode == DImode && !TARGET_64BIT)) return false; if (FLOAT_MODE_P (mode)) { rtx second_test = NULL, bypass_test = NULL; rtx compare_op, compare_seq; /* Shortcut: following common codes never translate into carry flag compares. */ if (code == EQ || code == NE || code == UNEQ || code == LTGT || code == ORDERED || code == UNORDERED) return false; /* These comparisons require zero flag; swap operands so they won't. */ if ((code == GT || code == UNLE || code == LE || code == UNGT) && !TARGET_IEEE_FP) { rtx tmp = op0; op0 = op1; op1 = tmp; code = swap_condition (code); } /* Try to expand the comparison and verify that we end up with carry flag based comparison. This is fails to be true only when we decide to expand comparison using arithmetic that is not too common scenario. */ start_sequence (); compare_op = ix86_expand_fp_compare (code, op0, op1, NULL_RTX, &second_test, &bypass_test); compare_seq = get_insns (); end_sequence (); if (second_test || bypass_test) return false; if (GET_MODE (XEXP (compare_op, 0)) == CCFPmode || GET_MODE (XEXP (compare_op, 0)) == CCFPUmode) code = ix86_fp_compare_code_to_integer (GET_CODE (compare_op)); else code = GET_CODE (compare_op); if (code != LTU && code != GEU) return false; emit_insn (compare_seq); *pop = compare_op; return true; } if (!INTEGRAL_MODE_P (mode)) return false; switch (code) { case LTU: case GEU: break; /* Convert a==0 into (unsigned)a<1. */ case EQ: case NE: if (op1 != const0_rtx) return false; op1 = const1_rtx; code = (code == EQ ? LTU : GEU); break; /* Convert a>b into b=b-1. */ case GTU: case LEU: if (GET_CODE (op1) == CONST_INT) { op1 = gen_int_mode (INTVAL (op1) + 1, GET_MODE (op0)); /* Bail out on overflow. We still can swap operands but that would force loading of the constant into register. */ if (op1 == const0_rtx || !x86_64_immediate_operand (op1, GET_MODE (op1))) return false; code = (code == GTU ? GEU : LTU); } else { rtx tmp = op1; op1 = op0; op0 = tmp; code = (code == GTU ? LTU : GEU); } break; /* Convert a>=0 into (unsigned)a<0x80000000. */ case LT: case GE: if (mode == DImode || op1 != const0_rtx) return false; op1 = gen_int_mode (1 << (GET_MODE_BITSIZE (mode) - 1), mode); code = (code == LT ? GEU : LTU); break; case LE: case GT: if (mode == DImode || op1 != constm1_rtx) return false; op1 = gen_int_mode (1 << (GET_MODE_BITSIZE (mode) - 1), mode); code = (code == LE ? GEU : LTU); break; default: return false; } /* Swapping operands may cause constant to appear as first operand. */ if (!nonimmediate_operand (op0, VOIDmode)) { if (no_new_pseudos) return false; op0 = force_reg (mode, op0); } ix86_compare_op0 = op0; ix86_compare_op1 = op1; *pop = ix86_expand_compare (code, NULL, NULL); if (GET_CODE (*pop) != LTU && GET_CODE (*pop) != GEU) abort (); return true; } int ix86_expand_int_movcc (rtx operands[]) { enum rtx_code code = GET_CODE (operands[1]), compare_code; rtx compare_seq, compare_op; rtx second_test, bypass_test; enum machine_mode mode = GET_MODE (operands[0]); bool sign_bit_compare_p = false;; start_sequence (); compare_op = ix86_expand_compare (code, &second_test, &bypass_test); compare_seq = get_insns (); end_sequence (); compare_code = GET_CODE (compare_op); if ((ix86_compare_op1 == const0_rtx && (code == GE || code == LT)) || (ix86_compare_op1 == constm1_rtx && (code == GT || code == LE))) sign_bit_compare_p = true; /* Don't attempt mode expansion here -- if we had to expand 5 or 6 HImode insns, we'd be swallowed in word prefix ops. */ if ((mode != HImode || TARGET_FAST_PREFIX) && (mode != DImode || TARGET_64BIT) && GET_CODE (operands[2]) == CONST_INT && GET_CODE (operands[3]) == CONST_INT) { rtx out = operands[0]; HOST_WIDE_INT ct = INTVAL (operands[2]); HOST_WIDE_INT cf = INTVAL (operands[3]); HOST_WIDE_INT diff; diff = ct - cf; /* Sign bit compares are better done using shifts than we do by using sbb. */ if (sign_bit_compare_p || ix86_expand_carry_flag_compare (code, ix86_compare_op0, ix86_compare_op1, &compare_op)) { /* Detect overlap between destination and compare sources. */ rtx tmp = out; if (!sign_bit_compare_p) { bool fpcmp = false; compare_code = GET_CODE (compare_op); if (GET_MODE (XEXP (compare_op, 0)) == CCFPmode || GET_MODE (XEXP (compare_op, 0)) == CCFPUmode) { fpcmp = true; compare_code = ix86_fp_compare_code_to_integer (compare_code); } /* To simplify rest of code, restrict to the GEU case. */ if (compare_code == LTU) { HOST_WIDE_INT tmp = ct; ct = cf; cf = tmp; compare_code = reverse_condition (compare_code); code = reverse_condition (code); } else { if (fpcmp) PUT_CODE (compare_op, reverse_condition_maybe_unordered (GET_CODE (compare_op))); else PUT_CODE (compare_op, reverse_condition (GET_CODE (compare_op))); } diff = ct - cf; if (reg_overlap_mentioned_p (out, ix86_compare_op0) || reg_overlap_mentioned_p (out, ix86_compare_op1)) tmp = gen_reg_rtx (mode); if (mode == DImode) emit_insn (gen_x86_movdicc_0_m1_rex64 (tmp, compare_op)); else emit_insn (gen_x86_movsicc_0_m1 (gen_lowpart (SImode, tmp), compare_op)); } else { if (code == GT || code == GE) code = reverse_condition (code); else { HOST_WIDE_INT tmp = ct; ct = cf; cf = tmp; diff = ct - cf; } tmp = emit_store_flag (tmp, code, ix86_compare_op0, ix86_compare_op1, VOIDmode, 0, -1); } if (diff == 1) { /* * cmpl op0,op1 * sbbl dest,dest * [addl dest, ct] * * Size 5 - 8. */ if (ct) tmp = expand_simple_binop (mode, PLUS, tmp, GEN_INT (ct), copy_rtx (tmp), 1, OPTAB_DIRECT); } else if (cf == -1) { /* * cmpl op0,op1 * sbbl dest,dest * orl $ct, dest * * Size 8. */ tmp = expand_simple_binop (mode, IOR, tmp, GEN_INT (ct), copy_rtx (tmp), 1, OPTAB_DIRECT); } else if (diff == -1 && ct) { /* * cmpl op0,op1 * sbbl dest,dest * notl dest * [addl dest, cf] * * Size 8 - 11. */ tmp = expand_simple_unop (mode, NOT, tmp, copy_rtx (tmp), 1); if (cf) tmp = expand_simple_binop (mode, PLUS, copy_rtx (tmp), GEN_INT (cf), copy_rtx (tmp), 1, OPTAB_DIRECT); } else { /* * cmpl op0,op1 * sbbl dest,dest * [notl dest] * andl cf - ct, dest * [addl dest, ct] * * Size 8 - 11. */ if (cf == 0) { cf = ct; ct = 0; tmp = expand_simple_unop (mode, NOT, tmp, copy_rtx (tmp), 1); } tmp = expand_simple_binop (mode, AND, copy_rtx (tmp), gen_int_mode (cf - ct, mode), copy_rtx (tmp), 1, OPTAB_DIRECT); if (ct) tmp = expand_simple_binop (mode, PLUS, copy_rtx (tmp), GEN_INT (ct), copy_rtx (tmp), 1, OPTAB_DIRECT); } if (!rtx_equal_p (tmp, out)) emit_move_insn (copy_rtx (out), copy_rtx (tmp)); return 1; /* DONE */ } if (diff < 0) { HOST_WIDE_INT tmp; tmp = ct, ct = cf, cf = tmp; diff = -diff; if (FLOAT_MODE_P (GET_MODE (ix86_compare_op0))) { /* We may be reversing unordered compare to normal compare, that is not valid in general (we may convert non-trapping condition to trapping one), however on i386 we currently emit all comparisons unordered. */ compare_code = reverse_condition_maybe_unordered (compare_code); code = reverse_condition_maybe_unordered (code); } else { compare_code = reverse_condition (compare_code); code = reverse_condition (code); } } compare_code = NIL; if (GET_MODE_CLASS (GET_MODE (ix86_compare_op0)) == MODE_INT && GET_CODE (ix86_compare_op1) == CONST_INT) { if (ix86_compare_op1 == const0_rtx && (code == LT || code == GE)) compare_code = code; else if (ix86_compare_op1 == constm1_rtx) { if (code == LE) compare_code = LT; else if (code == GT) compare_code = GE; } } /* Optimize dest = (op0 < 0) ? -1 : cf. */ if (compare_code != NIL && GET_MODE (ix86_compare_op0) == GET_MODE (out) && (cf == -1 || ct == -1)) { /* If lea code below could be used, only optimize if it results in a 2 insn sequence. */ if (! (diff == 1 || diff == 2 || diff == 4 || diff == 8 || diff == 3 || diff == 5 || diff == 9) || (compare_code == LT && ct == -1) || (compare_code == GE && cf == -1)) { /* * notl op1 (if necessary) * sarl $31, op1 * orl cf, op1 */ if (ct != -1) { cf = ct; ct = -1; code = reverse_condition (code); } out = emit_store_flag (out, code, ix86_compare_op0, ix86_compare_op1, VOIDmode, 0, -1); out = expand_simple_binop (mode, IOR, out, GEN_INT (cf), out, 1, OPTAB_DIRECT); if (out != operands[0]) emit_move_insn (operands[0], out); return 1; /* DONE */ } } if ((diff == 1 || diff == 2 || diff == 4 || diff == 8 || diff == 3 || diff == 5 || diff == 9) && ((mode != QImode && mode != HImode) || !TARGET_PARTIAL_REG_STALL) && (mode != DImode || x86_64_sign_extended_value (GEN_INT (cf)))) { /* * xorl dest,dest * cmpl op1,op2 * setcc dest * lea cf(dest*(ct-cf)),dest * * Size 14. * * This also catches the degenerate setcc-only case. */ rtx tmp; int nops; out = emit_store_flag (out, code, ix86_compare_op0, ix86_compare_op1, VOIDmode, 0, 1); nops = 0; /* On x86_64 the lea instruction operates on Pmode, so we need to get arithmetics done in proper mode to match. */ if (diff == 1) tmp = copy_rtx (out); else { rtx out1; out1 = copy_rtx (out); tmp = gen_rtx_MULT (mode, out1, GEN_INT (diff & ~1)); nops++; if (diff & 1) { tmp = gen_rtx_PLUS (mode, tmp, out1); nops++; } } if (cf != 0) { tmp = gen_rtx_PLUS (mode, tmp, GEN_INT (cf)); nops++; } if (!rtx_equal_p (tmp, out)) { if (nops == 1) out = force_operand (tmp, copy_rtx (out)); else emit_insn (gen_rtx_SET (VOIDmode, copy_rtx (out), copy_rtx (tmp))); } if (!rtx_equal_p (out, operands[0])) emit_move_insn (operands[0], copy_rtx (out)); return 1; /* DONE */ } /* * General case: Jumpful: * xorl dest,dest cmpl op1, op2 * cmpl op1, op2 movl ct, dest * setcc dest jcc 1f * decl dest movl cf, dest * andl (cf-ct),dest 1: * addl ct,dest * * Size 20. Size 14. * * This is reasonably steep, but branch mispredict costs are * high on modern cpus, so consider failing only if optimizing * for space. */ if ((!TARGET_CMOVE || (mode == QImode && TARGET_PARTIAL_REG_STALL)) && BRANCH_COST >= 2) { if (cf == 0) { cf = ct; ct = 0; if (FLOAT_MODE_P (GET_MODE (ix86_compare_op0))) /* We may be reversing unordered compare to normal compare, that is not valid in general (we may convert non-trapping condition to trapping one), however on i386 we currently emit all comparisons unordered. */ code = reverse_condition_maybe_unordered (code); else { code = reverse_condition (code); if (compare_code != NIL) compare_code = reverse_condition (compare_code); } } if (compare_code != NIL) { /* notl op1 (if needed) sarl $31, op1 andl (cf-ct), op1 addl ct, op1 For x < 0 (resp. x <= -1) there will be no notl, so if possible swap the constants to get rid of the complement. True/false will be -1/0 while code below (store flag followed by decrement) is 0/-1, so the constants need to be exchanged once more. */ if (compare_code == GE || !cf) { code = reverse_condition (code); compare_code = LT; } else { HOST_WIDE_INT tmp = cf; cf = ct; ct = tmp; } out = emit_store_flag (out, code, ix86_compare_op0, ix86_compare_op1, VOIDmode, 0, -1); } else { out = emit_store_flag (out, code, ix86_compare_op0, ix86_compare_op1, VOIDmode, 0, 1); out = expand_simple_binop (mode, PLUS, copy_rtx (out), constm1_rtx, copy_rtx (out), 1, OPTAB_DIRECT); } out = expand_simple_binop (mode, AND, copy_rtx (out), gen_int_mode (cf - ct, mode), copy_rtx (out), 1, OPTAB_DIRECT); if (ct) out = expand_simple_binop (mode, PLUS, copy_rtx (out), GEN_INT (ct), copy_rtx (out), 1, OPTAB_DIRECT); if (!rtx_equal_p (out, operands[0])) emit_move_insn (operands[0], copy_rtx (out)); return 1; /* DONE */ } } if (!TARGET_CMOVE || (mode == QImode && TARGET_PARTIAL_REG_STALL)) { /* Try a few things more with specific constants and a variable. */ optab op; rtx var, orig_out, out, tmp; if (BRANCH_COST <= 2) return 0; /* FAIL */ /* If one of the two operands is an interesting constant, load a constant with the above and mask it in with a logical operation. */ if (GET_CODE (operands[2]) == CONST_INT) { var = operands[3]; if (INTVAL (operands[2]) == 0 && operands[3] != constm1_rtx) operands[3] = constm1_rtx, op = and_optab; else if (INTVAL (operands[2]) == -1 && operands[3] != const0_rtx) operands[3] = const0_rtx, op = ior_optab; else return 0; /* FAIL */ } else if (GET_CODE (operands[3]) == CONST_INT) { var = operands[2]; if (INTVAL (operands[3]) == 0 && operands[2] != constm1_rtx) operands[2] = constm1_rtx, op = and_optab; else if (INTVAL (operands[3]) == -1 && operands[3] != const0_rtx) operands[2] = const0_rtx, op = ior_optab; else return 0; /* FAIL */ } else return 0; /* FAIL */ orig_out = operands[0]; tmp = gen_reg_rtx (mode); operands[0] = tmp; /* Recurse to get the constant loaded. */ if (ix86_expand_int_movcc (operands) == 0) return 0; /* FAIL */ /* Mask in the interesting variable. */ out = expand_binop (mode, op, var, tmp, orig_out, 0, OPTAB_WIDEN); if (!rtx_equal_p (out, orig_out)) emit_move_insn (copy_rtx (orig_out), copy_rtx (out)); return 1; /* DONE */ } /* * For comparison with above, * * movl cf,dest * movl ct,tmp * cmpl op1,op2 * cmovcc tmp,dest * * Size 15. */ if (! nonimmediate_operand (operands[2], mode)) operands[2] = force_reg (mode, operands[2]); if (! nonimmediate_operand (operands[3], mode)) operands[3] = force_reg (mode, operands[3]); if (bypass_test && reg_overlap_mentioned_p (operands[0], operands[3])) { rtx tmp = gen_reg_rtx (mode); emit_move_insn (tmp, operands[3]); operands[3] = tmp; } if (second_test && reg_overlap_mentioned_p (operands[0], operands[2])) { rtx tmp = gen_reg_rtx (mode); emit_move_insn (tmp, operands[2]); operands[2] = tmp; } if (! register_operand (operands[2], VOIDmode) && (mode == QImode || ! register_operand (operands[3], VOIDmode))) operands[2] = force_reg (mode, operands[2]); if (mode == QImode && ! register_operand (operands[3], VOIDmode)) operands[3] = force_reg (mode, operands[3]); emit_insn (compare_seq); emit_insn (gen_rtx_SET (VOIDmode, operands[0], gen_rtx_IF_THEN_ELSE (mode, compare_op, operands[2], operands[3]))); if (bypass_test) emit_insn (gen_rtx_SET (VOIDmode, copy_rtx (operands[0]), gen_rtx_IF_THEN_ELSE (mode, bypass_test, copy_rtx (operands[3]), copy_rtx (operands[0])))); if (second_test) emit_insn (gen_rtx_SET (VOIDmode, copy_rtx (operands[0]), gen_rtx_IF_THEN_ELSE (mode, second_test, copy_rtx (operands[2]), copy_rtx (operands[0])))); return 1; /* DONE */ } int ix86_expand_fp_movcc (rtx operands[]) { enum rtx_code code; rtx tmp; rtx compare_op, second_test, bypass_test; /* For SF/DFmode conditional moves based on comparisons in same mode, we may want to use SSE min/max instructions. */ if (((TARGET_SSE_MATH && GET_MODE (operands[0]) == SFmode) || (TARGET_SSE2 && TARGET_SSE_MATH && GET_MODE (operands[0]) == DFmode)) && GET_MODE (ix86_compare_op0) == GET_MODE (operands[0]) /* The SSE comparisons does not support the LTGT/UNEQ pair. */ && (!TARGET_IEEE_FP || (GET_CODE (operands[1]) != LTGT && GET_CODE (operands[1]) != UNEQ)) /* We may be called from the post-reload splitter. */ && (!REG_P (operands[0]) || SSE_REG_P (operands[0]) || REGNO (operands[0]) >= FIRST_PSEUDO_REGISTER)) { rtx op0 = ix86_compare_op0, op1 = ix86_compare_op1; code = GET_CODE (operands[1]); /* See if we have (cross) match between comparison operands and conditional move operands. */ if (rtx_equal_p (operands[2], op1)) { rtx tmp = op0; op0 = op1; op1 = tmp; code = reverse_condition_maybe_unordered (code); } if (rtx_equal_p (operands[2], op0) && rtx_equal_p (operands[3], op1)) { /* Check for min operation. */ if (code == LT || code == UNLE) { if (code == UNLE) { rtx tmp = op0; op0 = op1; op1 = tmp; } operands[0] = force_reg (GET_MODE (operands[0]), operands[0]); if (memory_operand (op0, VOIDmode)) op0 = force_reg (GET_MODE (operands[0]), op0); if (GET_MODE (operands[0]) == SFmode) emit_insn (gen_minsf3 (operands[0], op0, op1)); else emit_insn (gen_mindf3 (operands[0], op0, op1)); return 1; } /* Check for max operation. */ if (code == GT || code == UNGE) { if (code == UNGE) { rtx tmp = op0; op0 = op1; op1 = tmp; } operands[0] = force_reg (GET_MODE (operands[0]), operands[0]); if (memory_operand (op0, VOIDmode)) op0 = force_reg (GET_MODE (operands[0]), op0); if (GET_MODE (operands[0]) == SFmode) emit_insn (gen_maxsf3 (operands[0], op0, op1)); else emit_insn (gen_maxdf3 (operands[0], op0, op1)); return 1; } } /* Manage condition to be sse_comparison_operator. In case we are in non-ieee mode, try to canonicalize the destination operand to be first in the comparison - this helps reload to avoid extra moves. */ if (!sse_comparison_operator (operands[1], VOIDmode) || (rtx_equal_p (operands[0], ix86_compare_op1) && !TARGET_IEEE_FP)) { rtx tmp = ix86_compare_op0; ix86_compare_op0 = ix86_compare_op1; ix86_compare_op1 = tmp; operands[1] = gen_rtx_fmt_ee (swap_condition (GET_CODE (operands[1])), VOIDmode, ix86_compare_op0, ix86_compare_op1); } /* Similarly try to manage result to be first operand of conditional move. We also don't support the NE comparison on SSE, so try to avoid it. */ if ((rtx_equal_p (operands[0], operands[3]) && (!TARGET_IEEE_FP || GET_CODE (operands[1]) != EQ)) || (GET_CODE (operands[1]) == NE && TARGET_IEEE_FP)) { rtx tmp = operands[2]; operands[2] = operands[3]; operands[3] = tmp; operands[1] = gen_rtx_fmt_ee (reverse_condition_maybe_unordered (GET_CODE (operands[1])), VOIDmode, ix86_compare_op0, ix86_compare_op1); } if (GET_MODE (operands[0]) == SFmode) emit_insn (gen_sse_movsfcc (operands[0], operands[1], operands[2], operands[3], ix86_compare_op0, ix86_compare_op1)); else emit_insn (gen_sse_movdfcc (operands[0], operands[1], operands[2], operands[3], ix86_compare_op0, ix86_compare_op1)); return 1; } /* The floating point conditional move instructions don't directly support conditions resulting from a signed integer comparison. */ code = GET_CODE (operands[1]); compare_op = ix86_expand_compare (code, &second_test, &bypass_test); /* The floating point conditional move instructions don't directly support signed integer comparisons. */ if (!fcmov_comparison_operator (compare_op, VOIDmode)) { if (second_test != NULL || bypass_test != NULL) abort (); tmp = gen_reg_rtx (QImode); ix86_expand_setcc (code, tmp); code = NE; ix86_compare_op0 = tmp; ix86_compare_op1 = const0_rtx; compare_op = ix86_expand_compare (code, &second_test, &bypass_test); } if (bypass_test && reg_overlap_mentioned_p (operands[0], operands[3])) { tmp = gen_reg_rtx (GET_MODE (operands[0])); emit_move_insn (tmp, operands[3]); operands[3] = tmp; } if (second_test && reg_overlap_mentioned_p (operands[0], operands[2])) { tmp = gen_reg_rtx (GET_MODE (operands[0])); emit_move_insn (tmp, operands[2]); operands[2] = tmp; } emit_insn (gen_rtx_SET (VOIDmode, operands[0], gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]), compare_op, operands[2], operands[3]))); if (bypass_test) emit_insn (gen_rtx_SET (VOIDmode, operands[0], gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]), bypass_test, operands[3], operands[0]))); if (second_test) emit_insn (gen_rtx_SET (VOIDmode, operands[0], gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]), second_test, operands[2], operands[0]))); return 1; } /* Expand conditional increment or decrement using adb/sbb instructions. The default case using setcc followed by the conditional move can be done by generic code. */ int ix86_expand_int_addcc (rtx operands[]) { enum rtx_code code = GET_CODE (operands[1]); rtx compare_op; rtx val = const0_rtx; bool fpcmp = false; enum machine_mode mode = GET_MODE (operands[0]); if (operands[3] != const1_rtx && operands[3] != constm1_rtx) return 0; if (!ix86_expand_carry_flag_compare (code, ix86_compare_op0, ix86_compare_op1, &compare_op)) return 0; code = GET_CODE (compare_op); if (GET_MODE (XEXP (compare_op, 0)) == CCFPmode || GET_MODE (XEXP (compare_op, 0)) == CCFPUmode) { fpcmp = true; code = ix86_fp_compare_code_to_integer (code); } if (code != LTU) { val = constm1_rtx; if (fpcmp) PUT_CODE (compare_op, reverse_condition_maybe_unordered (GET_CODE (compare_op))); else PUT_CODE (compare_op, reverse_condition (GET_CODE (compare_op))); } PUT_MODE (compare_op, mode); /* Construct either adc or sbb insn. */ if ((code == LTU) == (operands[3] == constm1_rtx)) { switch (GET_MODE (operands[0])) { case QImode: emit_insn (gen_subqi3_carry (operands[0], operands[2], val, compare_op)); break; case HImode: emit_insn (gen_subhi3_carry (operands[0], operands[2], val, compare_op)); break; case SImode: emit_insn (gen_subsi3_carry (operands[0], operands[2], val, compare_op)); break; case DImode: emit_insn (gen_subdi3_carry_rex64 (operands[0], operands[2], val, compare_op)); break; default: abort (); } } else { switch (GET_MODE (operands[0])) { case QImode: emit_insn (gen_addqi3_carry (operands[0], operands[2], val, compare_op)); break; case HImode: emit_insn (gen_addhi3_carry (operands[0], operands[2], val, compare_op)); break; case SImode: emit_insn (gen_addsi3_carry (operands[0], operands[2], val, compare_op)); break; case DImode: emit_insn (gen_adddi3_carry_rex64 (operands[0], operands[2], val, compare_op)); break; default: abort (); } } return 1; /* DONE */ } /* Split operands 0 and 1 into SImode parts. Similar to split_di, but works for floating pointer parameters and nonoffsetable memories. For pushes, it returns just stack offsets; the values will be saved in the right order. Maximally three parts are generated. */ static int ix86_split_to_parts (rtx operand, rtx *parts, enum machine_mode mode) { int size; if (!TARGET_64BIT) size = mode==XFmode ? 3 : GET_MODE_SIZE (mode) / 4; else size = (GET_MODE_SIZE (mode) + 4) / 8; if (GET_CODE (operand) == REG && MMX_REGNO_P (REGNO (operand))) abort (); if (size < 2 || size > 3) abort (); /* Optimize constant pool reference to immediates. This is used by fp moves, that force all constants to memory to allow combining. */ if (GET_CODE (operand) == MEM && RTX_UNCHANGING_P (operand)) { rtx tmp = maybe_get_pool_constant (operand); if (tmp) operand = tmp; } if (GET_CODE (operand) == MEM && !offsettable_memref_p (operand)) { /* The only non-offsetable memories we handle are pushes. */ if (! push_operand (operand, VOIDmode)) abort (); operand = copy_rtx (operand); PUT_MODE (operand, Pmode); parts[0] = parts[1] = parts[2] = operand; } else if (!TARGET_64BIT) { if (mode == DImode) split_di (&operand, 1, &parts[0], &parts[1]); else { if (REG_P (operand)) { if (!reload_completed) abort (); parts[0] = gen_rtx_REG (SImode, REGNO (operand) + 0); parts[1] = gen_rtx_REG (SImode, REGNO (operand) + 1); if (size == 3) parts[2] = gen_rtx_REG (SImode, REGNO (operand) + 2); } else if (offsettable_memref_p (operand)) { operand = adjust_address (operand, SImode, 0); parts[0] = operand; parts[1] = adjust_address (operand, SImode, 4); if (size == 3) parts[2] = adjust_address (operand, SImode, 8); } else if (GET_CODE (operand) == CONST_DOUBLE) { REAL_VALUE_TYPE r; long l[4]; REAL_VALUE_FROM_CONST_DOUBLE (r, operand); switch (mode) { case XFmode: REAL_VALUE_TO_TARGET_LONG_DOUBLE (r, l); parts[2] = gen_int_mode (l[2], SImode); break; case DFmode: REAL_VALUE_TO_TARGET_DOUBLE (r, l); break; default: abort (); } parts[1] = gen_int_mode (l[1], SImode); parts[0] = gen_int_mode (l[0], SImode); } else abort (); } } else { if (mode == TImode) split_ti (&operand, 1, &parts[0], &parts[1]); if (mode == XFmode || mode == TFmode) { enum machine_mode upper_mode = mode==XFmode ? SImode : DImode; if (REG_P (operand)) { if (!reload_completed) abort (); parts[0] = gen_rtx_REG (DImode, REGNO (operand) + 0); parts[1] = gen_rtx_REG (upper_mode, REGNO (operand) + 1); } else if (offsettable_memref_p (operand)) { operand = adjust_address (operand, DImode, 0); parts[0] = operand; parts[1] = adjust_address (operand, upper_mode, 8); } else if (GET_CODE (operand) == CONST_DOUBLE) { REAL_VALUE_TYPE r; long l[4]; REAL_VALUE_FROM_CONST_DOUBLE (r, operand); real_to_target (l, &r, mode); /* Do not use shift by 32 to avoid warning on 32bit systems. */ if (HOST_BITS_PER_WIDE_INT >= 64) parts[0] = gen_int_mode ((l[0] & (((HOST_WIDE_INT) 2 << 31) - 1)) + ((((HOST_WIDE_INT) l[1]) << 31) << 1), DImode); else parts[0] = immed_double_const (l[0], l[1], DImode); if (upper_mode == SImode) parts[1] = gen_int_mode (l[2], SImode); else if (HOST_BITS_PER_WIDE_INT >= 64) parts[1] = gen_int_mode ((l[2] & (((HOST_WIDE_INT) 2 << 31) - 1)) + ((((HOST_WIDE_INT) l[3]) << 31) << 1), DImode); else parts[1] = immed_double_const (l[2], l[3], DImode); } else abort (); } } return size; } /* Emit insns to perform a move or push of DI, DF, and XF values. Return false when normal moves are needed; true when all required insns have been emitted. Operands 2-4 contain the input values int the correct order; operands 5-7 contain the output values. */ void ix86_split_long_move (rtx operands[]) { rtx part[2][3]; int nparts; int push = 0; int collisions = 0; enum machine_mode mode = GET_MODE (operands[0]); /* The DFmode expanders may ask us to move double. For 64bit target this is single move. By hiding the fact here we simplify i386.md splitters. */ if (GET_MODE_SIZE (GET_MODE (operands[0])) == 8 && TARGET_64BIT) { /* Optimize constant pool reference to immediates. This is used by fp moves, that force all constants to memory to allow combining. */ if (GET_CODE (operands[1]) == MEM && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (XEXP (operands[1], 0))) operands[1] = get_pool_constant (XEXP (operands[1], 0)); if (push_operand (operands[0], VOIDmode)) { operands[0] = copy_rtx (operands[0]); PUT_MODE (operands[0], Pmode); } else operands[0] = gen_lowpart (DImode, operands[0]); operands[1] = gen_lowpart (DImode, operands[1]); emit_move_insn (operands[0], operands[1]); return; } /* The only non-offsettable memory we handle is push. */ if (push_operand (operands[0], VOIDmode)) push = 1; else if (GET_CODE (operands[0]) == MEM && ! offsettable_memref_p (operands[0])) abort (); nparts = ix86_split_to_parts (operands[1], part[1], GET_MODE (operands[0])); ix86_split_to_parts (operands[0], part[0], GET_MODE (operands[0])); /* When emitting push, take care for source operands on the stack. */ if (push && GET_CODE (operands[1]) == MEM && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1])) { if (nparts == 3) part[1][1] = change_address (part[1][1], GET_MODE (part[1][1]), XEXP (part[1][2], 0)); part[1][0] = change_address (part[1][0], GET_MODE (part[1][0]), XEXP (part[1][1], 0)); } /* We need to do copy in the right order in case an address register of the source overlaps the destination. */ if (REG_P (part[0][0]) && GET_CODE (part[1][0]) == MEM) { if (reg_overlap_mentioned_p (part[0][0], XEXP (part[1][0], 0))) collisions++; if (reg_overlap_mentioned_p (part[0][1], XEXP (part[1][0], 0))) collisions++; if (nparts == 3 && reg_overlap_mentioned_p (part[0][2], XEXP (part[1][0], 0))) collisions++; /* Collision in the middle part can be handled by reordering. */ if (collisions == 1 && nparts == 3 && reg_overlap_mentioned_p (part[0][1], XEXP (part[1][0], 0))) { rtx tmp; tmp = part[0][1]; part[0][1] = part[0][2]; part[0][2] = tmp; tmp = part[1][1]; part[1][1] = part[1][2]; part[1][2] = tmp; } /* If there are more collisions, we can't handle it by reordering. Do an lea to the last part and use only one colliding move. */ else if (collisions > 1) { rtx base; collisions = 1; base = part[0][nparts - 1]; /* Handle the case when the last part isn't valid for lea. Happens in 64-bit mode storing the 12-byte XFmode. */ if (GET_MODE (base) != Pmode) base = gen_rtx_REG (Pmode, REGNO (base)); emit_insn (gen_rtx_SET (VOIDmode, base, XEXP (part[1][0], 0))); part[1][0] = replace_equiv_address (part[1][0], base); part[1][1] = replace_equiv_address (part[1][1], plus_constant (base, UNITS_PER_WORD)); if (nparts == 3) part[1][2] = replace_equiv_address (part[1][2], plus_constant (base, 8)); } } if (push) { if (!TARGET_64BIT) { if (nparts == 3) { if (TARGET_128BIT_LONG_DOUBLE && mode == XFmode) emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (-4))); emit_move_insn (part[0][2], part[1][2]); } } else { /* In 64bit mode we don't have 32bit push available. In case this is register, it is OK - we will just use larger counterpart. We also retype memory - these comes from attempt to avoid REX prefix on moving of second half of TFmode value. */ if (GET_MODE (part[1][1]) == SImode) { if (GET_CODE (part[1][1]) == MEM) part[1][1] = adjust_address (part[1][1], DImode, 0); else if (REG_P (part[1][1])) part[1][1] = gen_rtx_REG (DImode, REGNO (part[1][1])); else abort (); if (GET_MODE (part[1][0]) == SImode) part[1][0] = part[1][1]; } } emit_move_insn (part[0][1], part[1][1]); emit_move_insn (part[0][0], part[1][0]); return; } /* Choose correct order to not overwrite the source before it is copied. */ if ((REG_P (part[0][0]) && REG_P (part[1][1]) && (REGNO (part[0][0]) == REGNO (part[1][1]) || (nparts == 3 && REGNO (part[0][0]) == REGNO (part[1][2])))) || (collisions > 0 && reg_overlap_mentioned_p (part[0][0], XEXP (part[1][0], 0)))) { if (nparts == 3) { operands[2] = part[0][2]; operands[3] = part[0][1]; operands[4] = part[0][0]; operands[5] = part[1][2]; operands[6] = part[1][1]; operands[7] = part[1][0]; } else { operands[2] = part[0][1]; operands[3] = part[0][0]; operands[5] = part[1][1]; operands[6] = part[1][0]; } } else { if (nparts == 3) { operands[2] = part[0][0]; operands[3] = part[0][1]; operands[4] = part[0][2]; operands[5] = part[1][0]; operands[6] = part[1][1]; operands[7] = part[1][2]; } else { operands[2] = part[0][0]; operands[3] = part[0][1]; operands[5] = part[1][0]; operands[6] = part[1][1]; } } emit_move_insn (operands[2], operands[5]); emit_move_insn (operands[3], operands[6]); if (nparts == 3) emit_move_insn (operands[4], operands[7]); return; } void ix86_split_ashldi (rtx *operands, rtx scratch) { rtx low[2], high[2]; int count; if (GET_CODE (operands[2]) == CONST_INT) { split_di (operands, 2, low, high); count = INTVAL (operands[2]) & 63; if (count >= 32) { emit_move_insn (high[0], low[1]); emit_move_insn (low[0], const0_rtx); if (count > 32) emit_insn (gen_ashlsi3 (high[0], high[0], GEN_INT (count - 32))); } else { if (!rtx_equal_p (operands[0], operands[1])) emit_move_insn (operands[0], operands[1]); emit_insn (gen_x86_shld_1 (high[0], low[0], GEN_INT (count))); emit_insn (gen_ashlsi3 (low[0], low[0], GEN_INT (count))); } } else { if (!rtx_equal_p (operands[0], operands[1])) emit_move_insn (operands[0], operands[1]); split_di (operands, 1, low, high); emit_insn (gen_x86_shld_1 (high[0], low[0], operands[2])); emit_insn (gen_ashlsi3 (low[0], low[0], operands[2])); if (TARGET_CMOVE && (! no_new_pseudos || scratch)) { if (! no_new_pseudos) scratch = force_reg (SImode, const0_rtx); else emit_move_insn (scratch, const0_rtx); emit_insn (gen_x86_shift_adj_1 (high[0], low[0], operands[2], scratch)); } else emit_insn (gen_x86_shift_adj_2 (high[0], low[0], operands[2])); } } void ix86_split_ashrdi (rtx *operands, rtx scratch) { rtx low[2], high[2]; int count; if (GET_CODE (operands[2]) == CONST_INT) { split_di (operands, 2, low, high); count = INTVAL (operands[2]) & 63; if (count >= 32) { emit_move_insn (low[0], high[1]); if (! reload_completed) emit_insn (gen_ashrsi3 (high[0], low[0], GEN_INT (31))); else { emit_move_insn (high[0], low[0]); emit_insn (gen_ashrsi3 (high[0], high[0], GEN_INT (31))); } if (count > 32) emit_insn (gen_ashrsi3 (low[0], low[0], GEN_INT (count - 32))); } else { if (!rtx_equal_p (operands[0], operands[1])) emit_move_insn (operands[0], operands[1]); emit_insn (gen_x86_shrd_1 (low[0], high[0], GEN_INT (count))); emit_insn (gen_ashrsi3 (high[0], high[0], GEN_INT (count))); } } else { if (!rtx_equal_p (operands[0], operands[1])) emit_move_insn (operands[0], operands[1]); split_di (operands, 1, low, high); emit_insn (gen_x86_shrd_1 (low[0], high[0], operands[2])); emit_insn (gen_ashrsi3 (high[0], high[0], operands[2])); if (TARGET_CMOVE && (! no_new_pseudos || scratch)) { if (! no_new_pseudos) scratch = gen_reg_rtx (SImode); emit_move_insn (scratch, high[0]); emit_insn (gen_ashrsi3 (scratch, scratch, GEN_INT (31))); emit_insn (gen_x86_shift_adj_1 (low[0], high[0], operands[2], scratch)); } else emit_insn (gen_x86_shift_adj_3 (low[0], high[0], operands[2])); } } void ix86_split_lshrdi (rtx *operands, rtx scratch) { rtx low[2], high[2]; int count; if (GET_CODE (operands[2]) == CONST_INT) { split_di (operands, 2, low, high); count = INTVAL (operands[2]) & 63; if (count >= 32) { emit_move_insn (low[0], high[1]); emit_move_insn (high[0], const0_rtx); if (count > 32) emit_insn (gen_lshrsi3 (low[0], low[0], GEN_INT (count - 32))); } else { if (!rtx_equal_p (operands[0], operands[1])) emit_move_insn (operands[0], operands[1]); emit_insn (gen_x86_shrd_1 (low[0], high[0], GEN_INT (count))); emit_insn (gen_lshrsi3 (high[0], high[0], GEN_INT (count))); } } else { if (!rtx_equal_p (operands[0], operands[1])) emit_move_insn (operands[0], operands[1]); split_di (operands, 1, low, high); emit_insn (gen_x86_shrd_1 (low[0], high[0], operands[2])); emit_insn (gen_lshrsi3 (high[0], high[0], operands[2])); /* Heh. By reversing the arguments, we can reuse this pattern. */ if (TARGET_CMOVE && (! no_new_pseudos || scratch)) { if (! no_new_pseudos) scratch = force_reg (SImode, const0_rtx); else emit_move_insn (scratch, const0_rtx); emit_insn (gen_x86_shift_adj_1 (low[0], high[0], operands[2], scratch)); } else emit_insn (gen_x86_shift_adj_2 (low[0], high[0], operands[2])); } } /* Helper function for the string operations below. Dest VARIABLE whether it is aligned to VALUE bytes. If true, jump to the label. */ static rtx ix86_expand_aligntest (rtx variable, int value) { rtx label = gen_label_rtx (); rtx tmpcount = gen_reg_rtx (GET_MODE (variable)); if (GET_MODE (variable) == DImode) emit_insn (gen_anddi3 (tmpcount, variable, GEN_INT (value))); else emit_insn (gen_andsi3 (tmpcount, variable, GEN_INT (value))); emit_cmp_and_jump_insns (tmpcount, const0_rtx, EQ, 0, GET_MODE (variable), 1, label); return label; } /* Adjust COUNTER by the VALUE. */ static void ix86_adjust_counter (rtx countreg, HOST_WIDE_INT value) { if (GET_MODE (countreg) == DImode) emit_insn (gen_adddi3 (countreg, countreg, GEN_INT (-value))); else emit_insn (gen_addsi3 (countreg, countreg, GEN_INT (-value))); } /* Zero extend possibly SImode EXP to Pmode register. */ rtx ix86_zero_extend_to_Pmode (rtx exp) { rtx r; if (GET_MODE (exp) == VOIDmode) return force_reg (Pmode, exp); if (GET_MODE (exp) == Pmode) return copy_to_mode_reg (Pmode, exp); r = gen_reg_rtx (Pmode); emit_insn (gen_zero_extendsidi2 (r, exp)); return r; } /* Expand string move (memcpy) operation. Use i386 string operations when profitable. expand_clrstr contains similar code. */ int ix86_expand_movstr (rtx dst, rtx src, rtx count_exp, rtx align_exp) { rtx srcreg, destreg, countreg, srcexp, destexp; enum machine_mode counter_mode; HOST_WIDE_INT align = 0; unsigned HOST_WIDE_INT count = 0; if (GET_CODE (align_exp) == CONST_INT) align = INTVAL (align_exp); /* Can't use any of this if the user has appropriated esi or edi. */ if (global_regs[4] || global_regs[5]) return 0; /* This simple hack avoids all inlining code and simplifies code below. */ if (!TARGET_ALIGN_STRINGOPS) align = 64; if (GET_CODE (count_exp) == CONST_INT) { count = INTVAL (count_exp); if (!TARGET_INLINE_ALL_STRINGOPS && count > 64) return 0; } /* Figure out proper mode for counter. For 32bits it is always SImode, for 64bits use SImode when possible, otherwise DImode. Set count to number of bytes copied when known at compile time. */ if (!TARGET_64BIT || GET_MODE (count_exp) == SImode || x86_64_zero_extended_value (count_exp)) counter_mode = SImode; else counter_mode = DImode; if (counter_mode != SImode && counter_mode != DImode) abort (); destreg = copy_to_mode_reg (Pmode, XEXP (dst, 0)); if (destreg != XEXP (dst, 0)) dst = replace_equiv_address_nv (dst, destreg); srcreg = copy_to_mode_reg (Pmode, XEXP (src, 0)); if (srcreg != XEXP (src, 0)) src = replace_equiv_address_nv (src, srcreg); /* When optimizing for size emit simple rep ; movsb instruction for counts not divisible by 4. */ if ((!optimize || optimize_size) && (count == 0 || (count & 0x03))) { emit_insn (gen_cld ()); countreg = ix86_zero_extend_to_Pmode (count_exp); destexp = gen_rtx_PLUS (Pmode, destreg, countreg); srcexp = gen_rtx_PLUS (Pmode, srcreg, countreg); emit_insn (gen_rep_mov (destreg, dst, srcreg, src, countreg, destexp, srcexp)); } /* For constant aligned (or small unaligned) copies use rep movsl followed by code copying the rest. For PentiumPro ensure 8 byte alignment to allow rep movsl acceleration. */ else if (count != 0 && (align >= 8 || (!TARGET_PENTIUMPRO && !TARGET_64BIT && align >= 4) || optimize_size || count < (unsigned int) 64)) { unsigned HOST_WIDE_INT offset = 0; int size = TARGET_64BIT && !optimize_size ? 8 : 4; rtx srcmem, dstmem; emit_insn (gen_cld ()); if (count & ~(size - 1)) { countreg = copy_to_mode_reg (counter_mode, GEN_INT ((count >> (size == 4 ? 2 : 3)) & (TARGET_64BIT ? -1 : 0x3fffffff))); countreg = ix86_zero_extend_to_Pmode (countreg); destexp = gen_rtx_ASHIFT (Pmode, countreg, GEN_INT (size == 4 ? 2 : 3)); srcexp = gen_rtx_PLUS (Pmode, destexp, srcreg); destexp = gen_rtx_PLUS (Pmode, destexp, destreg); emit_insn (gen_rep_mov (destreg, dst, srcreg, src, countreg, destexp, srcexp)); offset = count & ~(size - 1); } if (size == 8 && (count & 0x04)) { srcmem = adjust_automodify_address_nv (src, SImode, srcreg, offset); dstmem = adjust_automodify_address_nv (dst, SImode, destreg, offset); emit_insn (gen_strmov (destreg, dstmem, srcreg, srcmem)); offset += 4; } if (count & 0x02) { srcmem = adjust_automodify_address_nv (src, HImode, srcreg, offset); dstmem = adjust_automodify_address_nv (dst, HImode, destreg, offset); emit_insn (gen_strmov (destreg, dstmem, srcreg, srcmem)); offset += 2; } if (count & 0x01) { srcmem = adjust_automodify_address_nv (src, QImode, srcreg, offset); dstmem = adjust_automodify_address_nv (dst, QImode, destreg, offset); emit_insn (gen_strmov (destreg, dstmem, srcreg, srcmem)); } } /* The generic code based on the glibc implementation: - align destination to 4 bytes (8 byte alignment is used for PentiumPro allowing accelerated copying there) - copy the data using rep movsl - copy the rest. */ else { rtx countreg2; rtx label = NULL; rtx srcmem, dstmem; int desired_alignment = (TARGET_PENTIUMPRO && (count == 0 || count >= (unsigned int) 260) ? 8 : UNITS_PER_WORD); /* Get rid of MEM_OFFSETs, they won't be accurate. */ dst = change_address (dst, BLKmode, destreg); src = change_address (src, BLKmode, srcreg); /* In case we don't know anything about the alignment, default to library version, since it is usually equally fast and result in shorter code. Also emit call when we know that the count is large and call overhead will not be important. */ if (!TARGET_INLINE_ALL_STRINGOPS && (align < UNITS_PER_WORD || !TARGET_REP_MOVL_OPTIMAL)) return 0; if (TARGET_SINGLE_STRINGOP) emit_insn (gen_cld ()); countreg2 = gen_reg_rtx (Pmode); countreg = copy_to_mode_reg (counter_mode, count_exp); /* We don't use loops to align destination and to copy parts smaller than 4 bytes, because gcc is able to optimize such code better (in the case the destination or the count really is aligned, gcc is often able to predict the branches) and also it is friendlier to the hardware branch prediction. Using loops is beneficial for generic case, because we can handle small counts using the loops. Many CPUs (such as Athlon) have large REP prefix setup costs. This is quite costly. Maybe we can revisit this decision later or add some customizability to this code. */ if (count == 0 && align < desired_alignment) { label = gen_label_rtx (); emit_cmp_and_jump_insns (countreg, GEN_INT (desired_alignment - 1), LEU, 0, counter_mode, 1, label); } if (align <= 1) { rtx label = ix86_expand_aligntest (destreg, 1); srcmem = change_address (src, QImode, srcreg); dstmem = change_address (dst, QImode, destreg); emit_insn (gen_strmov (destreg, dstmem, srcreg, srcmem)); ix86_adjust_counter (countreg, 1); emit_label (label); LABEL_NUSES (label) = 1; } if (align <= 2) { rtx label = ix86_expand_aligntest (destreg, 2); srcmem = change_address (src, HImode, srcreg); dstmem = change_address (dst, HImode, destreg); emit_insn (gen_strmov (destreg, dstmem, srcreg, srcmem)); ix86_adjust_counter (countreg, 2); emit_label (label); LABEL_NUSES (label) = 1; } if (align <= 4 && desired_alignment > 4) { rtx label = ix86_expand_aligntest (destreg, 4); srcmem = change_address (src, SImode, srcreg); dstmem = change_address (dst, SImode, destreg); emit_insn (gen_strmov (destreg, dstmem, srcreg, srcmem)); ix86_adjust_counter (countreg, 4); emit_label (label); LABEL_NUSES (label) = 1; } if (label && desired_alignment > 4 && !TARGET_64BIT) { emit_label (label); LABEL_NUSES (label) = 1; label = NULL_RTX; } if (!TARGET_SINGLE_STRINGOP) emit_insn (gen_cld ()); if (TARGET_64BIT) { emit_insn (gen_lshrdi3 (countreg2, ix86_zero_extend_to_Pmode (countreg), GEN_INT (3))); destexp = gen_rtx_ASHIFT (Pmode, countreg2, GEN_INT (3)); } else { emit_insn (gen_lshrsi3 (countreg2, countreg, const2_rtx)); destexp = gen_rtx_ASHIFT (Pmode, countreg2, const2_rtx); } srcexp = gen_rtx_PLUS (Pmode, destexp, srcreg); destexp = gen_rtx_PLUS (Pmode, destexp, destreg); emit_insn (gen_rep_mov (destreg, dst, srcreg, src, countreg2, destexp, srcexp)); if (label) { emit_label (label); LABEL_NUSES (label) = 1; } if (TARGET_64BIT && align > 4 && count != 0 && (count & 4)) { srcmem = change_address (src, SImode, srcreg); dstmem = change_address (dst, SImode, destreg); emit_insn (gen_strmov (destreg, dstmem, srcreg, srcmem)); } if ((align <= 4 || count == 0) && TARGET_64BIT) { rtx label = ix86_expand_aligntest (countreg, 4); srcmem = change_address (src, SImode, srcreg); dstmem = change_address (dst, SImode, destreg); emit_insn (gen_strmov (destreg, dstmem, srcreg, srcmem)); emit_label (label); LABEL_NUSES (label) = 1; } if (align > 2 && count != 0 && (count & 2)) { srcmem = change_address (src, HImode, srcreg); dstmem = change_address (dst, HImode, destreg); emit_insn (gen_strmov (destreg, dstmem, srcreg, srcmem)); } if (align <= 2 || count == 0) { rtx label = ix86_expand_aligntest (countreg, 2); srcmem = change_address (src, HImode, srcreg); dstmem = change_address (dst, HImode, destreg); emit_insn (gen_strmov (destreg, dstmem, srcreg, srcmem)); emit_label (label); LABEL_NUSES (label) = 1; } if (align > 1 && count != 0 && (count & 1)) { srcmem = change_address (src, QImode, srcreg); dstmem = change_address (dst, QImode, destreg); emit_insn (gen_strmov (destreg, dstmem, srcreg, srcmem)); } if (align <= 1 || count == 0) { rtx label = ix86_expand_aligntest (countreg, 1); srcmem = change_address (src, QImode, srcreg); dstmem = change_address (dst, QImode, destreg); emit_insn (gen_strmov (destreg, dstmem, srcreg, srcmem)); emit_label (label); LABEL_NUSES (label) = 1; } } return 1; } /* Expand string clear operation (bzero). Use i386 string operations when profitable. expand_movstr contains similar code. */ int ix86_expand_clrstr (rtx dst, rtx count_exp, rtx align_exp) { rtx destreg, zeroreg, countreg, destexp; enum machine_mode counter_mode; HOST_WIDE_INT align = 0; unsigned HOST_WIDE_INT count = 0; if (GET_CODE (align_exp) == CONST_INT) align = INTVAL (align_exp); /* Can't use any of this if the user has appropriated esi. */ if (global_regs[4]) return 0; /* This simple hack avoids all inlining code and simplifies code below. */ if (!TARGET_ALIGN_STRINGOPS) align = 32; if (GET_CODE (count_exp) == CONST_INT) { count = INTVAL (count_exp); if (!TARGET_INLINE_ALL_STRINGOPS && count > 64) return 0; } /* Figure out proper mode for counter. For 32bits it is always SImode, for 64bits use SImode when possible, otherwise DImode. Set count to number of bytes copied when known at compile time. */ if (!TARGET_64BIT || GET_MODE (count_exp) == SImode || x86_64_zero_extended_value (count_exp)) counter_mode = SImode; else counter_mode = DImode; destreg = copy_to_mode_reg (Pmode, XEXP (dst, 0)); if (destreg != XEXP (dst, 0)) dst = replace_equiv_address_nv (dst, destreg); emit_insn (gen_cld ()); /* When optimizing for size emit simple rep ; movsb instruction for counts not divisible by 4. */ if ((!optimize || optimize_size) && (count == 0 || (count & 0x03))) { countreg = ix86_zero_extend_to_Pmode (count_exp); zeroreg = copy_to_mode_reg (QImode, const0_rtx); destexp = gen_rtx_PLUS (Pmode, destreg, countreg); emit_insn (gen_rep_stos (destreg, countreg, dst, zeroreg, destexp)); } else if (count != 0 && (align >= 8 || (!TARGET_PENTIUMPRO && !TARGET_64BIT && align >= 4) || optimize_size || count < (unsigned int) 64)) { int size = TARGET_64BIT && !optimize_size ? 8 : 4; unsigned HOST_WIDE_INT offset = 0; zeroreg = copy_to_mode_reg (size == 4 ? SImode : DImode, const0_rtx); if (count & ~(size - 1)) { countreg = copy_to_mode_reg (counter_mode, GEN_INT ((count >> (size == 4 ? 2 : 3)) & (TARGET_64BIT ? -1 : 0x3fffffff))); countreg = ix86_zero_extend_to_Pmode (countreg); destexp = gen_rtx_ASHIFT (Pmode, countreg, GEN_INT (size == 4 ? 2 : 3)); destexp = gen_rtx_PLUS (Pmode, destexp, destreg); emit_insn (gen_rep_stos (destreg, countreg, dst, zeroreg, destexp)); offset = count & ~(size - 1); } if (size == 8 && (count & 0x04)) { rtx mem = adjust_automodify_address_nv (dst, SImode, destreg, offset); emit_insn (gen_strset (destreg, mem, gen_rtx_SUBREG (SImode, zeroreg, 0))); offset += 4; } if (count & 0x02) { rtx mem = adjust_automodify_address_nv (dst, HImode, destreg, offset); emit_insn (gen_strset (destreg, mem, gen_rtx_SUBREG (HImode, zeroreg, 0))); offset += 2; } if (count & 0x01) { rtx mem = adjust_automodify_address_nv (dst, QImode, destreg, offset); emit_insn (gen_strset (destreg, mem, gen_rtx_SUBREG (QImode, zeroreg, 0))); } } else { rtx countreg2; rtx label = NULL; /* Compute desired alignment of the string operation. */ int desired_alignment = (TARGET_PENTIUMPRO && (count == 0 || count >= (unsigned int) 260) ? 8 : UNITS_PER_WORD); /* In case we don't know anything about the alignment, default to library version, since it is usually equally fast and result in shorter code. Also emit call when we know that the count is large and call overhead will not be important. */ if (!TARGET_INLINE_ALL_STRINGOPS && (align < UNITS_PER_WORD || !TARGET_REP_MOVL_OPTIMAL)) return 0; if (TARGET_SINGLE_STRINGOP) emit_insn (gen_cld ()); countreg2 = gen_reg_rtx (Pmode); countreg = copy_to_mode_reg (counter_mode, count_exp); zeroreg = copy_to_mode_reg (Pmode, const0_rtx); /* Get rid of MEM_OFFSET, it won't be accurate. */ dst = change_address (dst, BLKmode, destreg); if (count == 0 && align < desired_alignment) { label = gen_label_rtx (); emit_cmp_and_jump_insns (countreg, GEN_INT (desired_alignment - 1), LEU, 0, counter_mode, 1, label); } if (align <= 1) { rtx label = ix86_expand_aligntest (destreg, 1); emit_insn (gen_strset (destreg, dst, gen_rtx_SUBREG (QImode, zeroreg, 0))); ix86_adjust_counter (countreg, 1); emit_label (label); LABEL_NUSES (label) = 1; } if (align <= 2) { rtx label = ix86_expand_aligntest (destreg, 2); emit_insn (gen_strset (destreg, dst, gen_rtx_SUBREG (HImode, zeroreg, 0))); ix86_adjust_counter (countreg, 2); emit_label (label); LABEL_NUSES (label) = 1; } if (align <= 4 && desired_alignment > 4) { rtx label = ix86_expand_aligntest (destreg, 4); emit_insn (gen_strset (destreg, dst, (TARGET_64BIT ? gen_rtx_SUBREG (SImode, zeroreg, 0) : zeroreg))); ix86_adjust_counter (countreg, 4); emit_label (label); LABEL_NUSES (label) = 1; } if (label && desired_alignment > 4 && !TARGET_64BIT) { emit_label (label); LABEL_NUSES (label) = 1; label = NULL_RTX; } if (!TARGET_SINGLE_STRINGOP) emit_insn (gen_cld ()); if (TARGET_64BIT) { emit_insn (gen_lshrdi3 (countreg2, ix86_zero_extend_to_Pmode (countreg), GEN_INT (3))); destexp = gen_rtx_ASHIFT (Pmode, countreg2, GEN_INT (3)); } else { emit_insn (gen_lshrsi3 (countreg2, countreg, const2_rtx)); destexp = gen_rtx_ASHIFT (Pmode, countreg2, const2_rtx); } destexp = gen_rtx_PLUS (Pmode, destexp, destreg); emit_insn (gen_rep_stos (destreg, countreg2, dst, zeroreg, destexp)); if (label) { emit_label (label); LABEL_NUSES (label) = 1; } if (TARGET_64BIT && align > 4 && count != 0 && (count & 4)) emit_insn (gen_strset (destreg, dst, gen_rtx_SUBREG (SImode, zeroreg, 0))); if (TARGET_64BIT && (align <= 4 || count == 0)) { rtx label = ix86_expand_aligntest (countreg, 4); emit_insn (gen_strset (destreg, dst, gen_rtx_SUBREG (SImode, zeroreg, 0))); emit_label (label); LABEL_NUSES (label) = 1; } if (align > 2 && count != 0 && (count & 2)) emit_insn (gen_strset (destreg, dst, gen_rtx_SUBREG (HImode, zeroreg, 0))); if (align <= 2 || count == 0) { rtx label = ix86_expand_aligntest (countreg, 2); emit_insn (gen_strset (destreg, dst, gen_rtx_SUBREG (HImode, zeroreg, 0))); emit_label (label); LABEL_NUSES (label) = 1; } if (align > 1 && count != 0 && (count & 1)) emit_insn (gen_strset (destreg, dst, gen_rtx_SUBREG (QImode, zeroreg, 0))); if (align <= 1 || count == 0) { rtx label = ix86_expand_aligntest (countreg, 1); emit_insn (gen_strset (destreg, dst, gen_rtx_SUBREG (QImode, zeroreg, 0))); emit_label (label); LABEL_NUSES (label) = 1; } } return 1; } /* Expand strlen. */ int ix86_expand_strlen (rtx out, rtx src, rtx eoschar, rtx align) { rtx addr, scratch1, scratch2, scratch3, scratch4; /* The generic case of strlen expander is long. Avoid it's expanding unless TARGET_INLINE_ALL_STRINGOPS. */ if (TARGET_UNROLL_STRLEN && eoschar == const0_rtx && optimize > 1 && !TARGET_INLINE_ALL_STRINGOPS && !optimize_size && (GET_CODE (align) != CONST_INT || INTVAL (align) < 4)) return 0; addr = force_reg (Pmode, XEXP (src, 0)); scratch1 = gen_reg_rtx (Pmode); if (TARGET_UNROLL_STRLEN && eoschar == const0_rtx && optimize > 1 && !optimize_size) { /* Well it seems that some optimizer does not combine a call like foo(strlen(bar), strlen(bar)); when the move and the subtraction is done here. It does calculate the length just once when these instructions are done inside of output_strlen_unroll(). But I think since &bar[strlen(bar)] is often used and I use one fewer register for the lifetime of output_strlen_unroll() this is better. */ emit_move_insn (out, addr); ix86_expand_strlensi_unroll_1 (out, src, align); /* strlensi_unroll_1 returns the address of the zero at the end of the string, like memchr(), so compute the length by subtracting the start address. */ if (TARGET_64BIT) emit_insn (gen_subdi3 (out, out, addr)); else emit_insn (gen_subsi3 (out, out, addr)); } else { rtx unspec; scratch2 = gen_reg_rtx (Pmode); scratch3 = gen_reg_rtx (Pmode); scratch4 = force_reg (Pmode, constm1_rtx); emit_move_insn (scratch3, addr); eoschar = force_reg (QImode, eoschar); emit_insn (gen_cld ()); src = replace_equiv_address_nv (src, scratch3); /* If .md starts supporting :P, this can be done in .md. */ unspec = gen_rtx_UNSPEC (Pmode, gen_rtvec (4, src, eoschar, align, scratch4), UNSPEC_SCAS); emit_insn (gen_strlenqi_1 (scratch1, scratch3, unspec)); if (TARGET_64BIT) { emit_insn (gen_one_cmpldi2 (scratch2, scratch1)); emit_insn (gen_adddi3 (out, scratch2, constm1_rtx)); } else { emit_insn (gen_one_cmplsi2 (scratch2, scratch1)); emit_insn (gen_addsi3 (out, scratch2, constm1_rtx)); } } return 1; } /* Expand the appropriate insns for doing strlen if not just doing repnz; scasb out = result, initialized with the start address align_rtx = alignment of the address. scratch = scratch register, initialized with the startaddress when not aligned, otherwise undefined This is just the body. It needs the initializations mentioned above and some address computing at the end. These things are done in i386.md. */ static void ix86_expand_strlensi_unroll_1 (rtx out, rtx src, rtx align_rtx) { int align; rtx tmp; rtx align_2_label = NULL_RTX; rtx align_3_label = NULL_RTX; rtx align_4_label = gen_label_rtx (); rtx end_0_label = gen_label_rtx (); rtx mem; rtx tmpreg = gen_reg_rtx (SImode); rtx scratch = gen_reg_rtx (SImode); rtx cmp; align = 0; if (GET_CODE (align_rtx) == CONST_INT) align = INTVAL (align_rtx); /* Loop to check 1..3 bytes for null to get an aligned pointer. */ /* Is there a known alignment and is it less than 4? */ if (align < 4) { rtx scratch1 = gen_reg_rtx (Pmode); emit_move_insn (scratch1, out); /* Is there a known alignment and is it not 2? */ if (align != 2) { align_3_label = gen_label_rtx (); /* Label when aligned to 3-byte */ align_2_label = gen_label_rtx (); /* Label when aligned to 2-byte */ /* Leave just the 3 lower bits. */ align_rtx = expand_binop (Pmode, and_optab, scratch1, GEN_INT (3), NULL_RTX, 0, OPTAB_WIDEN); emit_cmp_and_jump_insns (align_rtx, const0_rtx, EQ, NULL, Pmode, 1, align_4_label); emit_cmp_and_jump_insns (align_rtx, GEN_INT (2), EQ, NULL, Pmode, 1, align_2_label); emit_cmp_and_jump_insns (align_rtx, GEN_INT (2), GTU, NULL, Pmode, 1, align_3_label); } else { /* Since the alignment is 2, we have to check 2 or 0 bytes; check if is aligned to 4 - byte. */ align_rtx = expand_binop (Pmode, and_optab, scratch1, GEN_INT (2), NULL_RTX, 0, OPTAB_WIDEN); emit_cmp_and_jump_insns (align_rtx, const0_rtx, EQ, NULL, Pmode, 1, align_4_label); } mem = change_address (src, QImode, out); /* Now compare the bytes. */ /* Compare the first n unaligned byte on a byte per byte basis. */ emit_cmp_and_jump_insns (mem, const0_rtx, EQ, NULL, QImode, 1, end_0_label); /* Increment the address. */ if (TARGET_64BIT) emit_insn (gen_adddi3 (out, out, const1_rtx)); else emit_insn (gen_addsi3 (out, out, const1_rtx)); /* Not needed with an alignment of 2 */ if (align != 2) { emit_label (align_2_label); emit_cmp_and_jump_insns (mem, const0_rtx, EQ, NULL, QImode, 1, end_0_label); if (TARGET_64BIT) emit_insn (gen_adddi3 (out, out, const1_rtx)); else emit_insn (gen_addsi3 (out, out, const1_rtx)); emit_label (align_3_label); } emit_cmp_and_jump_insns (mem, const0_rtx, EQ, NULL, QImode, 1, end_0_label); if (TARGET_64BIT) emit_insn (gen_adddi3 (out, out, const1_rtx)); else emit_insn (gen_addsi3 (out, out, const1_rtx)); } /* Generate loop to check 4 bytes at a time. It is not a good idea to align this loop. It gives only huge programs, but does not help to speed up. */ emit_label (align_4_label); mem = change_address (src, SImode, out); emit_move_insn (scratch, mem); if (TARGET_64BIT) emit_insn (gen_adddi3 (out, out, GEN_INT (4))); else emit_insn (gen_addsi3 (out, out, GEN_INT (4))); /* This formula yields a nonzero result iff one of the bytes is zero. This saves three branches inside loop and many cycles. */ emit_insn (gen_addsi3 (tmpreg, scratch, GEN_INT (-0x01010101))); emit_insn (gen_one_cmplsi2 (scratch, scratch)); emit_insn (gen_andsi3 (tmpreg, tmpreg, scratch)); emit_insn (gen_andsi3 (tmpreg, tmpreg, gen_int_mode (0x80808080, SImode))); emit_cmp_and_jump_insns (tmpreg, const0_rtx, EQ, 0, SImode, 1, align_4_label); if (TARGET_CMOVE) { rtx reg = gen_reg_rtx (SImode); rtx reg2 = gen_reg_rtx (Pmode); emit_move_insn (reg, tmpreg); emit_insn (gen_lshrsi3 (reg, reg, GEN_INT (16))); /* If zero is not in the first two bytes, move two bytes forward. */ emit_insn (gen_testsi_ccno_1 (tmpreg, GEN_INT (0x8080))); tmp = gen_rtx_REG (CCNOmode, FLAGS_REG); tmp = gen_rtx_EQ (VOIDmode, tmp, const0_rtx); emit_insn (gen_rtx_SET (VOIDmode, tmpreg, gen_rtx_IF_THEN_ELSE (SImode, tmp, reg, tmpreg))); /* Emit lea manually to avoid clobbering of flags. */ emit_insn (gen_rtx_SET (SImode, reg2, gen_rtx_PLUS (Pmode, out, GEN_INT (2)))); tmp = gen_rtx_REG (CCNOmode, FLAGS_REG); tmp = gen_rtx_EQ (VOIDmode, tmp, const0_rtx); emit_insn (gen_rtx_SET (VOIDmode, out, gen_rtx_IF_THEN_ELSE (Pmode, tmp, reg2, out))); } else { rtx end_2_label = gen_label_rtx (); /* Is zero in the first two bytes? */ emit_insn (gen_testsi_ccno_1 (tmpreg, GEN_INT (0x8080))); tmp = gen_rtx_REG (CCNOmode, FLAGS_REG); tmp = gen_rtx_NE (VOIDmode, tmp, const0_rtx); tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp, gen_rtx_LABEL_REF (VOIDmode, end_2_label), pc_rtx); tmp = emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp)); JUMP_LABEL (tmp) = end_2_label; /* Not in the first two. Move two bytes forward. */ emit_insn (gen_lshrsi3 (tmpreg, tmpreg, GEN_INT (16))); if (TARGET_64BIT) emit_insn (gen_adddi3 (out, out, GEN_INT (2))); else emit_insn (gen_addsi3 (out, out, GEN_INT (2))); emit_label (end_2_label); } /* Avoid branch in fixing the byte. */ tmpreg = gen_lowpart (QImode, tmpreg); emit_insn (gen_addqi3_cc (tmpreg, tmpreg, tmpreg)); cmp = gen_rtx_LTU (Pmode, gen_rtx_REG (CCmode, 17), const0_rtx); if (TARGET_64BIT) emit_insn (gen_subdi3_carry_rex64 (out, out, GEN_INT (3), cmp)); else emit_insn (gen_subsi3_carry (out, out, GEN_INT (3), cmp)); emit_label (end_0_label); } void ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1, rtx callarg2 ATTRIBUTE_UNUSED, rtx pop, int sibcall) { rtx use = NULL, call; if (pop == const0_rtx) pop = NULL; if (TARGET_64BIT && pop) abort (); #if TARGET_MACHO if (flag_pic && GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF) fnaddr = machopic_indirect_call_target (fnaddr); #else /* Static functions and indirect calls don't need the pic register. */ if (! TARGET_64BIT && flag_pic && GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF && ! SYMBOL_REF_LOCAL_P (XEXP (fnaddr, 0))) use_reg (&use, pic_offset_table_rtx); if (TARGET_64BIT && INTVAL (callarg2) >= 0) { rtx al = gen_rtx_REG (QImode, 0); emit_move_insn (al, callarg2); use_reg (&use, al); } #endif /* TARGET_MACHO */ if (! call_insn_operand (XEXP (fnaddr, 0), Pmode)) { fnaddr = copy_to_mode_reg (Pmode, XEXP (fnaddr, 0)); fnaddr = gen_rtx_MEM (QImode, fnaddr); } if (sibcall && TARGET_64BIT && !constant_call_address_operand (XEXP (fnaddr, 0), Pmode)) { rtx addr; addr = copy_to_mode_reg (Pmode, XEXP (fnaddr, 0)); fnaddr = gen_rtx_REG (Pmode, FIRST_REX_INT_REG + 3 /* R11 */); emit_move_insn (fnaddr, addr); fnaddr = gen_rtx_MEM (QImode, fnaddr); } call = gen_rtx_CALL (VOIDmode, fnaddr, callarg1); if (retval) call = gen_rtx_SET (VOIDmode, retval, call); if (pop) { pop = gen_rtx_PLUS (Pmode, stack_pointer_rtx, pop); pop = gen_rtx_SET (VOIDmode, stack_pointer_rtx, pop); call = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, call, pop)); } call = emit_call_insn (call); if (use) CALL_INSN_FUNCTION_USAGE (call) = use; } /* Clear stack slot assignments remembered from previous functions. This is called from INIT_EXPANDERS once before RTL is emitted for each function. */ static struct machine_function * ix86_init_machine_status (void) { struct machine_function *f; f = ggc_alloc_cleared (sizeof (struct machine_function)); f->use_fast_prologue_epilogue_nregs = -1; return f; } /* Return a MEM corresponding to a stack slot with mode MODE. Allocate a new slot if necessary. The RTL for a function can have several slots available: N is which slot to use. */ rtx assign_386_stack_local (enum machine_mode mode, int n) { struct stack_local_entry *s; if (n < 0 || n >= MAX_386_STACK_LOCALS) abort (); for (s = ix86_stack_locals; s; s = s->next) if (s->mode == mode && s->n == n) return s->rtl; s = (struct stack_local_entry *) ggc_alloc (sizeof (struct stack_local_entry)); s->n = n; s->mode = mode; s->rtl = assign_stack_local (mode, GET_MODE_SIZE (mode), 0); s->next = ix86_stack_locals; ix86_stack_locals = s; return s->rtl; } /* Construct the SYMBOL_REF for the tls_get_addr function. */ static GTY(()) rtx ix86_tls_symbol; rtx ix86_tls_get_addr (void) { if (!ix86_tls_symbol) { ix86_tls_symbol = gen_rtx_SYMBOL_REF (Pmode, (TARGET_GNU_TLS && !TARGET_64BIT) ? "___tls_get_addr" : "__tls_get_addr"); } return ix86_tls_symbol; } /* Calculate the length of the memory address in the instruction encoding. Does not include the one-byte modrm, opcode, or prefix. */ static int memory_address_length (rtx addr) { struct ix86_address parts; rtx base, index, disp; int len; if (GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == POST_INC || GET_CODE (addr) == PRE_MODIFY || GET_CODE (addr) == POST_MODIFY) return 0; if (! ix86_decompose_address (addr, &parts)) abort (); base = parts.base; index = parts.index; disp = parts.disp; len = 0; /* Rule of thumb: - esp as the base always wants an index, - ebp as the base always wants a displacement. */ /* Register Indirect. */ if (base && !index && !disp) { /* esp (for its index) and ebp (for its displacement) need the two-byte modrm form. */ if (addr == stack_pointer_rtx || addr == arg_pointer_rtx || addr == frame_pointer_rtx || addr == hard_frame_pointer_rtx) len = 1; } /* Direct Addressing. */ else if (disp && !base && !index) len = 4; else { /* Find the length of the displacement constant. */ if (disp) { if (GET_CODE (disp) == CONST_INT && CONST_OK_FOR_LETTER_P (INTVAL (disp), 'K') && base) len = 1; else len = 4; } /* ebp always wants a displacement. */ else if (base == hard_frame_pointer_rtx) len = 1; /* An index requires the two-byte modrm form.... */ if (index /* ...like esp, which always wants an index. */ || base == stack_pointer_rtx || base == arg_pointer_rtx || base == frame_pointer_rtx) len += 1; } return len; } /* Compute default value for "length_immediate" attribute. When SHORTFORM is set, expect that insn have 8bit immediate alternative. */ int ix86_attr_length_immediate_default (rtx insn, int shortform) { int len = 0; int i; extract_insn_cached (insn); for (i = recog_data.n_operands - 1; i >= 0; --i) if (CONSTANT_P (recog_data.operand[i])) { if (len) abort (); if (shortform && GET_CODE (recog_data.operand[i]) == CONST_INT && CONST_OK_FOR_LETTER_P (INTVAL (recog_data.operand[i]), 'K')) len = 1; else { switch (get_attr_mode (insn)) { case MODE_QI: len+=1; break; case MODE_HI: len+=2; break; case MODE_SI: len+=4; break; /* Immediates for DImode instructions are encoded as 32bit sign extended values. */ case MODE_DI: len+=4; break; default: fatal_insn ("unknown insn mode", insn); } } } return len; } /* Compute default value for "length_address" attribute. */ int ix86_attr_length_address_default (rtx insn) { int i; if (get_attr_type (insn) == TYPE_LEA) { rtx set = PATTERN (insn); if (GET_CODE (set) == SET) ; else if (GET_CODE (set) == PARALLEL && GET_CODE (XVECEXP (set, 0, 0)) == SET) set = XVECEXP (set, 0, 0); else { #ifdef ENABLE_CHECKING abort (); #endif return 0; } return memory_address_length (SET_SRC (set)); } extract_insn_cached (insn); for (i = recog_data.n_operands - 1; i >= 0; --i) if (GET_CODE (recog_data.operand[i]) == MEM) { return memory_address_length (XEXP (recog_data.operand[i], 0)); break; } return 0; } /* Return the maximum number of instructions a cpu can issue. */ static int ix86_issue_rate (void) { switch (ix86_tune) { case PROCESSOR_PENTIUM: case PROCESSOR_K6: return 2; case PROCESSOR_PENTIUMPRO: case PROCESSOR_PENTIUM4: case PROCESSOR_ATHLON: case PROCESSOR_K8: return 3; default: return 1; } } /* A subroutine of ix86_adjust_cost -- return true iff INSN reads flags set by DEP_INSN and nothing set by DEP_INSN. */ static int ix86_flags_dependant (rtx insn, rtx dep_insn, enum attr_type insn_type) { rtx set, set2; /* Simplify the test for uninteresting insns. */ if (insn_type != TYPE_SETCC && insn_type != TYPE_ICMOV && insn_type != TYPE_FCMOV && insn_type != TYPE_IBR) return 0; if ((set = single_set (dep_insn)) != 0) { set = SET_DEST (set); set2 = NULL_RTX; } else if (GET_CODE (PATTERN (dep_insn)) == PARALLEL && XVECLEN (PATTERN (dep_insn), 0) == 2 && GET_CODE (XVECEXP (PATTERN (dep_insn), 0, 0)) == SET && GET_CODE (XVECEXP (PATTERN (dep_insn), 0, 1)) == SET) { set = SET_DEST (XVECEXP (PATTERN (dep_insn), 0, 0)); set2 = SET_DEST (XVECEXP (PATTERN (dep_insn), 0, 0)); } else return 0; if (GET_CODE (set) != REG || REGNO (set) != FLAGS_REG) return 0; /* This test is true if the dependent insn reads the flags but not any other potentially set register. */ if (!reg_overlap_mentioned_p (set, PATTERN (insn))) return 0; if (set2 && reg_overlap_mentioned_p (set2, PATTERN (insn))) return 0; return 1; } /* A subroutine of ix86_adjust_cost -- return true iff INSN has a memory address with operands set by DEP_INSN. */ static int ix86_agi_dependant (rtx insn, rtx dep_insn, enum attr_type insn_type) { rtx addr; if (insn_type == TYPE_LEA && TARGET_PENTIUM) { addr = PATTERN (insn); if (GET_CODE (addr) == SET) ; else if (GET_CODE (addr) == PARALLEL && GET_CODE (XVECEXP (addr, 0, 0)) == SET) addr = XVECEXP (addr, 0, 0); else abort (); addr = SET_SRC (addr); } else { int i; extract_insn_cached (insn); for (i = recog_data.n_operands - 1; i >= 0; --i) if (GET_CODE (recog_data.operand[i]) == MEM) { addr = XEXP (recog_data.operand[i], 0); goto found; } return 0; found:; } return modified_in_p (addr, dep_insn); } static int ix86_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost) { enum attr_type insn_type, dep_insn_type; enum attr_memory memory, dep_memory; rtx set, set2; int dep_insn_code_number; /* Anti and output dependencies have zero cost on all CPUs. */ if (REG_NOTE_KIND (link) != 0) return 0; dep_insn_code_number = recog_memoized (dep_insn); /* If we can't recognize the insns, we can't really do anything. */ if (dep_insn_code_number < 0 || recog_memoized (insn) < 0) return cost; insn_type = get_attr_type (insn); dep_insn_type = get_attr_type (dep_insn); switch (ix86_tune) { case PROCESSOR_PENTIUM: /* Address Generation Interlock adds a cycle of latency. */ if (ix86_agi_dependant (insn, dep_insn, insn_type)) cost += 1; /* ??? Compares pair with jump/setcc. */ if (ix86_flags_dependant (insn, dep_insn, insn_type)) cost = 0; /* Floating point stores require value to be ready one cycle earlier. */ if (insn_type == TYPE_FMOV && get_attr_memory (insn) == MEMORY_STORE && !ix86_agi_dependant (insn, dep_insn, insn_type)) cost += 1; break; case PROCESSOR_PENTIUMPRO: memory = get_attr_memory (insn); dep_memory = get_attr_memory (dep_insn); /* Since we can't represent delayed latencies of load+operation, increase the cost here for non-imov insns. */ if (dep_insn_type != TYPE_IMOV && dep_insn_type != TYPE_FMOV && (dep_memory == MEMORY_LOAD || dep_memory == MEMORY_BOTH)) cost += 1; /* INT->FP conversion is expensive. */ if (get_attr_fp_int_src (dep_insn)) cost += 5; /* There is one cycle extra latency between an FP op and a store. */ if (insn_type == TYPE_FMOV && (set = single_set (dep_insn)) != NULL_RTX && (set2 = single_set (insn)) != NULL_RTX && rtx_equal_p (SET_DEST (set), SET_SRC (set2)) && GET_CODE (SET_DEST (set2)) == MEM) cost += 1; /* Show ability of reorder buffer to hide latency of load by executing in parallel with previous instruction in case previous instruction is not needed to compute the address. */ if ((memory == MEMORY_LOAD || memory == MEMORY_BOTH) && !ix86_agi_dependant (insn, dep_insn, insn_type)) { /* Claim moves to take one cycle, as core can issue one load at time and the next load can start cycle later. */ if (dep_insn_type == TYPE_IMOV || dep_insn_type == TYPE_FMOV) cost = 1; else if (cost > 1) cost--; } break; case PROCESSOR_K6: memory = get_attr_memory (insn); dep_memory = get_attr_memory (dep_insn); /* The esp dependency is resolved before the instruction is really finished. */ if ((insn_type == TYPE_PUSH || insn_type == TYPE_POP) && (dep_insn_type == TYPE_PUSH || dep_insn_type == TYPE_POP)) return 1; /* Since we can't represent delayed latencies of load+operation, increase the cost here for non-imov insns. */ if (dep_memory == MEMORY_LOAD || dep_memory == MEMORY_BOTH) cost += (dep_insn_type != TYPE_IMOV) ? 2 : 1; /* INT->FP conversion is expensive. */ if (get_attr_fp_int_src (dep_insn)) cost += 5; /* Show ability of reorder buffer to hide latency of load by executing in parallel with previous instruction in case previous instruction is not needed to compute the address. */ if ((memory == MEMORY_LOAD || memory == MEMORY_BOTH) && !ix86_agi_dependant (insn, dep_insn, insn_type)) { /* Claim moves to take one cycle, as core can issue one load at time and the next load can start cycle later. */ if (dep_insn_type == TYPE_IMOV || dep_insn_type == TYPE_FMOV) cost = 1; else if (cost > 2) cost -= 2; else cost = 1; } break; case PROCESSOR_ATHLON: case PROCESSOR_K8: memory = get_attr_memory (insn); dep_memory = get_attr_memory (dep_insn); /* Show ability of reorder buffer to hide latency of load by executing in parallel with previous instruction in case previous instruction is not needed to compute the address. */ if ((memory == MEMORY_LOAD || memory == MEMORY_BOTH) && !ix86_agi_dependant (insn, dep_insn, insn_type)) { enum attr_unit unit = get_attr_unit (insn); int loadcost = 3; /* Because of the difference between the length of integer and floating unit pipeline preparation stages, the memory operands for floating point are cheaper. ??? For Athlon it the difference is most probably 2. */ if (unit == UNIT_INTEGER || unit == UNIT_UNKNOWN) loadcost = 3; else loadcost = TARGET_ATHLON ? 2 : 0; if (cost >= loadcost) cost -= loadcost; else cost = 0; } default: break; } return cost; } static union { struct ppro_sched_data { rtx decode[3]; int issued_this_cycle; } ppro; } ix86_sched_data; static enum attr_ppro_uops ix86_safe_ppro_uops (rtx insn) { if (recog_memoized (insn) >= 0) return get_attr_ppro_uops (insn); else return PPRO_UOPS_MANY; } static void ix86_dump_ppro_packet (FILE *dump) { if (ix86_sched_data.ppro.decode[0]) { fprintf (dump, "PPRO packet: %d", INSN_UID (ix86_sched_data.ppro.decode[0])); if (ix86_sched_data.ppro.decode[1]) fprintf (dump, " %d", INSN_UID (ix86_sched_data.ppro.decode[1])); if (ix86_sched_data.ppro.decode[2]) fprintf (dump, " %d", INSN_UID (ix86_sched_data.ppro.decode[2])); fputc ('\n', dump); } } /* We're beginning a new block. Initialize data structures as necessary. */ static void ix86_sched_init (FILE *dump ATTRIBUTE_UNUSED, int sched_verbose ATTRIBUTE_UNUSED, int veclen ATTRIBUTE_UNUSED) { memset (&ix86_sched_data, 0, sizeof (ix86_sched_data)); } /* Shift INSN to SLOT, and shift everything else down. */ static void ix86_reorder_insn (rtx *insnp, rtx *slot) { if (insnp != slot) { rtx insn = *insnp; do insnp[0] = insnp[1]; while (++insnp != slot); *insnp = insn; } } static void ix86_sched_reorder_ppro (rtx *ready, rtx *e_ready) { rtx decode[3]; enum attr_ppro_uops cur_uops; int issued_this_cycle; rtx *insnp; int i; /* At this point .ppro.decode contains the state of the three decoders from last "cycle". That is, those insns that were actually independent. But here we're scheduling for the decoder, and we may find things that are decodable in the same cycle. */ memcpy (decode, ix86_sched_data.ppro.decode, sizeof (decode)); issued_this_cycle = 0; insnp = e_ready; cur_uops = ix86_safe_ppro_uops (*insnp); /* If the decoders are empty, and we've a complex insn at the head of the priority queue, let it issue without complaint. */ if (decode[0] == NULL) { if (cur_uops == PPRO_UOPS_MANY) { decode[0] = *insnp; goto ppro_done; } /* Otherwise, search for a 2-4 uop unsn to issue. */ while (cur_uops != PPRO_UOPS_FEW) { if (insnp == ready) break; cur_uops = ix86_safe_ppro_uops (*--insnp); } /* If so, move it to the head of the line. */ if (cur_uops == PPRO_UOPS_FEW) ix86_reorder_insn (insnp, e_ready); /* Issue the head of the queue. */ issued_this_cycle = 1; decode[0] = *e_ready--; } /* Look for simple insns to fill in the other two slots. */ for (i = 1; i < 3; ++i) if (decode[i] == NULL) { if (ready > e_ready) goto ppro_done; insnp = e_ready; cur_uops = ix86_safe_ppro_uops (*insnp); while (cur_uops != PPRO_UOPS_ONE) { if (insnp == ready) break; cur_uops = ix86_safe_ppro_uops (*--insnp); } /* Found one. Move it to the head of the queue and issue it. */ if (cur_uops == PPRO_UOPS_ONE) { ix86_reorder_insn (insnp, e_ready); decode[i] = *e_ready--; issued_this_cycle++; continue; } /* ??? Didn't find one. Ideally, here we would do a lazy split of 2-uop insns, issue one and queue the other. */ } ppro_done: if (issued_this_cycle == 0) issued_this_cycle = 1; ix86_sched_data.ppro.issued_this_cycle = issued_this_cycle; } /* We are about to being issuing insns for this clock cycle. Override the default sort algorithm to better slot instructions. */ static int ix86_sched_reorder (FILE *dump ATTRIBUTE_UNUSED, int sched_verbose ATTRIBUTE_UNUSED, rtx *ready, int *n_readyp, int clock_var ATTRIBUTE_UNUSED) { int n_ready = *n_readyp; rtx *e_ready = ready + n_ready - 1; /* Make sure to go ahead and initialize key items in ix86_sched_data if we are not going to bother trying to reorder the ready queue. */ if (n_ready < 2) { ix86_sched_data.ppro.issued_this_cycle = 1; goto out; } switch (ix86_tune) { default: break; case PROCESSOR_PENTIUMPRO: ix86_sched_reorder_ppro (ready, e_ready); break; } out: return ix86_issue_rate (); } /* We are about to issue INSN. Return the number of insns left on the ready queue that can be issued this cycle. */ static int ix86_variable_issue (FILE *dump, int sched_verbose, rtx insn, int can_issue_more) { int i; switch (ix86_tune) { default: return can_issue_more - 1; case PROCESSOR_PENTIUMPRO: { enum attr_ppro_uops uops = ix86_safe_ppro_uops (insn); if (uops == PPRO_UOPS_MANY) { if (sched_verbose) ix86_dump_ppro_packet (dump); ix86_sched_data.ppro.decode[0] = insn; ix86_sched_data.ppro.decode[1] = NULL; ix86_sched_data.ppro.decode[2] = NULL; if (sched_verbose) ix86_dump_ppro_packet (dump); ix86_sched_data.ppro.decode[0] = NULL; } else if (uops == PPRO_UOPS_FEW) { if (sched_verbose) ix86_dump_ppro_packet (dump); ix86_sched_data.ppro.decode[0] = insn; ix86_sched_data.ppro.decode[1] = NULL; ix86_sched_data.ppro.decode[2] = NULL; } else { for (i = 0; i < 3; ++i) if (ix86_sched_data.ppro.decode[i] == NULL) { ix86_sched_data.ppro.decode[i] = insn; break; } if (i == 3) abort (); if (i == 2) { if (sched_verbose) ix86_dump_ppro_packet (dump); ix86_sched_data.ppro.decode[0] = NULL; ix86_sched_data.ppro.decode[1] = NULL; ix86_sched_data.ppro.decode[2] = NULL; } } } return --ix86_sched_data.ppro.issued_this_cycle; } } static int ia32_use_dfa_pipeline_interface (void) { if (TARGET_PENTIUM || TARGET_ATHLON_K8) return 1; return 0; } /* How many alternative schedules to try. This should be as wide as the scheduling freedom in the DFA, but no wider. Making this value too large results extra work for the scheduler. */ static int ia32_multipass_dfa_lookahead (void) { if (ix86_tune == PROCESSOR_PENTIUM) return 2; else return 0; } /* Compute the alignment given to a constant that is being placed in memory. EXP is the constant and ALIGN is the alignment that the object would ordinarily have. The value of this function is used instead of that alignment to align the object. */ int ix86_constant_alignment (tree exp, int align) { if (TREE_CODE (exp) == REAL_CST) { if (TYPE_MODE (TREE_TYPE (exp)) == DFmode && align < 64) return 64; else if (ALIGN_MODE_128 (TYPE_MODE (TREE_TYPE (exp))) && align < 128) return 128; } else if (!optimize_size && TREE_CODE (exp) == STRING_CST && !TARGET_NO_ALIGN_LONG_STRINGS && TREE_STRING_LENGTH (exp) >= 31 && align < BITS_PER_WORD) return BITS_PER_WORD; return align; } /* Compute the alignment for a static variable. TYPE is the data type, and ALIGN is the alignment that the object would ordinarily have. The value of this function is used instead of that alignment to align the object. */ int ix86_data_alignment (tree type, int align) { if (AGGREGATE_TYPE_P (type) && TYPE_SIZE (type) && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST && (TREE_INT_CST_LOW (TYPE_SIZE (type)) >= 256 || TREE_INT_CST_HIGH (TYPE_SIZE (type))) && align < 256) return 256; /* x86-64 ABI requires arrays greater than 16 bytes to be aligned to 16byte boundary. */ if (TARGET_64BIT) { if (AGGREGATE_TYPE_P (type) && TYPE_SIZE (type) && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST && (TREE_INT_CST_LOW (TYPE_SIZE (type)) >= 128 || TREE_INT_CST_HIGH (TYPE_SIZE (type))) && align < 128) return 128; } if (TREE_CODE (type) == ARRAY_TYPE) { if (TYPE_MODE (TREE_TYPE (type)) == DFmode && align < 64) return 64; if (ALIGN_MODE_128 (TYPE_MODE (TREE_TYPE (type))) && align < 128) return 128; } else if (TREE_CODE (type) == COMPLEX_TYPE) { if (TYPE_MODE (type) == DCmode && align < 64) return 64; if (TYPE_MODE (type) == XCmode && align < 128) return 128; } else if ((TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE || TREE_CODE (type) == QUAL_UNION_TYPE) && TYPE_FIELDS (type)) { if (DECL_MODE (TYPE_FIELDS (type)) == DFmode && align < 64) return 64; if (ALIGN_MODE_128 (DECL_MODE (TYPE_FIELDS (type))) && align < 128) return 128; } else if (TREE_CODE (type) == REAL_TYPE || TREE_CODE (type) == VECTOR_TYPE || TREE_CODE (type) == INTEGER_TYPE) { if (TYPE_MODE (type) == DFmode && align < 64) return 64; if (ALIGN_MODE_128 (TYPE_MODE (type)) && align < 128) return 128; } return align; } /* Compute the alignment for a local variable. TYPE is the data type, and ALIGN is the alignment that the object would ordinarily have. The value of this macro is used instead of that alignment to align the object. */ int ix86_local_alignment (tree type, int align) { /* x86-64 ABI requires arrays greater than 16 bytes to be aligned to 16byte boundary. */ if (TARGET_64BIT) { if (AGGREGATE_TYPE_P (type) && TYPE_SIZE (type) && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST && (TREE_INT_CST_LOW (TYPE_SIZE (type)) >= 16 || TREE_INT_CST_HIGH (TYPE_SIZE (type))) && align < 128) return 128; } if (TREE_CODE (type) == ARRAY_TYPE) { if (TYPE_MODE (TREE_TYPE (type)) == DFmode && align < 64) return 64; if (ALIGN_MODE_128 (TYPE_MODE (TREE_TYPE (type))) && align < 128) return 128; } else if (TREE_CODE (type) == COMPLEX_TYPE) { if (TYPE_MODE (type) == DCmode && align < 64) return 64; if (TYPE_MODE (type) == XCmode && align < 128) return 128; } else if ((TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE || TREE_CODE (type) == QUAL_UNION_TYPE) && TYPE_FIELDS (type)) { if (DECL_MODE (TYPE_FIELDS (type)) == DFmode && align < 64) return 64; if (ALIGN_MODE_128 (DECL_MODE (TYPE_FIELDS (type))) && align < 128) return 128; } else if (TREE_CODE (type) == REAL_TYPE || TREE_CODE (type) == VECTOR_TYPE || TREE_CODE (type) == INTEGER_TYPE) { if (TYPE_MODE (type) == DFmode && align < 64) return 64; if (ALIGN_MODE_128 (TYPE_MODE (type)) && align < 128) return 128; } return align; } /* Emit RTL insns to initialize the variable parts of a trampoline. FNADDR is an RTX for the address of the function's pure code. CXT is an RTX for the static chain value for the function. */ void x86_initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt) { if (!TARGET_64BIT) { /* Compute offset from the end of the jmp to the target function. */ rtx disp = expand_binop (SImode, sub_optab, fnaddr, plus_constant (tramp, 10), NULL_RTX, 1, OPTAB_DIRECT); emit_move_insn (gen_rtx_MEM (QImode, tramp), gen_int_mode (0xb9, QImode)); emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 1)), cxt); emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 5)), gen_int_mode (0xe9, QImode)); emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 6)), disp); } else { int offset = 0; /* Try to load address using shorter movl instead of movabs. We may want to support movq for kernel mode, but kernel does not use trampolines at the moment. */ if (x86_64_zero_extended_value (fnaddr)) { fnaddr = copy_to_mode_reg (DImode, fnaddr); emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, offset)), gen_int_mode (0xbb41, HImode)); emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, offset + 2)), gen_lowpart (SImode, fnaddr)); offset += 6; } else { emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, offset)), gen_int_mode (0xbb49, HImode)); emit_move_insn (gen_rtx_MEM (DImode, plus_constant (tramp, offset + 2)), fnaddr); offset += 10; } /* Load static chain using movabs to r10. */ emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, offset)), gen_int_mode (0xba49, HImode)); emit_move_insn (gen_rtx_MEM (DImode, plus_constant (tramp, offset + 2)), cxt); offset += 10; /* Jump to the r11 */ emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, offset)), gen_int_mode (0xff49, HImode)); emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, offset+2)), gen_int_mode (0xe3, QImode)); offset += 3; if (offset > TRAMPOLINE_SIZE) abort (); } #ifdef ENABLE_EXECUTE_STACK emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "__enable_execute_stack"), LCT_NORMAL, VOIDmode, 1, tramp, Pmode); #endif } #define def_builtin(MASK, NAME, TYPE, CODE) \ do { \ if ((MASK) & target_flags \ && (!((MASK) & MASK_64BIT) || TARGET_64BIT)) \ builtin_function ((NAME), (TYPE), (CODE), BUILT_IN_MD, \ NULL, NULL_TREE); \ } while (0) struct builtin_description { const unsigned int mask; const enum insn_code icode; const char *const name; const enum ix86_builtins code; const enum rtx_code comparison; const unsigned int flag; }; static const struct builtin_description bdesc_comi[] = { { MASK_SSE, CODE_FOR_sse_comi, "__builtin_ia32_comieq", IX86_BUILTIN_COMIEQSS, UNEQ, 0 }, { MASK_SSE, CODE_FOR_sse_comi, "__builtin_ia32_comilt", IX86_BUILTIN_COMILTSS, UNLT, 0 }, { MASK_SSE, CODE_FOR_sse_comi, "__builtin_ia32_comile", IX86_BUILTIN_COMILESS, UNLE, 0 }, { MASK_SSE, CODE_FOR_sse_comi, "__builtin_ia32_comigt", IX86_BUILTIN_COMIGTSS, GT, 0 }, { MASK_SSE, CODE_FOR_sse_comi, "__builtin_ia32_comige", IX86_BUILTIN_COMIGESS, GE, 0 }, { MASK_SSE, CODE_FOR_sse_comi, "__builtin_ia32_comineq", IX86_BUILTIN_COMINEQSS, LTGT, 0 }, { MASK_SSE, CODE_FOR_sse_ucomi, "__builtin_ia32_ucomieq", IX86_BUILTIN_UCOMIEQSS, UNEQ, 0 }, { MASK_SSE, CODE_FOR_sse_ucomi, "__builtin_ia32_ucomilt", IX86_BUILTIN_UCOMILTSS, UNLT, 0 }, { MASK_SSE, CODE_FOR_sse_ucomi, "__builtin_ia32_ucomile", IX86_BUILTIN_UCOMILESS, UNLE, 0 }, { MASK_SSE, CODE_FOR_sse_ucomi, "__builtin_ia32_ucomigt", IX86_BUILTIN_UCOMIGTSS, GT, 0 }, { MASK_SSE, CODE_FOR_sse_ucomi, "__builtin_ia32_ucomige", IX86_BUILTIN_UCOMIGESS, GE, 0 }, { MASK_SSE, CODE_FOR_sse_ucomi, "__builtin_ia32_ucomineq", IX86_BUILTIN_UCOMINEQSS, LTGT, 0 }, { MASK_SSE2, CODE_FOR_sse2_comi, "__builtin_ia32_comisdeq", IX86_BUILTIN_COMIEQSD, UNEQ, 0 }, { MASK_SSE2, CODE_FOR_sse2_comi, "__builtin_ia32_comisdlt", IX86_BUILTIN_COMILTSD, UNLT, 0 }, { MASK_SSE2, CODE_FOR_sse2_comi, "__builtin_ia32_comisdle", IX86_BUILTIN_COMILESD, UNLE, 0 }, { MASK_SSE2, CODE_FOR_sse2_comi, "__builtin_ia32_comisdgt", IX86_BUILTIN_COMIGTSD, GT, 0 }, { MASK_SSE2, CODE_FOR_sse2_comi, "__builtin_ia32_comisdge", IX86_BUILTIN_COMIGESD, GE, 0 }, { MASK_SSE2, CODE_FOR_sse2_comi, "__builtin_ia32_comisdneq", IX86_BUILTIN_COMINEQSD, LTGT, 0 }, { MASK_SSE2, CODE_FOR_sse2_ucomi, "__builtin_ia32_ucomisdeq", IX86_BUILTIN_UCOMIEQSD, UNEQ, 0 }, { MASK_SSE2, CODE_FOR_sse2_ucomi, "__builtin_ia32_ucomisdlt", IX86_BUILTIN_UCOMILTSD, UNLT, 0 }, { MASK_SSE2, CODE_FOR_sse2_ucomi, "__builtin_ia32_ucomisdle", IX86_BUILTIN_UCOMILESD, UNLE, 0 }, { MASK_SSE2, CODE_FOR_sse2_ucomi, "__builtin_ia32_ucomisdgt", IX86_BUILTIN_UCOMIGTSD, GT, 0 }, { MASK_SSE2, CODE_FOR_sse2_ucomi, "__builtin_ia32_ucomisdge", IX86_BUILTIN_UCOMIGESD, GE, 0 }, { MASK_SSE2, CODE_FOR_sse2_ucomi, "__builtin_ia32_ucomisdneq", IX86_BUILTIN_UCOMINEQSD, LTGT, 0 }, }; static const struct builtin_description bdesc_2arg[] = { /* SSE */ { MASK_SSE, CODE_FOR_addv4sf3, "__builtin_ia32_addps", IX86_BUILTIN_ADDPS, 0, 0 }, { MASK_SSE, CODE_FOR_subv4sf3, "__builtin_ia32_subps", IX86_BUILTIN_SUBPS, 0, 0 }, { MASK_SSE, CODE_FOR_mulv4sf3, "__builtin_ia32_mulps", IX86_BUILTIN_MULPS, 0, 0 }, { MASK_SSE, CODE_FOR_divv4sf3, "__builtin_ia32_divps", IX86_BUILTIN_DIVPS, 0, 0 }, { MASK_SSE, CODE_FOR_vmaddv4sf3, "__builtin_ia32_addss", IX86_BUILTIN_ADDSS, 0, 0 }, { MASK_SSE, CODE_FOR_vmsubv4sf3, "__builtin_ia32_subss", IX86_BUILTIN_SUBSS, 0, 0 }, { MASK_SSE, CODE_FOR_vmmulv4sf3, "__builtin_ia32_mulss", IX86_BUILTIN_MULSS, 0, 0 }, { MASK_SSE, CODE_FOR_vmdivv4sf3, "__builtin_ia32_divss", IX86_BUILTIN_DIVSS, 0, 0 }, { MASK_SSE, CODE_FOR_maskcmpv4sf3, "__builtin_ia32_cmpeqps", IX86_BUILTIN_CMPEQPS, EQ, 0 }, { MASK_SSE, CODE_FOR_maskcmpv4sf3, "__builtin_ia32_cmpltps", IX86_BUILTIN_CMPLTPS, LT, 0 }, { MASK_SSE, CODE_FOR_maskcmpv4sf3, "__builtin_ia32_cmpleps", IX86_BUILTIN_CMPLEPS, LE, 0 }, { MASK_SSE, CODE_FOR_maskcmpv4sf3, "__builtin_ia32_cmpgtps", IX86_BUILTIN_CMPGTPS, LT, 1 }, { MASK_SSE, CODE_FOR_maskcmpv4sf3, "__builtin_ia32_cmpgeps", IX86_BUILTIN_CMPGEPS, LE, 1 }, { MASK_SSE, CODE_FOR_maskcmpv4sf3, "__builtin_ia32_cmpunordps", IX86_BUILTIN_CMPUNORDPS, UNORDERED, 0 }, { MASK_SSE, CODE_FOR_maskncmpv4sf3, "__builtin_ia32_cmpneqps", IX86_BUILTIN_CMPNEQPS, EQ, 0 }, { MASK_SSE, CODE_FOR_maskncmpv4sf3, "__builtin_ia32_cmpnltps", IX86_BUILTIN_CMPNLTPS, LT, 0 }, { MASK_SSE, CODE_FOR_maskncmpv4sf3, "__builtin_ia32_cmpnleps", IX86_BUILTIN_CMPNLEPS, LE, 0 }, { MASK_SSE, CODE_FOR_maskncmpv4sf3, "__builtin_ia32_cmpngtps", IX86_BUILTIN_CMPNGTPS, LT, 1 }, { MASK_SSE, CODE_FOR_maskncmpv4sf3, "__builtin_ia32_cmpngeps", IX86_BUILTIN_CMPNGEPS, LE, 1 }, { MASK_SSE, CODE_FOR_maskncmpv4sf3, "__builtin_ia32_cmpordps", IX86_BUILTIN_CMPORDPS, UNORDERED, 0 }, { MASK_SSE, CODE_FOR_vmmaskcmpv4sf3, "__builtin_ia32_cmpeqss", IX86_BUILTIN_CMPEQSS, EQ, 0 }, { MASK_SSE, CODE_FOR_vmmaskcmpv4sf3, "__builtin_ia32_cmpltss", IX86_BUILTIN_CMPLTSS, LT, 0 }, { MASK_SSE, CODE_FOR_vmmaskcmpv4sf3, "__builtin_ia32_cmpless", IX86_BUILTIN_CMPLESS, LE, 0 }, { MASK_SSE, CODE_FOR_vmmaskcmpv4sf3, "__builtin_ia32_cmpunordss", IX86_BUILTIN_CMPUNORDSS, UNORDERED, 0 }, { MASK_SSE, CODE_FOR_vmmaskncmpv4sf3, "__builtin_ia32_cmpneqss", IX86_BUILTIN_CMPNEQSS, EQ, 0 }, { MASK_SSE, CODE_FOR_vmmaskncmpv4sf3, "__builtin_ia32_cmpnltss", IX86_BUILTIN_CMPNLTSS, LT, 0 }, { MASK_SSE, CODE_FOR_vmmaskncmpv4sf3, "__builtin_ia32_cmpnless", IX86_BUILTIN_CMPNLESS, LE, 0 }, { MASK_SSE, CODE_FOR_vmmaskncmpv4sf3, "__builtin_ia32_cmpordss", IX86_BUILTIN_CMPORDSS, UNORDERED, 0 }, { MASK_SSE, CODE_FOR_sminv4sf3, "__builtin_ia32_minps", IX86_BUILTIN_MINPS, 0, 0 }, { MASK_SSE, CODE_FOR_smaxv4sf3, "__builtin_ia32_maxps", IX86_BUILTIN_MAXPS, 0, 0 }, { MASK_SSE, CODE_FOR_vmsminv4sf3, "__builtin_ia32_minss", IX86_BUILTIN_MINSS, 0, 0 }, { MASK_SSE, CODE_FOR_vmsmaxv4sf3, "__builtin_ia32_maxss", IX86_BUILTIN_MAXSS, 0, 0 }, { MASK_SSE, CODE_FOR_sse_andv4sf3, "__builtin_ia32_andps", IX86_BUILTIN_ANDPS, 0, 0 }, { MASK_SSE, CODE_FOR_sse_nandv4sf3, "__builtin_ia32_andnps", IX86_BUILTIN_ANDNPS, 0, 0 }, { MASK_SSE, CODE_FOR_sse_iorv4sf3, "__builtin_ia32_orps", IX86_BUILTIN_ORPS, 0, 0 }, { MASK_SSE, CODE_FOR_sse_xorv4sf3, "__builtin_ia32_xorps", IX86_BUILTIN_XORPS, 0, 0 }, { MASK_SSE, CODE_FOR_sse_movss, "__builtin_ia32_movss", IX86_BUILTIN_MOVSS, 0, 0 }, { MASK_SSE, CODE_FOR_sse_movhlps, "__builtin_ia32_movhlps", IX86_BUILTIN_MOVHLPS, 0, 0 }, { MASK_SSE, CODE_FOR_sse_movlhps, "__builtin_ia32_movlhps", IX86_BUILTIN_MOVLHPS, 0, 0 }, { MASK_SSE, CODE_FOR_sse_unpckhps, "__builtin_ia32_unpckhps", IX86_BUILTIN_UNPCKHPS, 0, 0 }, { MASK_SSE, CODE_FOR_sse_unpcklps, "__builtin_ia32_unpcklps", IX86_BUILTIN_UNPCKLPS, 0, 0 }, /* MMX */ { MASK_MMX, CODE_FOR_addv8qi3, "__builtin_ia32_paddb", IX86_BUILTIN_PADDB, 0, 0 }, { MASK_MMX, CODE_FOR_addv4hi3, "__builtin_ia32_paddw", IX86_BUILTIN_PADDW, 0, 0 }, { MASK_MMX, CODE_FOR_addv2si3, "__builtin_ia32_paddd", IX86_BUILTIN_PADDD, 0, 0 }, { MASK_MMX, CODE_FOR_mmx_adddi3, "__builtin_ia32_paddq", IX86_BUILTIN_PADDQ, 0, 0 }, { MASK_MMX, CODE_FOR_subv8qi3, "__builtin_ia32_psubb", IX86_BUILTIN_PSUBB, 0, 0 }, { MASK_MMX, CODE_FOR_subv4hi3, "__builtin_ia32_psubw", IX86_BUILTIN_PSUBW, 0, 0 }, { MASK_MMX, CODE_FOR_subv2si3, "__builtin_ia32_psubd", IX86_BUILTIN_PSUBD, 0, 0 }, { MASK_MMX, CODE_FOR_mmx_subdi3, "__builtin_ia32_psubq", IX86_BUILTIN_PSUBQ, 0, 0 }, { MASK_MMX, CODE_FOR_ssaddv8qi3, "__builtin_ia32_paddsb", IX86_BUILTIN_PADDSB, 0, 0 }, { MASK_MMX, CODE_FOR_ssaddv4hi3, "__builtin_ia32_paddsw", IX86_BUILTIN_PADDSW, 0, 0 }, { MASK_MMX, CODE_FOR_sssubv8qi3, "__builtin_ia32_psubsb", IX86_BUILTIN_PSUBSB, 0, 0 }, { MASK_MMX, CODE_FOR_sssubv4hi3, "__builtin_ia32_psubsw", IX86_BUILTIN_PSUBSW, 0, 0 }, { MASK_MMX, CODE_FOR_usaddv8qi3, "__builtin_ia32_paddusb", IX86_BUILTIN_PADDUSB, 0, 0 }, { MASK_MMX, CODE_FOR_usaddv4hi3, "__builtin_ia32_paddusw", IX86_BUILTIN_PADDUSW, 0, 0 }, { MASK_MMX, CODE_FOR_ussubv8qi3, "__builtin_ia32_psubusb", IX86_BUILTIN_PSUBUSB, 0, 0 }, { MASK_MMX, CODE_FOR_ussubv4hi3, "__builtin_ia32_psubusw", IX86_BUILTIN_PSUBUSW, 0, 0 }, { MASK_MMX, CODE_FOR_mulv4hi3, "__builtin_ia32_pmullw", IX86_BUILTIN_PMULLW, 0, 0 }, { MASK_MMX, CODE_FOR_smulv4hi3_highpart, "__builtin_ia32_pmulhw", IX86_BUILTIN_PMULHW, 0, 0 }, { MASK_SSE | MASK_3DNOW_A, CODE_FOR_umulv4hi3_highpart, "__builtin_ia32_pmulhuw", IX86_BUILTIN_PMULHUW, 0, 0 }, { MASK_MMX, CODE_FOR_mmx_anddi3, "__builtin_ia32_pand", IX86_BUILTIN_PAND, 0, 0 }, { MASK_MMX, CODE_FOR_mmx_nanddi3, "__builtin_ia32_pandn", IX86_BUILTIN_PANDN, 0, 0 }, { MASK_MMX, CODE_FOR_mmx_iordi3, "__builtin_ia32_por", IX86_BUILTIN_POR, 0, 0 }, { MASK_MMX, CODE_FOR_mmx_xordi3, "__builtin_ia32_pxor", IX86_BUILTIN_PXOR, 0, 0 }, { MASK_SSE | MASK_3DNOW_A, CODE_FOR_mmx_uavgv8qi3, "__builtin_ia32_pavgb", IX86_BUILTIN_PAVGB, 0, 0 }, { MASK_SSE | MASK_3DNOW_A, CODE_FOR_mmx_uavgv4hi3, "__builtin_ia32_pavgw", IX86_BUILTIN_PAVGW, 0, 0 }, { MASK_MMX, CODE_FOR_eqv8qi3, "__builtin_ia32_pcmpeqb", IX86_BUILTIN_PCMPEQB, 0, 0 }, { MASK_MMX, CODE_FOR_eqv4hi3, "__builtin_ia32_pcmpeqw", IX86_BUILTIN_PCMPEQW, 0, 0 }, { MASK_MMX, CODE_FOR_eqv2si3, "__builtin_ia32_pcmpeqd", IX86_BUILTIN_PCMPEQD, 0, 0 }, { MASK_MMX, CODE_FOR_gtv8qi3, "__builtin_ia32_pcmpgtb", IX86_BUILTIN_PCMPGTB, 0, 0 }, { MASK_MMX, CODE_FOR_gtv4hi3, "__builtin_ia32_pcmpgtw", IX86_BUILTIN_PCMPGTW, 0, 0 }, { MASK_MMX, CODE_FOR_gtv2si3, "__builtin_ia32_pcmpgtd", IX86_BUILTIN_PCMPGTD, 0, 0 }, { MASK_SSE | MASK_3DNOW_A, CODE_FOR_umaxv8qi3, "__builtin_ia32_pmaxub", IX86_BUILTIN_PMAXUB, 0, 0 }, { MASK_SSE | MASK_3DNOW_A, CODE_FOR_smaxv4hi3, "__builtin_ia32_pmaxsw", IX86_BUILTIN_PMAXSW, 0, 0 }, { MASK_SSE | MASK_3DNOW_A, CODE_FOR_uminv8qi3, "__builtin_ia32_pminub", IX86_BUILTIN_PMINUB, 0, 0 }, { MASK_SSE | MASK_3DNOW_A, CODE_FOR_sminv4hi3, "__builtin_ia32_pminsw", IX86_BUILTIN_PMINSW, 0, 0 }, { MASK_MMX, CODE_FOR_mmx_punpckhbw, "__builtin_ia32_punpckhbw", IX86_BUILTIN_PUNPCKHBW, 0, 0 }, { MASK_MMX, CODE_FOR_mmx_punpckhwd, "__builtin_ia32_punpckhwd", IX86_BUILTIN_PUNPCKHWD, 0, 0 }, { MASK_MMX, CODE_FOR_mmx_punpckhdq, "__builtin_ia32_punpckhdq", IX86_BUILTIN_PUNPCKHDQ, 0, 0 }, { MASK_MMX, CODE_FOR_mmx_punpcklbw, "__builtin_ia32_punpcklbw", IX86_BUILTIN_PUNPCKLBW, 0, 0 }, { MASK_MMX, CODE_FOR_mmx_punpcklwd, "__builtin_ia32_punpcklwd", IX86_BUILTIN_PUNPCKLWD, 0, 0 }, { MASK_MMX, CODE_FOR_mmx_punpckldq, "__builtin_ia32_punpckldq", IX86_BUILTIN_PUNPCKLDQ, 0, 0 }, /* Special. */ { MASK_MMX, CODE_FOR_mmx_packsswb, 0, IX86_BUILTIN_PACKSSWB, 0, 0 }, { MASK_MMX, CODE_FOR_mmx_packssdw, 0, IX86_BUILTIN_PACKSSDW, 0, 0 }, { MASK_MMX, CODE_FOR_mmx_packuswb, 0, IX86_BUILTIN_PACKUSWB, 0, 0 }, { MASK_SSE, CODE_FOR_cvtpi2ps, 0, IX86_BUILTIN_CVTPI2PS, 0, 0 }, { MASK_SSE, CODE_FOR_cvtsi2ss, 0, IX86_BUILTIN_CVTSI2SS, 0, 0 }, { MASK_SSE | MASK_64BIT, CODE_FOR_cvtsi2ssq, 0, IX86_BUILTIN_CVTSI642SS, 0, 0 }, { MASK_MMX, CODE_FOR_ashlv4hi3, 0, IX86_BUILTIN_PSLLW, 0, 0 }, { MASK_MMX, CODE_FOR_ashlv4hi3, 0, IX86_BUILTIN_PSLLWI, 0, 0 }, { MASK_MMX, CODE_FOR_ashlv2si3, 0, IX86_BUILTIN_PSLLD, 0, 0 }, { MASK_MMX, CODE_FOR_ashlv2si3, 0, IX86_BUILTIN_PSLLDI, 0, 0 }, { MASK_MMX, CODE_FOR_mmx_ashldi3, 0, IX86_BUILTIN_PSLLQ, 0, 0 }, { MASK_MMX, CODE_FOR_mmx_ashldi3, 0, IX86_BUILTIN_PSLLQI, 0, 0 }, { MASK_MMX, CODE_FOR_lshrv4hi3, 0, IX86_BUILTIN_PSRLW, 0, 0 }, { MASK_MMX, CODE_FOR_lshrv4hi3, 0, IX86_BUILTIN_PSRLWI, 0, 0 }, { MASK_MMX, CODE_FOR_lshrv2si3, 0, IX86_BUILTIN_PSRLD, 0, 0 }, { MASK_MMX, CODE_FOR_lshrv2si3, 0, IX86_BUILTIN_PSRLDI, 0, 0 }, { MASK_MMX, CODE_FOR_mmx_lshrdi3, 0, IX86_BUILTIN_PSRLQ, 0, 0 }, { MASK_MMX, CODE_FOR_mmx_lshrdi3, 0, IX86_BUILTIN_PSRLQI, 0, 0 }, { MASK_MMX, CODE_FOR_ashrv4hi3, 0, IX86_BUILTIN_PSRAW, 0, 0 }, { MASK_MMX, CODE_FOR_ashrv4hi3, 0, IX86_BUILTIN_PSRAWI, 0, 0 }, { MASK_MMX, CODE_FOR_ashrv2si3, 0, IX86_BUILTIN_PSRAD, 0, 0 }, { MASK_MMX, CODE_FOR_ashrv2si3, 0, IX86_BUILTIN_PSRADI, 0, 0 }, { MASK_SSE | MASK_3DNOW_A, CODE_FOR_mmx_psadbw, 0, IX86_BUILTIN_PSADBW, 0, 0 }, { MASK_MMX, CODE_FOR_mmx_pmaddwd, 0, IX86_BUILTIN_PMADDWD, 0, 0 }, /* SSE2 */ { MASK_SSE2, CODE_FOR_addv2df3, "__builtin_ia32_addpd", IX86_BUILTIN_ADDPD, 0, 0 }, { MASK_SSE2, CODE_FOR_subv2df3, "__builtin_ia32_subpd", IX86_BUILTIN_SUBPD, 0, 0 }, { MASK_SSE2, CODE_FOR_mulv2df3, "__builtin_ia32_mulpd", IX86_BUILTIN_MULPD, 0, 0 }, { MASK_SSE2, CODE_FOR_divv2df3, "__builtin_ia32_divpd", IX86_BUILTIN_DIVPD, 0, 0 }, { MASK_SSE2, CODE_FOR_vmaddv2df3, "__builtin_ia32_addsd", IX86_BUILTIN_ADDSD, 0, 0 }, { MASK_SSE2, CODE_FOR_vmsubv2df3, "__builtin_ia32_subsd", IX86_BUILTIN_SUBSD, 0, 0 }, { MASK_SSE2, CODE_FOR_vmmulv2df3, "__builtin_ia32_mulsd", IX86_BUILTIN_MULSD, 0, 0 }, { MASK_SSE2, CODE_FOR_vmdivv2df3, "__builtin_ia32_divsd", IX86_BUILTIN_DIVSD, 0, 0 }, { MASK_SSE2, CODE_FOR_maskcmpv2df3, "__builtin_ia32_cmpeqpd", IX86_BUILTIN_CMPEQPD, EQ, 0 }, { MASK_SSE2, CODE_FOR_maskcmpv2df3, "__builtin_ia32_cmpltpd", IX86_BUILTIN_CMPLTPD, LT, 0 }, { MASK_SSE2, CODE_FOR_maskcmpv2df3, "__builtin_ia32_cmplepd", IX86_BUILTIN_CMPLEPD, LE, 0 }, { MASK_SSE2, CODE_FOR_maskcmpv2df3, "__builtin_ia32_cmpgtpd", IX86_BUILTIN_CMPGTPD, LT, 1 }, { MASK_SSE2, CODE_FOR_maskcmpv2df3, "__builtin_ia32_cmpgepd", IX86_BUILTIN_CMPGEPD, LE, 1 }, { MASK_SSE2, CODE_FOR_maskcmpv2df3, "__builtin_ia32_cmpunordpd", IX86_BUILTIN_CMPUNORDPD, UNORDERED, 0 }, { MASK_SSE2, CODE_FOR_maskncmpv2df3, "__builtin_ia32_cmpneqpd", IX86_BUILTIN_CMPNEQPD, EQ, 0 }, { MASK_SSE2, CODE_FOR_maskncmpv2df3, "__builtin_ia32_cmpnltpd", IX86_BUILTIN_CMPNLTPD, LT, 0 }, { MASK_SSE2, CODE_FOR_maskncmpv2df3, "__builtin_ia32_cmpnlepd", IX86_BUILTIN_CMPNLEPD, LE, 0 }, { MASK_SSE2, CODE_FOR_maskncmpv2df3, "__builtin_ia32_cmpngtpd", IX86_BUILTIN_CMPNGTPD, LT, 1 }, { MASK_SSE2, CODE_FOR_maskncmpv2df3, "__builtin_ia32_cmpngepd", IX86_BUILTIN_CMPNGEPD, LE, 1 }, { MASK_SSE2, CODE_FOR_maskncmpv2df3, "__builtin_ia32_cmpordpd", IX86_BUILTIN_CMPORDPD, UNORDERED, 0 }, { MASK_SSE2, CODE_FOR_vmmaskcmpv2df3, "__builtin_ia32_cmpeqsd", IX86_BUILTIN_CMPEQSD, EQ, 0 }, { MASK_SSE2, CODE_FOR_vmmaskcmpv2df3, "__builtin_ia32_cmpltsd", IX86_BUILTIN_CMPLTSD, LT, 0 }, { MASK_SSE2, CODE_FOR_vmmaskcmpv2df3, "__builtin_ia32_cmplesd", IX86_BUILTIN_CMPLESD, LE, 0 }, { MASK_SSE2, CODE_FOR_vmmaskcmpv2df3, "__builtin_ia32_cmpunordsd", IX86_BUILTIN_CMPUNORDSD, UNORDERED, 0 }, { MASK_SSE2, CODE_FOR_vmmaskncmpv2df3, "__builtin_ia32_cmpneqsd", IX86_BUILTIN_CMPNEQSD, EQ, 0 }, { MASK_SSE2, CODE_FOR_vmmaskncmpv2df3, "__builtin_ia32_cmpnltsd", IX86_BUILTIN_CMPNLTSD, LT, 0 }, { MASK_SSE2, CODE_FOR_vmmaskncmpv2df3, "__builtin_ia32_cmpnlesd", IX86_BUILTIN_CMPNLESD, LE, 0 }, { MASK_SSE2, CODE_FOR_vmmaskncmpv2df3, "__builtin_ia32_cmpordsd", IX86_BUILTIN_CMPORDSD, UNORDERED, 0 }, { MASK_SSE2, CODE_FOR_sminv2df3, "__builtin_ia32_minpd", IX86_BUILTIN_MINPD, 0, 0 }, { MASK_SSE2, CODE_FOR_smaxv2df3, "__builtin_ia32_maxpd", IX86_BUILTIN_MAXPD, 0, 0 }, { MASK_SSE2, CODE_FOR_vmsminv2df3, "__builtin_ia32_minsd", IX86_BUILTIN_MINSD, 0, 0 }, { MASK_SSE2, CODE_FOR_vmsmaxv2df3, "__builtin_ia32_maxsd", IX86_BUILTIN_MAXSD, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_andv2df3, "__builtin_ia32_andpd", IX86_BUILTIN_ANDPD, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_nandv2df3, "__builtin_ia32_andnpd", IX86_BUILTIN_ANDNPD, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_iorv2df3, "__builtin_ia32_orpd", IX86_BUILTIN_ORPD, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_xorv2df3, "__builtin_ia32_xorpd", IX86_BUILTIN_XORPD, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_movsd, "__builtin_ia32_movsd", IX86_BUILTIN_MOVSD, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_unpckhpd, "__builtin_ia32_unpckhpd", IX86_BUILTIN_UNPCKHPD, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_unpcklpd, "__builtin_ia32_unpcklpd", IX86_BUILTIN_UNPCKLPD, 0, 0 }, /* SSE2 MMX */ { MASK_SSE2, CODE_FOR_addv16qi3, "__builtin_ia32_paddb128", IX86_BUILTIN_PADDB128, 0, 0 }, { MASK_SSE2, CODE_FOR_addv8hi3, "__builtin_ia32_paddw128", IX86_BUILTIN_PADDW128, 0, 0 }, { MASK_SSE2, CODE_FOR_addv4si3, "__builtin_ia32_paddd128", IX86_BUILTIN_PADDD128, 0, 0 }, { MASK_SSE2, CODE_FOR_addv2di3, "__builtin_ia32_paddq128", IX86_BUILTIN_PADDQ128, 0, 0 }, { MASK_SSE2, CODE_FOR_subv16qi3, "__builtin_ia32_psubb128", IX86_BUILTIN_PSUBB128, 0, 0 }, { MASK_SSE2, CODE_FOR_subv8hi3, "__builtin_ia32_psubw128", IX86_BUILTIN_PSUBW128, 0, 0 }, { MASK_SSE2, CODE_FOR_subv4si3, "__builtin_ia32_psubd128", IX86_BUILTIN_PSUBD128, 0, 0 }, { MASK_SSE2, CODE_FOR_subv2di3, "__builtin_ia32_psubq128", IX86_BUILTIN_PSUBQ128, 0, 0 }, { MASK_MMX, CODE_FOR_ssaddv16qi3, "__builtin_ia32_paddsb128", IX86_BUILTIN_PADDSB128, 0, 0 }, { MASK_MMX, CODE_FOR_ssaddv8hi3, "__builtin_ia32_paddsw128", IX86_BUILTIN_PADDSW128, 0, 0 }, { MASK_MMX, CODE_FOR_sssubv16qi3, "__builtin_ia32_psubsb128", IX86_BUILTIN_PSUBSB128, 0, 0 }, { MASK_MMX, CODE_FOR_sssubv8hi3, "__builtin_ia32_psubsw128", IX86_BUILTIN_PSUBSW128, 0, 0 }, { MASK_MMX, CODE_FOR_usaddv16qi3, "__builtin_ia32_paddusb128", IX86_BUILTIN_PADDUSB128, 0, 0 }, { MASK_MMX, CODE_FOR_usaddv8hi3, "__builtin_ia32_paddusw128", IX86_BUILTIN_PADDUSW128, 0, 0 }, { MASK_MMX, CODE_FOR_ussubv16qi3, "__builtin_ia32_psubusb128", IX86_BUILTIN_PSUBUSB128, 0, 0 }, { MASK_MMX, CODE_FOR_ussubv8hi3, "__builtin_ia32_psubusw128", IX86_BUILTIN_PSUBUSW128, 0, 0 }, { MASK_SSE2, CODE_FOR_mulv8hi3, "__builtin_ia32_pmullw128", IX86_BUILTIN_PMULLW128, 0, 0 }, { MASK_SSE2, CODE_FOR_smulv8hi3_highpart, "__builtin_ia32_pmulhw128", IX86_BUILTIN_PMULHW128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_umulsidi3, "__builtin_ia32_pmuludq", IX86_BUILTIN_PMULUDQ, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_umulv2siv2di3, "__builtin_ia32_pmuludq128", IX86_BUILTIN_PMULUDQ128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_andv2di3, "__builtin_ia32_pand128", IX86_BUILTIN_PAND128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_nandv2di3, "__builtin_ia32_pandn128", IX86_BUILTIN_PANDN128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_iorv2di3, "__builtin_ia32_por128", IX86_BUILTIN_POR128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_xorv2di3, "__builtin_ia32_pxor128", IX86_BUILTIN_PXOR128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_uavgv16qi3, "__builtin_ia32_pavgb128", IX86_BUILTIN_PAVGB128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_uavgv8hi3, "__builtin_ia32_pavgw128", IX86_BUILTIN_PAVGW128, 0, 0 }, { MASK_SSE2, CODE_FOR_eqv16qi3, "__builtin_ia32_pcmpeqb128", IX86_BUILTIN_PCMPEQB128, 0, 0 }, { MASK_SSE2, CODE_FOR_eqv8hi3, "__builtin_ia32_pcmpeqw128", IX86_BUILTIN_PCMPEQW128, 0, 0 }, { MASK_SSE2, CODE_FOR_eqv4si3, "__builtin_ia32_pcmpeqd128", IX86_BUILTIN_PCMPEQD128, 0, 0 }, { MASK_SSE2, CODE_FOR_gtv16qi3, "__builtin_ia32_pcmpgtb128", IX86_BUILTIN_PCMPGTB128, 0, 0 }, { MASK_SSE2, CODE_FOR_gtv8hi3, "__builtin_ia32_pcmpgtw128", IX86_BUILTIN_PCMPGTW128, 0, 0 }, { MASK_SSE2, CODE_FOR_gtv4si3, "__builtin_ia32_pcmpgtd128", IX86_BUILTIN_PCMPGTD128, 0, 0 }, { MASK_SSE2, CODE_FOR_umaxv16qi3, "__builtin_ia32_pmaxub128", IX86_BUILTIN_PMAXUB128, 0, 0 }, { MASK_SSE2, CODE_FOR_smaxv8hi3, "__builtin_ia32_pmaxsw128", IX86_BUILTIN_PMAXSW128, 0, 0 }, { MASK_SSE2, CODE_FOR_uminv16qi3, "__builtin_ia32_pminub128", IX86_BUILTIN_PMINUB128, 0, 0 }, { MASK_SSE2, CODE_FOR_sminv8hi3, "__builtin_ia32_pminsw128", IX86_BUILTIN_PMINSW128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_punpckhbw, "__builtin_ia32_punpckhbw128", IX86_BUILTIN_PUNPCKHBW128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_punpckhwd, "__builtin_ia32_punpckhwd128", IX86_BUILTIN_PUNPCKHWD128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_punpckhdq, "__builtin_ia32_punpckhdq128", IX86_BUILTIN_PUNPCKHDQ128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_punpckhqdq, "__builtin_ia32_punpckhqdq128", IX86_BUILTIN_PUNPCKHQDQ128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_punpcklbw, "__builtin_ia32_punpcklbw128", IX86_BUILTIN_PUNPCKLBW128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_punpcklwd, "__builtin_ia32_punpcklwd128", IX86_BUILTIN_PUNPCKLWD128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_punpckldq, "__builtin_ia32_punpckldq128", IX86_BUILTIN_PUNPCKLDQ128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_punpcklqdq, "__builtin_ia32_punpcklqdq128", IX86_BUILTIN_PUNPCKLQDQ128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_packsswb, "__builtin_ia32_packsswb128", IX86_BUILTIN_PACKSSWB128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_packssdw, "__builtin_ia32_packssdw128", IX86_BUILTIN_PACKSSDW128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_packuswb, "__builtin_ia32_packuswb128", IX86_BUILTIN_PACKUSWB128, 0, 0 }, { MASK_SSE2, CODE_FOR_umulv8hi3_highpart, "__builtin_ia32_pmulhuw128", IX86_BUILTIN_PMULHUW128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_psadbw, 0, IX86_BUILTIN_PSADBW128, 0, 0 }, { MASK_SSE2, CODE_FOR_ashlv8hi3_ti, 0, IX86_BUILTIN_PSLLW128, 0, 0 }, { MASK_SSE2, CODE_FOR_ashlv8hi3, 0, IX86_BUILTIN_PSLLWI128, 0, 0 }, { MASK_SSE2, CODE_FOR_ashlv4si3_ti, 0, IX86_BUILTIN_PSLLD128, 0, 0 }, { MASK_SSE2, CODE_FOR_ashlv4si3, 0, IX86_BUILTIN_PSLLDI128, 0, 0 }, { MASK_SSE2, CODE_FOR_ashlv2di3_ti, 0, IX86_BUILTIN_PSLLQ128, 0, 0 }, { MASK_SSE2, CODE_FOR_ashlv2di3, 0, IX86_BUILTIN_PSLLQI128, 0, 0 }, { MASK_SSE2, CODE_FOR_lshrv8hi3_ti, 0, IX86_BUILTIN_PSRLW128, 0, 0 }, { MASK_SSE2, CODE_FOR_lshrv8hi3, 0, IX86_BUILTIN_PSRLWI128, 0, 0 }, { MASK_SSE2, CODE_FOR_lshrv4si3_ti, 0, IX86_BUILTIN_PSRLD128, 0, 0 }, { MASK_SSE2, CODE_FOR_lshrv4si3, 0, IX86_BUILTIN_PSRLDI128, 0, 0 }, { MASK_SSE2, CODE_FOR_lshrv2di3_ti, 0, IX86_BUILTIN_PSRLQ128, 0, 0 }, { MASK_SSE2, CODE_FOR_lshrv2di3, 0, IX86_BUILTIN_PSRLQI128, 0, 0 }, { MASK_SSE2, CODE_FOR_ashrv8hi3_ti, 0, IX86_BUILTIN_PSRAW128, 0, 0 }, { MASK_SSE2, CODE_FOR_ashrv8hi3, 0, IX86_BUILTIN_PSRAWI128, 0, 0 }, { MASK_SSE2, CODE_FOR_ashrv4si3_ti, 0, IX86_BUILTIN_PSRAD128, 0, 0 }, { MASK_SSE2, CODE_FOR_ashrv4si3, 0, IX86_BUILTIN_PSRADI128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_pmaddwd, 0, IX86_BUILTIN_PMADDWD128, 0, 0 }, { MASK_SSE2, CODE_FOR_cvtsi2sd, 0, IX86_BUILTIN_CVTSI2SD, 0, 0 }, { MASK_SSE2 | MASK_64BIT, CODE_FOR_cvtsi2sdq, 0, IX86_BUILTIN_CVTSI642SD, 0, 0 }, { MASK_SSE2, CODE_FOR_cvtsd2ss, 0, IX86_BUILTIN_CVTSD2SS, 0, 0 }, { MASK_SSE2, CODE_FOR_cvtss2sd, 0, IX86_BUILTIN_CVTSS2SD, 0, 0 }, /* SSE3 MMX */ { MASK_SSE3, CODE_FOR_addsubv4sf3, "__builtin_ia32_addsubps", IX86_BUILTIN_ADDSUBPS, 0, 0 }, { MASK_SSE3, CODE_FOR_addsubv2df3, "__builtin_ia32_addsubpd", IX86_BUILTIN_ADDSUBPD, 0, 0 }, { MASK_SSE3, CODE_FOR_haddv4sf3, "__builtin_ia32_haddps", IX86_BUILTIN_HADDPS, 0, 0 }, { MASK_SSE3, CODE_FOR_haddv2df3, "__builtin_ia32_haddpd", IX86_BUILTIN_HADDPD, 0, 0 }, { MASK_SSE3, CODE_FOR_hsubv4sf3, "__builtin_ia32_hsubps", IX86_BUILTIN_HSUBPS, 0, 0 }, { MASK_SSE3, CODE_FOR_hsubv2df3, "__builtin_ia32_hsubpd", IX86_BUILTIN_HSUBPD, 0, 0 } }; static const struct builtin_description bdesc_1arg[] = { { MASK_SSE | MASK_3DNOW_A, CODE_FOR_mmx_pmovmskb, 0, IX86_BUILTIN_PMOVMSKB, 0, 0 }, { MASK_SSE, CODE_FOR_sse_movmskps, 0, IX86_BUILTIN_MOVMSKPS, 0, 0 }, { MASK_SSE, CODE_FOR_sqrtv4sf2, 0, IX86_BUILTIN_SQRTPS, 0, 0 }, { MASK_SSE, CODE_FOR_rsqrtv4sf2, 0, IX86_BUILTIN_RSQRTPS, 0, 0 }, { MASK_SSE, CODE_FOR_rcpv4sf2, 0, IX86_BUILTIN_RCPPS, 0, 0 }, { MASK_SSE, CODE_FOR_cvtps2pi, 0, IX86_BUILTIN_CVTPS2PI, 0, 0 }, { MASK_SSE, CODE_FOR_cvtss2si, 0, IX86_BUILTIN_CVTSS2SI, 0, 0 }, { MASK_SSE | MASK_64BIT, CODE_FOR_cvtss2siq, 0, IX86_BUILTIN_CVTSS2SI64, 0, 0 }, { MASK_SSE, CODE_FOR_cvttps2pi, 0, IX86_BUILTIN_CVTTPS2PI, 0, 0 }, { MASK_SSE, CODE_FOR_cvttss2si, 0, IX86_BUILTIN_CVTTSS2SI, 0, 0 }, { MASK_SSE | MASK_64BIT, CODE_FOR_cvttss2siq, 0, IX86_BUILTIN_CVTTSS2SI64, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_pmovmskb, 0, IX86_BUILTIN_PMOVMSKB128, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_movmskpd, 0, IX86_BUILTIN_MOVMSKPD, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_movq2dq, 0, IX86_BUILTIN_MOVQ2DQ, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_movdq2q, 0, IX86_BUILTIN_MOVDQ2Q, 0, 0 }, { MASK_SSE2, CODE_FOR_sqrtv2df2, 0, IX86_BUILTIN_SQRTPD, 0, 0 }, { MASK_SSE2, CODE_FOR_cvtdq2pd, 0, IX86_BUILTIN_CVTDQ2PD, 0, 0 }, { MASK_SSE2, CODE_FOR_cvtdq2ps, 0, IX86_BUILTIN_CVTDQ2PS, 0, 0 }, { MASK_SSE2, CODE_FOR_cvtpd2dq, 0, IX86_BUILTIN_CVTPD2DQ, 0, 0 }, { MASK_SSE2, CODE_FOR_cvtpd2pi, 0, IX86_BUILTIN_CVTPD2PI, 0, 0 }, { MASK_SSE2, CODE_FOR_cvtpd2ps, 0, IX86_BUILTIN_CVTPD2PS, 0, 0 }, { MASK_SSE2, CODE_FOR_cvttpd2dq, 0, IX86_BUILTIN_CVTTPD2DQ, 0, 0 }, { MASK_SSE2, CODE_FOR_cvttpd2pi, 0, IX86_BUILTIN_CVTTPD2PI, 0, 0 }, { MASK_SSE2, CODE_FOR_cvtpi2pd, 0, IX86_BUILTIN_CVTPI2PD, 0, 0 }, { MASK_SSE2, CODE_FOR_cvtsd2si, 0, IX86_BUILTIN_CVTSD2SI, 0, 0 }, { MASK_SSE2, CODE_FOR_cvttsd2si, 0, IX86_BUILTIN_CVTTSD2SI, 0, 0 }, { MASK_SSE2 | MASK_64BIT, CODE_FOR_cvtsd2siq, 0, IX86_BUILTIN_CVTSD2SI64, 0, 0 }, { MASK_SSE2 | MASK_64BIT, CODE_FOR_cvttsd2siq, 0, IX86_BUILTIN_CVTTSD2SI64, 0, 0 }, { MASK_SSE2, CODE_FOR_cvtps2dq, 0, IX86_BUILTIN_CVTPS2DQ, 0, 0 }, { MASK_SSE2, CODE_FOR_cvtps2pd, 0, IX86_BUILTIN_CVTPS2PD, 0, 0 }, { MASK_SSE2, CODE_FOR_cvttps2dq, 0, IX86_BUILTIN_CVTTPS2DQ, 0, 0 }, { MASK_SSE2, CODE_FOR_sse2_movq, 0, IX86_BUILTIN_MOVQ, 0, 0 }, /* SSE3 */ { MASK_SSE3, CODE_FOR_movshdup, 0, IX86_BUILTIN_MOVSHDUP, 0, 0 }, { MASK_SSE3, CODE_FOR_movsldup, 0, IX86_BUILTIN_MOVSLDUP, 0, 0 }, { MASK_SSE3, CODE_FOR_movddup, 0, IX86_BUILTIN_MOVDDUP, 0, 0 } }; void ix86_init_builtins (void) { if (TARGET_MMX) ix86_init_mmx_sse_builtins (); } /* Set up all the MMX/SSE builtins. This is not called if TARGET_MMX is zero. Otherwise, if TARGET_SSE is not set, only expand the MMX builtins. */ static void ix86_init_mmx_sse_builtins (void) { const struct builtin_description * d; size_t i; tree pchar_type_node = build_pointer_type (char_type_node); tree pcchar_type_node = build_pointer_type ( build_type_variant (char_type_node, 1, 0)); tree pfloat_type_node = build_pointer_type (float_type_node); tree pcfloat_type_node = build_pointer_type ( build_type_variant (float_type_node, 1, 0)); tree pv2si_type_node = build_pointer_type (V2SI_type_node); tree pv2di_type_node = build_pointer_type (V2DI_type_node); tree pdi_type_node = build_pointer_type (long_long_unsigned_type_node); /* Comparisons. */ tree int_ftype_v4sf_v4sf = build_function_type_list (integer_type_node, V4SF_type_node, V4SF_type_node, NULL_TREE); tree v4si_ftype_v4sf_v4sf = build_function_type_list (V4SI_type_node, V4SF_type_node, V4SF_type_node, NULL_TREE); /* MMX/SSE/integer conversions. */ tree int_ftype_v4sf = build_function_type_list (integer_type_node, V4SF_type_node, NULL_TREE); tree int64_ftype_v4sf = build_function_type_list (long_long_integer_type_node, V4SF_type_node, NULL_TREE); tree int_ftype_v8qi = build_function_type_list (integer_type_node, V8QI_type_node, NULL_TREE); tree v4sf_ftype_v4sf_int = build_function_type_list (V4SF_type_node, V4SF_type_node, integer_type_node, NULL_TREE); tree v4sf_ftype_v4sf_int64 = build_function_type_list (V4SF_type_node, V4SF_type_node, long_long_integer_type_node, NULL_TREE); tree v4sf_ftype_v4sf_v2si = build_function_type_list (V4SF_type_node, V4SF_type_node, V2SI_type_node, NULL_TREE); tree int_ftype_v4hi_int = build_function_type_list (integer_type_node, V4HI_type_node, integer_type_node, NULL_TREE); tree v4hi_ftype_v4hi_int_int = build_function_type_list (V4HI_type_node, V4HI_type_node, integer_type_node, integer_type_node, NULL_TREE); /* Miscellaneous. */ tree v8qi_ftype_v4hi_v4hi = build_function_type_list (V8QI_type_node, V4HI_type_node, V4HI_type_node, NULL_TREE); tree v4hi_ftype_v2si_v2si = build_function_type_list (V4HI_type_node, V2SI_type_node, V2SI_type_node, NULL_TREE); tree v4sf_ftype_v4sf_v4sf_int = build_function_type_list (V4SF_type_node, V4SF_type_node, V4SF_type_node, integer_type_node, NULL_TREE); tree v2si_ftype_v4hi_v4hi = build_function_type_list (V2SI_type_node, V4HI_type_node, V4HI_type_node, NULL_TREE); tree v4hi_ftype_v4hi_int = build_function_type_list (V4HI_type_node, V4HI_type_node, integer_type_node, NULL_TREE); tree v4hi_ftype_v4hi_di = build_function_type_list (V4HI_type_node, V4HI_type_node, long_long_unsigned_type_node, NULL_TREE); tree v2si_ftype_v2si_di = build_function_type_list (V2SI_type_node, V2SI_type_node, long_long_unsigned_type_node, NULL_TREE); tree void_ftype_void = build_function_type (void_type_node, void_list_node); tree void_ftype_unsigned = build_function_type_list (void_type_node, unsigned_type_node, NULL_TREE); tree void_ftype_unsigned_unsigned = build_function_type_list (void_type_node, unsigned_type_node, unsigned_type_node, NULL_TREE); tree void_ftype_pcvoid_unsigned_unsigned = build_function_type_list (void_type_node, const_ptr_type_node, unsigned_type_node, unsigned_type_node, NULL_TREE); tree unsigned_ftype_void = build_function_type (unsigned_type_node, void_list_node); tree di_ftype_void = build_function_type (long_long_unsigned_type_node, void_list_node); tree v4sf_ftype_void = build_function_type (V4SF_type_node, void_list_node); tree v2si_ftype_v4sf = build_function_type_list (V2SI_type_node, V4SF_type_node, NULL_TREE); /* Loads/stores. */ tree void_ftype_v8qi_v8qi_pchar = build_function_type_list (void_type_node, V8QI_type_node, V8QI_type_node, pchar_type_node, NULL_TREE); tree v4sf_ftype_pcfloat = build_function_type_list (V4SF_type_node, pcfloat_type_node, NULL_TREE); /* @@@ the type is bogus */ tree v4sf_ftype_v4sf_pv2si = build_function_type_list (V4SF_type_node, V4SF_type_node, pv2si_type_node, NULL_TREE); tree void_ftype_pv2si_v4sf = build_function_type_list (void_type_node, pv2si_type_node, V4SF_type_node, NULL_TREE); tree void_ftype_pfloat_v4sf = build_function_type_list (void_type_node, pfloat_type_node, V4SF_type_node, NULL_TREE); tree void_ftype_pdi_di = build_function_type_list (void_type_node, pdi_type_node, long_long_unsigned_type_node, NULL_TREE); tree void_ftype_pv2di_v2di = build_function_type_list (void_type_node, pv2di_type_node, V2DI_type_node, NULL_TREE); /* Normal vector unops. */ tree v4sf_ftype_v4sf = build_function_type_list (V4SF_type_node, V4SF_type_node, NULL_TREE); /* Normal vector binops. */ tree v4sf_ftype_v4sf_v4sf = build_function_type_list (V4SF_type_node, V4SF_type_node, V4SF_type_node, NULL_TREE); tree v8qi_ftype_v8qi_v8qi = build_function_type_list (V8QI_type_node, V8QI_type_node, V8QI_type_node, NULL_TREE); tree v4hi_ftype_v4hi_v4hi = build_function_type_list (V4HI_type_node, V4HI_type_node, V4HI_type_node, NULL_TREE); tree v2si_ftype_v2si_v2si = build_function_type_list (V2SI_type_node, V2SI_type_node, V2SI_type_node, NULL_TREE); tree di_ftype_di_di = build_function_type_list (long_long_unsigned_type_node, long_long_unsigned_type_node, long_long_unsigned_type_node, NULL_TREE); tree v2si_ftype_v2sf = build_function_type_list (V2SI_type_node, V2SF_type_node, NULL_TREE); tree v2sf_ftype_v2si = build_function_type_list (V2SF_type_node, V2SI_type_node, NULL_TREE); tree v2si_ftype_v2si = build_function_type_list (V2SI_type_node, V2SI_type_node, NULL_TREE); tree v2sf_ftype_v2sf = build_function_type_list (V2SF_type_node, V2SF_type_node, NULL_TREE); tree v2sf_ftype_v2sf_v2sf = build_function_type_list (V2SF_type_node, V2SF_type_node, V2SF_type_node, NULL_TREE); tree v2si_ftype_v2sf_v2sf = build_function_type_list (V2SI_type_node, V2SF_type_node, V2SF_type_node, NULL_TREE); tree pint_type_node = build_pointer_type (integer_type_node); tree pcint_type_node = build_pointer_type ( build_type_variant (integer_type_node, 1, 0)); tree pdouble_type_node = build_pointer_type (double_type_node); tree pcdouble_type_node = build_pointer_type ( build_type_variant (double_type_node, 1, 0)); tree int_ftype_v2df_v2df = build_function_type_list (integer_type_node, V2DF_type_node, V2DF_type_node, NULL_TREE); tree ti_ftype_void = build_function_type (intTI_type_node, void_list_node); tree v2di_ftype_void = build_function_type (V2DI_type_node, void_list_node); tree ti_ftype_ti_ti = build_function_type_list (intTI_type_node, intTI_type_node, intTI_type_node, NULL_TREE); tree void_ftype_pcvoid = build_function_type_list (void_type_node, const_ptr_type_node, NULL_TREE); tree v2di_ftype_di = build_function_type_list (V2DI_type_node, long_long_unsigned_type_node, NULL_TREE); tree di_ftype_v2di = build_function_type_list (long_long_unsigned_type_node, V2DI_type_node, NULL_TREE); tree v4sf_ftype_v4si = build_function_type_list (V4SF_type_node, V4SI_type_node, NULL_TREE); tree v4si_ftype_v4sf = build_function_type_list (V4SI_type_node, V4SF_type_node, NULL_TREE); tree v2df_ftype_v4si = build_function_type_list (V2DF_type_node, V4SI_type_node, NULL_TREE); tree v4si_ftype_v2df = build_function_type_list (V4SI_type_node, V2DF_type_node, NULL_TREE); tree v2si_ftype_v2df = build_function_type_list (V2SI_type_node, V2DF_type_node, NULL_TREE); tree v4sf_ftype_v2df = build_function_type_list (V4SF_type_node, V2DF_type_node, NULL_TREE); tree v2df_ftype_v2si = build_function_type_list (V2DF_type_node, V2SI_type_node, NULL_TREE); tree v2df_ftype_v4sf = build_function_type_list (V2DF_type_node, V4SF_type_node, NULL_TREE); tree int_ftype_v2df = build_function_type_list (integer_type_node, V2DF_type_node, NULL_TREE); tree int64_ftype_v2df = build_function_type_list (long_long_integer_type_node, V2DF_type_node, NULL_TREE); tree v2df_ftype_v2df_int = build_function_type_list (V2DF_type_node, V2DF_type_node, integer_type_node, NULL_TREE); tree v2df_ftype_v2df_int64 = build_function_type_list (V2DF_type_node, V2DF_type_node, long_long_integer_type_node, NULL_TREE); tree v4sf_ftype_v4sf_v2df = build_function_type_list (V4SF_type_node, V4SF_type_node, V2DF_type_node, NULL_TREE); tree v2df_ftype_v2df_v4sf = build_function_type_list (V2DF_type_node, V2DF_type_node, V4SF_type_node, NULL_TREE); tree v2df_ftype_v2df_v2df_int = build_function_type_list (V2DF_type_node, V2DF_type_node, V2DF_type_node, integer_type_node, NULL_TREE); tree v2df_ftype_v2df_pv2si = build_function_type_list (V2DF_type_node, V2DF_type_node, pv2si_type_node, NULL_TREE); tree void_ftype_pv2si_v2df = build_function_type_list (void_type_node, pv2si_type_node, V2DF_type_node, NULL_TREE); tree void_ftype_pdouble_v2df = build_function_type_list (void_type_node, pdouble_type_node, V2DF_type_node, NULL_TREE); tree void_ftype_pint_int = build_function_type_list (void_type_node, pint_type_node, integer_type_node, NULL_TREE); tree void_ftype_v16qi_v16qi_pchar = build_function_type_list (void_type_node, V16QI_type_node, V16QI_type_node, pchar_type_node, NULL_TREE); tree v2df_ftype_pcdouble = build_function_type_list (V2DF_type_node, pcdouble_type_node, NULL_TREE); tree v2df_ftype_v2df_v2df = build_function_type_list (V2DF_type_node, V2DF_type_node, V2DF_type_node, NULL_TREE); tree v16qi_ftype_v16qi_v16qi = build_function_type_list (V16QI_type_node, V16QI_type_node, V16QI_type_node, NULL_TREE); tree v8hi_ftype_v8hi_v8hi = build_function_type_list (V8HI_type_node, V8HI_type_node, V8HI_type_node, NULL_TREE); tree v4si_ftype_v4si_v4si = build_function_type_list (V4SI_type_node, V4SI_type_node, V4SI_type_node, NULL_TREE); tree v2di_ftype_v2di_v2di = build_function_type_list (V2DI_type_node, V2DI_type_node, V2DI_type_node, NULL_TREE); tree v2di_ftype_v2df_v2df = build_function_type_list (V2DI_type_node, V2DF_type_node, V2DF_type_node, NULL_TREE); tree v2df_ftype_v2df = build_function_type_list (V2DF_type_node, V2DF_type_node, NULL_TREE); tree v2df_ftype_double = build_function_type_list (V2DF_type_node, double_type_node, NULL_TREE); tree v2df_ftype_double_double = build_function_type_list (V2DF_type_node, double_type_node, double_type_node, NULL_TREE); tree int_ftype_v8hi_int = build_function_type_list (integer_type_node, V8HI_type_node, integer_type_node, NULL_TREE); tree v8hi_ftype_v8hi_int_int = build_function_type_list (V8HI_type_node, V8HI_type_node, integer_type_node, integer_type_node, NULL_TREE); tree v2di_ftype_v2di_int = build_function_type_list (V2DI_type_node, V2DI_type_node, integer_type_node, NULL_TREE); tree v4si_ftype_v4si_int = build_function_type_list (V4SI_type_node, V4SI_type_node, integer_type_node, NULL_TREE); tree v8hi_ftype_v8hi_int = build_function_type_list (V8HI_type_node, V8HI_type_node, integer_type_node, NULL_TREE); tree v8hi_ftype_v8hi_v2di = build_function_type_list (V8HI_type_node, V8HI_type_node, V2DI_type_node, NULL_TREE); tree v4si_ftype_v4si_v2di = build_function_type_list (V4SI_type_node, V4SI_type_node, V2DI_type_node, NULL_TREE); tree v4si_ftype_v8hi_v8hi = build_function_type_list (V4SI_type_node, V8HI_type_node, V8HI_type_node, NULL_TREE); tree di_ftype_v8qi_v8qi = build_function_type_list (long_long_unsigned_type_node, V8QI_type_node, V8QI_type_node, NULL_TREE); tree v2di_ftype_v16qi_v16qi = build_function_type_list (V2DI_type_node, V16QI_type_node, V16QI_type_node, NULL_TREE); tree int_ftype_v16qi = build_function_type_list (integer_type_node, V16QI_type_node, NULL_TREE); tree v16qi_ftype_pcchar = build_function_type_list (V16QI_type_node, pcchar_type_node, NULL_TREE); tree void_ftype_pchar_v16qi = build_function_type_list (void_type_node, pchar_type_node, V16QI_type_node, NULL_TREE); tree v4si_ftype_pcint = build_function_type_list (V4SI_type_node, pcint_type_node, NULL_TREE); tree void_ftype_pcint_v4si = build_function_type_list (void_type_node, pcint_type_node, V4SI_type_node, NULL_TREE); tree v2di_ftype_v2di = build_function_type_list (V2DI_type_node, V2DI_type_node, NULL_TREE); tree float80_type; tree float128_type; /* The __float80 type. */ if (TYPE_MODE (long_double_type_node) == XFmode) (*lang_hooks.types.register_builtin_type) (long_double_type_node, "__float80"); else { /* The __float80 type. */ float80_type = make_node (REAL_TYPE); TYPE_PRECISION (float80_type) = 96; layout_type (float80_type); (*lang_hooks.types.register_builtin_type) (float80_type, "__float80"); } float128_type = make_node (REAL_TYPE); TYPE_PRECISION (float128_type) = 128; layout_type (float128_type); (*lang_hooks.types.register_builtin_type) (float128_type, "__float128"); /* Add all builtins that are more or less simple operations on two operands. */ for (i = 0, d = bdesc_2arg; i < ARRAY_SIZE (bdesc_2arg); i++, d++) { /* Use one of the operands; the target can have a different mode for mask-generating compares. */ enum machine_mode mode; tree type; if (d->name == 0) continue; mode = insn_data[d->icode].operand[1].mode; switch (mode) { case V16QImode: type = v16qi_ftype_v16qi_v16qi; break; case V8HImode: type = v8hi_ftype_v8hi_v8hi; break; case V4SImode: type = v4si_ftype_v4si_v4si; break; case V2DImode: type = v2di_ftype_v2di_v2di; break; case V2DFmode: type = v2df_ftype_v2df_v2df; break; case TImode: type = ti_ftype_ti_ti; break; case V4SFmode: type = v4sf_ftype_v4sf_v4sf; break; case V8QImode: type = v8qi_ftype_v8qi_v8qi; break; case V4HImode: type = v4hi_ftype_v4hi_v4hi; break; case V2SImode: type = v2si_ftype_v2si_v2si; break; case DImode: type = di_ftype_di_di; break; default: abort (); } /* Override for comparisons. */ if (d->icode == CODE_FOR_maskcmpv4sf3 || d->icode == CODE_FOR_maskncmpv4sf3 || d->icode == CODE_FOR_vmmaskcmpv4sf3 || d->icode == CODE_FOR_vmmaskncmpv4sf3) type = v4si_ftype_v4sf_v4sf; if (d->icode == CODE_FOR_maskcmpv2df3 || d->icode == CODE_FOR_maskncmpv2df3 || d->icode == CODE_FOR_vmmaskcmpv2df3 || d->icode == CODE_FOR_vmmaskncmpv2df3) type = v2di_ftype_v2df_v2df; def_builtin (d->mask, d->name, type, d->code); } /* Add the remaining MMX insns with somewhat more complicated types. */ def_builtin (MASK_MMX, "__builtin_ia32_mmx_zero", di_ftype_void, IX86_BUILTIN_MMX_ZERO); def_builtin (MASK_MMX, "__builtin_ia32_emms", void_ftype_void, IX86_BUILTIN_EMMS); def_builtin (MASK_MMX, "__builtin_ia32_psllw", v4hi_ftype_v4hi_di, IX86_BUILTIN_PSLLW); def_builtin (MASK_MMX, "__builtin_ia32_pslld", v2si_ftype_v2si_di, IX86_BUILTIN_PSLLD); def_builtin (MASK_MMX, "__builtin_ia32_psllq", di_ftype_di_di, IX86_BUILTIN_PSLLQ); def_builtin (MASK_MMX, "__builtin_ia32_psrlw", v4hi_ftype_v4hi_di, IX86_BUILTIN_PSRLW); def_builtin (MASK_MMX, "__builtin_ia32_psrld", v2si_ftype_v2si_di, IX86_BUILTIN_PSRLD); def_builtin (MASK_MMX, "__builtin_ia32_psrlq", di_ftype_di_di, IX86_BUILTIN_PSRLQ); def_builtin (MASK_MMX, "__builtin_ia32_psraw", v4hi_ftype_v4hi_di, IX86_BUILTIN_PSRAW); def_builtin (MASK_MMX, "__builtin_ia32_psrad", v2si_ftype_v2si_di, IX86_BUILTIN_PSRAD); def_builtin (MASK_MMX, "__builtin_ia32_pshufw", v4hi_ftype_v4hi_int, IX86_BUILTIN_PSHUFW); def_builtin (MASK_MMX, "__builtin_ia32_pmaddwd", v2si_ftype_v4hi_v4hi, IX86_BUILTIN_PMADDWD); /* comi/ucomi insns. */ for (i = 0, d = bdesc_comi; i < ARRAY_SIZE (bdesc_comi); i++, d++) if (d->mask == MASK_SSE2) def_builtin (d->mask, d->name, int_ftype_v2df_v2df, d->code); else def_builtin (d->mask, d->name, int_ftype_v4sf_v4sf, d->code); def_builtin (MASK_MMX, "__builtin_ia32_packsswb", v8qi_ftype_v4hi_v4hi, IX86_BUILTIN_PACKSSWB); def_builtin (MASK_MMX, "__builtin_ia32_packssdw", v4hi_ftype_v2si_v2si, IX86_BUILTIN_PACKSSDW); def_builtin (MASK_MMX, "__builtin_ia32_packuswb", v8qi_ftype_v4hi_v4hi, IX86_BUILTIN_PACKUSWB); def_builtin (MASK_SSE, "__builtin_ia32_ldmxcsr", void_ftype_unsigned, IX86_BUILTIN_LDMXCSR); def_builtin (MASK_SSE, "__builtin_ia32_stmxcsr", unsigned_ftype_void, IX86_BUILTIN_STMXCSR); def_builtin (MASK_SSE, "__builtin_ia32_cvtpi2ps", v4sf_ftype_v4sf_v2si, IX86_BUILTIN_CVTPI2PS); def_builtin (MASK_SSE, "__builtin_ia32_cvtps2pi", v2si_ftype_v4sf, IX86_BUILTIN_CVTPS2PI); def_builtin (MASK_SSE, "__builtin_ia32_cvtsi2ss", v4sf_ftype_v4sf_int, IX86_BUILTIN_CVTSI2SS); def_builtin (MASK_SSE | MASK_64BIT, "__builtin_ia32_cvtsi642ss", v4sf_ftype_v4sf_int64, IX86_BUILTIN_CVTSI642SS); def_builtin (MASK_SSE, "__builtin_ia32_cvtss2si", int_ftype_v4sf, IX86_BUILTIN_CVTSS2SI); def_builtin (MASK_SSE | MASK_64BIT, "__builtin_ia32_cvtss2si64", int64_ftype_v4sf, IX86_BUILTIN_CVTSS2SI64); def_builtin (MASK_SSE, "__builtin_ia32_cvttps2pi", v2si_ftype_v4sf, IX86_BUILTIN_CVTTPS2PI); def_builtin (MASK_SSE, "__builtin_ia32_cvttss2si", int_ftype_v4sf, IX86_BUILTIN_CVTTSS2SI); def_builtin (MASK_SSE | MASK_64BIT, "__builtin_ia32_cvttss2si64", int64_ftype_v4sf, IX86_BUILTIN_CVTTSS2SI64); def_builtin (MASK_SSE | MASK_3DNOW_A, "__builtin_ia32_pextrw", int_ftype_v4hi_int, IX86_BUILTIN_PEXTRW); def_builtin (MASK_SSE | MASK_3DNOW_A, "__builtin_ia32_pinsrw", v4hi_ftype_v4hi_int_int, IX86_BUILTIN_PINSRW); def_builtin (MASK_SSE | MASK_3DNOW_A, "__builtin_ia32_maskmovq", void_ftype_v8qi_v8qi_pchar, IX86_BUILTIN_MASKMOVQ); def_builtin (MASK_SSE, "__builtin_ia32_loadaps", v4sf_ftype_pcfloat, IX86_BUILTIN_LOADAPS); def_builtin (MASK_SSE, "__builtin_ia32_loadups", v4sf_ftype_pcfloat, IX86_BUILTIN_LOADUPS); def_builtin (MASK_SSE, "__builtin_ia32_loadss", v4sf_ftype_pcfloat, IX86_BUILTIN_LOADSS); def_builtin (MASK_SSE, "__builtin_ia32_storeaps", void_ftype_pfloat_v4sf, IX86_BUILTIN_STOREAPS); def_builtin (MASK_SSE, "__builtin_ia32_storeups", void_ftype_pfloat_v4sf, IX86_BUILTIN_STOREUPS); def_builtin (MASK_SSE, "__builtin_ia32_storess", void_ftype_pfloat_v4sf, IX86_BUILTIN_STORESS); def_builtin (MASK_SSE, "__builtin_ia32_loadhps", v4sf_ftype_v4sf_pv2si, IX86_BUILTIN_LOADHPS); def_builtin (MASK_SSE, "__builtin_ia32_loadlps", v4sf_ftype_v4sf_pv2si, IX86_BUILTIN_LOADLPS); def_builtin (MASK_SSE, "__builtin_ia32_storehps", void_ftype_pv2si_v4sf, IX86_BUILTIN_STOREHPS); def_builtin (MASK_SSE, "__builtin_ia32_storelps", void_ftype_pv2si_v4sf, IX86_BUILTIN_STORELPS); def_builtin (MASK_SSE, "__builtin_ia32_movmskps", int_ftype_v4sf, IX86_BUILTIN_MOVMSKPS); def_builtin (MASK_SSE | MASK_3DNOW_A, "__builtin_ia32_pmovmskb", int_ftype_v8qi, IX86_BUILTIN_PMOVMSKB); def_builtin (MASK_SSE, "__builtin_ia32_movntps", void_ftype_pfloat_v4sf, IX86_BUILTIN_MOVNTPS); def_builtin (MASK_SSE | MASK_3DNOW_A, "__builtin_ia32_movntq", void_ftype_pdi_di, IX86_BUILTIN_MOVNTQ); def_builtin (MASK_SSE | MASK_3DNOW_A, "__builtin_ia32_sfence", void_ftype_void, IX86_BUILTIN_SFENCE); def_builtin (MASK_SSE | MASK_3DNOW_A, "__builtin_ia32_psadbw", di_ftype_v8qi_v8qi, IX86_BUILTIN_PSADBW); def_builtin (MASK_SSE, "__builtin_ia32_rcpps", v4sf_ftype_v4sf, IX86_BUILTIN_RCPPS); def_builtin (MASK_SSE, "__builtin_ia32_rcpss", v4sf_ftype_v4sf, IX86_BUILTIN_RCPSS); def_builtin (MASK_SSE, "__builtin_ia32_rsqrtps", v4sf_ftype_v4sf, IX86_BUILTIN_RSQRTPS); def_builtin (MASK_SSE, "__builtin_ia32_rsqrtss", v4sf_ftype_v4sf, IX86_BUILTIN_RSQRTSS); def_builtin (MASK_SSE, "__builtin_ia32_sqrtps", v4sf_ftype_v4sf, IX86_BUILTIN_SQRTPS); def_builtin (MASK_SSE, "__builtin_ia32_sqrtss", v4sf_ftype_v4sf, IX86_BUILTIN_SQRTSS); def_builtin (MASK_SSE, "__builtin_ia32_shufps", v4sf_ftype_v4sf_v4sf_int, IX86_BUILTIN_SHUFPS); /* Original 3DNow! */ def_builtin (MASK_3DNOW, "__builtin_ia32_femms", void_ftype_void, IX86_BUILTIN_FEMMS); def_builtin (MASK_3DNOW, "__builtin_ia32_pavgusb", v8qi_ftype_v8qi_v8qi, IX86_BUILTIN_PAVGUSB); def_builtin (MASK_3DNOW, "__builtin_ia32_pf2id", v2si_ftype_v2sf, IX86_BUILTIN_PF2ID); def_builtin (MASK_3DNOW, "__builtin_ia32_pfacc", v2sf_ftype_v2sf_v2sf, IX86_BUILTIN_PFACC); def_builtin (MASK_3DNOW, "__builtin_ia32_pfadd", v2sf_ftype_v2sf_v2sf, IX86_BUILTIN_PFADD); def_builtin (MASK_3DNOW, "__builtin_ia32_pfcmpeq", v2si_ftype_v2sf_v2sf, IX86_BUILTIN_PFCMPEQ); def_builtin (MASK_3DNOW, "__builtin_ia32_pfcmpge", v2si_ftype_v2sf_v2sf, IX86_BUILTIN_PFCMPGE); def_builtin (MASK_3DNOW, "__builtin_ia32_pfcmpgt", v2si_ftype_v2sf_v2sf, IX86_BUILTIN_PFCMPGT); def_builtin (MASK_3DNOW, "__builtin_ia32_pfmax", v2sf_ftype_v2sf_v2sf, IX86_BUILTIN_PFMAX); def_builtin (MASK_3DNOW, "__builtin_ia32_pfmin", v2sf_ftype_v2sf_v2sf, IX86_BUILTIN_PFMIN); def_builtin (MASK_3DNOW, "__builtin_ia32_pfmul", v2sf_ftype_v2sf_v2sf, IX86_BUILTIN_PFMUL); def_builtin (MASK_3DNOW, "__builtin_ia32_pfrcp", v2sf_ftype_v2sf, IX86_BUILTIN_PFRCP); def_builtin (MASK_3DNOW, "__builtin_ia32_pfrcpit1", v2sf_ftype_v2sf_v2sf, IX86_BUILTIN_PFRCPIT1); def_builtin (MASK_3DNOW, "__builtin_ia32_pfrcpit2", v2sf_ftype_v2sf_v2sf, IX86_BUILTIN_PFRCPIT2); def_builtin (MASK_3DNOW, "__builtin_ia32_pfrsqrt", v2sf_ftype_v2sf, IX86_BUILTIN_PFRSQRT); def_builtin (MASK_3DNOW, "__builtin_ia32_pfrsqit1", v2sf_ftype_v2sf_v2sf, IX86_BUILTIN_PFRSQIT1); def_builtin (MASK_3DNOW, "__builtin_ia32_pfsub", v2sf_ftype_v2sf_v2sf, IX86_BUILTIN_PFSUB); def_builtin (MASK_3DNOW, "__builtin_ia32_pfsubr", v2sf_ftype_v2sf_v2sf, IX86_BUILTIN_PFSUBR); def_builtin (MASK_3DNOW, "__builtin_ia32_pi2fd", v2sf_ftype_v2si, IX86_BUILTIN_PI2FD); def_builtin (MASK_3DNOW, "__builtin_ia32_pmulhrw", v4hi_ftype_v4hi_v4hi, IX86_BUILTIN_PMULHRW); /* 3DNow! extension as used in the Athlon CPU. */ def_builtin (MASK_3DNOW_A, "__builtin_ia32_pf2iw", v2si_ftype_v2sf, IX86_BUILTIN_PF2IW); def_builtin (MASK_3DNOW_A, "__builtin_ia32_pfnacc", v2sf_ftype_v2sf_v2sf, IX86_BUILTIN_PFNACC); def_builtin (MASK_3DNOW_A, "__builtin_ia32_pfpnacc", v2sf_ftype_v2sf_v2sf, IX86_BUILTIN_PFPNACC); def_builtin (MASK_3DNOW_A, "__builtin_ia32_pi2fw", v2sf_ftype_v2si, IX86_BUILTIN_PI2FW); def_builtin (MASK_3DNOW_A, "__builtin_ia32_pswapdsf", v2sf_ftype_v2sf, IX86_BUILTIN_PSWAPDSF); def_builtin (MASK_3DNOW_A, "__builtin_ia32_pswapdsi", v2si_ftype_v2si, IX86_BUILTIN_PSWAPDSI); def_builtin (MASK_SSE, "__builtin_ia32_setzerops", v4sf_ftype_void, IX86_BUILTIN_SSE_ZERO); /* SSE2 */ def_builtin (MASK_SSE2, "__builtin_ia32_pextrw128", int_ftype_v8hi_int, IX86_BUILTIN_PEXTRW128); def_builtin (MASK_SSE2, "__builtin_ia32_pinsrw128", v8hi_ftype_v8hi_int_int, IX86_BUILTIN_PINSRW128); def_builtin (MASK_SSE2, "__builtin_ia32_maskmovdqu", void_ftype_v16qi_v16qi_pchar, IX86_BUILTIN_MASKMOVDQU); def_builtin (MASK_SSE2, "__builtin_ia32_movq2dq", v2di_ftype_di, IX86_BUILTIN_MOVQ2DQ); def_builtin (MASK_SSE2, "__builtin_ia32_movdq2q", di_ftype_v2di, IX86_BUILTIN_MOVDQ2Q); def_builtin (MASK_SSE2, "__builtin_ia32_loadapd", v2df_ftype_pcdouble, IX86_BUILTIN_LOADAPD); def_builtin (MASK_SSE2, "__builtin_ia32_loadupd", v2df_ftype_pcdouble, IX86_BUILTIN_LOADUPD); def_builtin (MASK_SSE2, "__builtin_ia32_loadsd", v2df_ftype_pcdouble, IX86_BUILTIN_LOADSD); def_builtin (MASK_SSE2, "__builtin_ia32_storeapd", void_ftype_pdouble_v2df, IX86_BUILTIN_STOREAPD); def_builtin (MASK_SSE2, "__builtin_ia32_storeupd", void_ftype_pdouble_v2df, IX86_BUILTIN_STOREUPD); def_builtin (MASK_SSE2, "__builtin_ia32_storesd", void_ftype_pdouble_v2df, IX86_BUILTIN_STORESD); def_builtin (MASK_SSE2, "__builtin_ia32_loadhpd", v2df_ftype_v2df_pv2si, IX86_BUILTIN_LOADHPD); def_builtin (MASK_SSE2, "__builtin_ia32_loadlpd", v2df_ftype_v2df_pv2si, IX86_BUILTIN_LOADLPD); def_builtin (MASK_SSE2, "__builtin_ia32_storehpd", void_ftype_pv2si_v2df, IX86_BUILTIN_STOREHPD); def_builtin (MASK_SSE2, "__builtin_ia32_storelpd", void_ftype_pv2si_v2df, IX86_BUILTIN_STORELPD); def_builtin (MASK_SSE2, "__builtin_ia32_movmskpd", int_ftype_v2df, IX86_BUILTIN_MOVMSKPD); def_builtin (MASK_SSE2, "__builtin_ia32_pmovmskb128", int_ftype_v16qi, IX86_BUILTIN_PMOVMSKB128); def_builtin (MASK_SSE2, "__builtin_ia32_movnti", void_ftype_pint_int, IX86_BUILTIN_MOVNTI); def_builtin (MASK_SSE2, "__builtin_ia32_movntpd", void_ftype_pdouble_v2df, IX86_BUILTIN_MOVNTPD); def_builtin (MASK_SSE2, "__builtin_ia32_movntdq", void_ftype_pv2di_v2di, IX86_BUILTIN_MOVNTDQ); def_builtin (MASK_SSE2, "__builtin_ia32_pshufd", v4si_ftype_v4si_int, IX86_BUILTIN_PSHUFD); def_builtin (MASK_SSE2, "__builtin_ia32_pshuflw", v8hi_ftype_v8hi_int, IX86_BUILTIN_PSHUFLW); def_builtin (MASK_SSE2, "__builtin_ia32_pshufhw", v8hi_ftype_v8hi_int, IX86_BUILTIN_PSHUFHW); def_builtin (MASK_SSE2, "__builtin_ia32_psadbw128", v2di_ftype_v16qi_v16qi, IX86_BUILTIN_PSADBW128); def_builtin (MASK_SSE2, "__builtin_ia32_sqrtpd", v2df_ftype_v2df, IX86_BUILTIN_SQRTPD); def_builtin (MASK_SSE2, "__builtin_ia32_sqrtsd", v2df_ftype_v2df, IX86_BUILTIN_SQRTSD); def_builtin (MASK_SSE2, "__builtin_ia32_shufpd", v2df_ftype_v2df_v2df_int, IX86_BUILTIN_SHUFPD); def_builtin (MASK_SSE2, "__builtin_ia32_cvtdq2pd", v2df_ftype_v4si, IX86_BUILTIN_CVTDQ2PD); def_builtin (MASK_SSE2, "__builtin_ia32_cvtdq2ps", v4sf_ftype_v4si, IX86_BUILTIN_CVTDQ2PS); def_builtin (MASK_SSE2, "__builtin_ia32_cvtpd2dq", v4si_ftype_v2df, IX86_BUILTIN_CVTPD2DQ); def_builtin (MASK_SSE2, "__builtin_ia32_cvtpd2pi", v2si_ftype_v2df, IX86_BUILTIN_CVTPD2PI); def_builtin (MASK_SSE2, "__builtin_ia32_cvtpd2ps", v4sf_ftype_v2df, IX86_BUILTIN_CVTPD2PS); def_builtin (MASK_SSE2, "__builtin_ia32_cvttpd2dq", v4si_ftype_v2df, IX86_BUILTIN_CVTTPD2DQ); def_builtin (MASK_SSE2, "__builtin_ia32_cvttpd2pi", v2si_ftype_v2df, IX86_BUILTIN_CVTTPD2PI); def_builtin (MASK_SSE2, "__builtin_ia32_cvtpi2pd", v2df_ftype_v2si, IX86_BUILTIN_CVTPI2PD); def_builtin (MASK_SSE2, "__builtin_ia32_cvtsd2si", int_ftype_v2df, IX86_BUILTIN_CVTSD2SI); def_builtin (MASK_SSE2, "__builtin_ia32_cvttsd2si", int_ftype_v2df, IX86_BUILTIN_CVTTSD2SI); def_builtin (MASK_SSE2 | MASK_64BIT, "__builtin_ia32_cvtsd2si64", int64_ftype_v2df, IX86_BUILTIN_CVTSD2SI64); def_builtin (MASK_SSE2 | MASK_64BIT, "__builtin_ia32_cvttsd2si64", int64_ftype_v2df, IX86_BUILTIN_CVTTSD2SI64); def_builtin (MASK_SSE2, "__builtin_ia32_cvtps2dq", v4si_ftype_v4sf, IX86_BUILTIN_CVTPS2DQ); def_builtin (MASK_SSE2, "__builtin_ia32_cvtps2pd", v2df_ftype_v4sf, IX86_BUILTIN_CVTPS2PD); def_builtin (MASK_SSE2, "__builtin_ia32_cvttps2dq", v4si_ftype_v4sf, IX86_BUILTIN_CVTTPS2DQ); def_builtin (MASK_SSE2, "__builtin_ia32_cvtsi2sd", v2df_ftype_v2df_int, IX86_BUILTIN_CVTSI2SD); def_builtin (MASK_SSE2 | MASK_64BIT, "__builtin_ia32_cvtsi642sd", v2df_ftype_v2df_int64, IX86_BUILTIN_CVTSI642SD); def_builtin (MASK_SSE2, "__builtin_ia32_cvtsd2ss", v4sf_ftype_v4sf_v2df, IX86_BUILTIN_CVTSD2SS); def_builtin (MASK_SSE2, "__builtin_ia32_cvtss2sd", v2df_ftype_v2df_v4sf, IX86_BUILTIN_CVTSS2SD); def_builtin (MASK_SSE2, "__builtin_ia32_setpd1", v2df_ftype_double, IX86_BUILTIN_SETPD1); def_builtin (MASK_SSE2, "__builtin_ia32_setpd", v2df_ftype_double_double, IX86_BUILTIN_SETPD); def_builtin (MASK_SSE2, "__builtin_ia32_setzeropd", ti_ftype_void, IX86_BUILTIN_CLRPD); def_builtin (MASK_SSE2, "__builtin_ia32_loadpd1", v2df_ftype_pcdouble, IX86_BUILTIN_LOADPD1); def_builtin (MASK_SSE2, "__builtin_ia32_loadrpd", v2df_ftype_pcdouble, IX86_BUILTIN_LOADRPD); def_builtin (MASK_SSE2, "__builtin_ia32_storepd1", void_ftype_pdouble_v2df, IX86_BUILTIN_STOREPD1); def_builtin (MASK_SSE2, "__builtin_ia32_storerpd", void_ftype_pdouble_v2df, IX86_BUILTIN_STORERPD); def_builtin (MASK_SSE2, "__builtin_ia32_clflush", void_ftype_pcvoid, IX86_BUILTIN_CLFLUSH); def_builtin (MASK_SSE2, "__builtin_ia32_lfence", void_ftype_void, IX86_BUILTIN_LFENCE); def_builtin (MASK_SSE2, "__builtin_ia32_mfence", void_ftype_void, IX86_BUILTIN_MFENCE); def_builtin (MASK_SSE2, "__builtin_ia32_loaddqa", v16qi_ftype_pcchar, IX86_BUILTIN_LOADDQA); def_builtin (MASK_SSE2, "__builtin_ia32_loaddqu", v16qi_ftype_pcchar, IX86_BUILTIN_LOADDQU); def_builtin (MASK_SSE2, "__builtin_ia32_loadd", v4si_ftype_pcint, IX86_BUILTIN_LOADD); def_builtin (MASK_SSE2, "__builtin_ia32_storedqa", void_ftype_pchar_v16qi, IX86_BUILTIN_STOREDQA); def_builtin (MASK_SSE2, "__builtin_ia32_storedqu", void_ftype_pchar_v16qi, IX86_BUILTIN_STOREDQU); def_builtin (MASK_SSE2, "__builtin_ia32_stored", void_ftype_pcint_v4si, IX86_BUILTIN_STORED); def_builtin (MASK_SSE2, "__builtin_ia32_movq", v2di_ftype_v2di, IX86_BUILTIN_MOVQ); def_builtin (MASK_SSE, "__builtin_ia32_setzero128", v2di_ftype_void, IX86_BUILTIN_CLRTI); def_builtin (MASK_SSE2, "__builtin_ia32_psllw128", v8hi_ftype_v8hi_v2di, IX86_BUILTIN_PSLLW128); def_builtin (MASK_SSE2, "__builtin_ia32_pslld128", v4si_ftype_v4si_v2di, IX86_BUILTIN_PSLLD128); def_builtin (MASK_SSE2, "__builtin_ia32_psllq128", v2di_ftype_v2di_v2di, IX86_BUILTIN_PSLLQ128); def_builtin (MASK_SSE2, "__builtin_ia32_psrlw128", v8hi_ftype_v8hi_v2di, IX86_BUILTIN_PSRLW128); def_builtin (MASK_SSE2, "__builtin_ia32_psrld128", v4si_ftype_v4si_v2di, IX86_BUILTIN_PSRLD128); def_builtin (MASK_SSE2, "__builtin_ia32_psrlq128", v2di_ftype_v2di_v2di, IX86_BUILTIN_PSRLQ128); def_builtin (MASK_SSE2, "__builtin_ia32_psraw128", v8hi_ftype_v8hi_v2di, IX86_BUILTIN_PSRAW128); def_builtin (MASK_SSE2, "__builtin_ia32_psrad128", v4si_ftype_v4si_v2di, IX86_BUILTIN_PSRAD128); def_builtin (MASK_SSE2, "__builtin_ia32_pslldqi128", v2di_ftype_v2di_int, IX86_BUILTIN_PSLLDQI128); def_builtin (MASK_SSE2, "__builtin_ia32_psllwi128", v8hi_ftype_v8hi_int, IX86_BUILTIN_PSLLWI128); def_builtin (MASK_SSE2, "__builtin_ia32_pslldi128", v4si_ftype_v4si_int, IX86_BUILTIN_PSLLDI128); def_builtin (MASK_SSE2, "__builtin_ia32_psllqi128", v2di_ftype_v2di_int, IX86_BUILTIN_PSLLQI128); def_builtin (MASK_SSE2, "__builtin_ia32_psrldqi128", v2di_ftype_v2di_int, IX86_BUILTIN_PSRLDQI128); def_builtin (MASK_SSE2, "__builtin_ia32_psrlwi128", v8hi_ftype_v8hi_int, IX86_BUILTIN_PSRLWI128); def_builtin (MASK_SSE2, "__builtin_ia32_psrldi128", v4si_ftype_v4si_int, IX86_BUILTIN_PSRLDI128); def_builtin (MASK_SSE2, "__builtin_ia32_psrlqi128", v2di_ftype_v2di_int, IX86_BUILTIN_PSRLQI128); def_builtin (MASK_SSE2, "__builtin_ia32_psrawi128", v8hi_ftype_v8hi_int, IX86_BUILTIN_PSRAWI128); def_builtin (MASK_SSE2, "__builtin_ia32_psradi128", v4si_ftype_v4si_int, IX86_BUILTIN_PSRADI128); def_builtin (MASK_SSE2, "__builtin_ia32_pmaddwd128", v4si_ftype_v8hi_v8hi, IX86_BUILTIN_PMADDWD128); /* Prescott New Instructions. */ def_builtin (MASK_SSE3, "__builtin_ia32_monitor", void_ftype_pcvoid_unsigned_unsigned, IX86_BUILTIN_MONITOR); def_builtin (MASK_SSE3, "__builtin_ia32_mwait", void_ftype_unsigned_unsigned, IX86_BUILTIN_MWAIT); def_builtin (MASK_SSE3, "__builtin_ia32_movshdup", v4sf_ftype_v4sf, IX86_BUILTIN_MOVSHDUP); def_builtin (MASK_SSE3, "__builtin_ia32_movsldup", v4sf_ftype_v4sf, IX86_BUILTIN_MOVSLDUP); def_builtin (MASK_SSE3, "__builtin_ia32_lddqu", v16qi_ftype_pcchar, IX86_BUILTIN_LDDQU); def_builtin (MASK_SSE3, "__builtin_ia32_loadddup", v2df_ftype_pcdouble, IX86_BUILTIN_LOADDDUP); def_builtin (MASK_SSE3, "__builtin_ia32_movddup", v2df_ftype_v2df, IX86_BUILTIN_MOVDDUP); } /* Errors in the source file can cause expand_expr to return const0_rtx where we expect a vector. To avoid crashing, use one of the vector clear instructions. */ static rtx safe_vector_operand (rtx x, enum machine_mode mode) { if (x != const0_rtx) return x; x = gen_reg_rtx (mode); if (VALID_MMX_REG_MODE (mode) || VALID_MMX_REG_MODE_3DNOW (mode)) emit_insn (gen_mmx_clrdi (mode == DImode ? x : gen_rtx_SUBREG (DImode, x, 0))); else emit_insn (gen_sse_clrv4sf (mode == V4SFmode ? x : gen_rtx_SUBREG (V4SFmode, x, 0), CONST0_RTX (V4SFmode))); return x; } /* Subroutine of ix86_expand_builtin to take care of binop insns. */ static rtx ix86_expand_binop_builtin (enum insn_code icode, tree arglist, rtx target) { rtx pat; tree arg0 = TREE_VALUE (arglist); tree arg1 = TREE_VALUE (TREE_CHAIN (arglist)); rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); rtx op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); enum machine_mode tmode = insn_data[icode].operand[0].mode; enum machine_mode mode0 = insn_data[icode].operand[1].mode; enum machine_mode mode1 = insn_data[icode].operand[2].mode; if (VECTOR_MODE_P (mode0)) op0 = safe_vector_operand (op0, mode0); if (VECTOR_MODE_P (mode1)) op1 = safe_vector_operand (op1, mode1); if (! target || GET_MODE (target) != tmode || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) target = gen_reg_rtx (tmode); if (GET_MODE (op1) == SImode && mode1 == TImode) { rtx x = gen_reg_rtx (V4SImode); emit_insn (gen_sse2_loadd (x, op1)); op1 = gen_lowpart (TImode, x); } /* In case the insn wants input operands in modes different from the result, abort. */ if ((GET_MODE (op0) != mode0 && GET_MODE (op0) != VOIDmode) || (GET_MODE (op1) != mode1 && GET_MODE (op1) != VOIDmode)) abort (); if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) op0 = copy_to_mode_reg (mode0, op0); if (! (*insn_data[icode].operand[2].predicate) (op1, mode1)) op1 = copy_to_mode_reg (mode1, op1); /* In the commutative cases, both op0 and op1 are nonimmediate_operand, yet one of the two must not be a memory. This is normally enforced by expanders, but we didn't bother to create one here. */ if (GET_CODE (op0) == MEM && GET_CODE (op1) == MEM) op0 = copy_to_mode_reg (mode0, op0); pat = GEN_FCN (icode) (target, op0, op1); if (! pat) return 0; emit_insn (pat); return target; } /* Subroutine of ix86_expand_builtin to take care of stores. */ static rtx ix86_expand_store_builtin (enum insn_code icode, tree arglist) { rtx pat; tree arg0 = TREE_VALUE (arglist); tree arg1 = TREE_VALUE (TREE_CHAIN (arglist)); rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); rtx op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); enum machine_mode mode0 = insn_data[icode].operand[0].mode; enum machine_mode mode1 = insn_data[icode].operand[1].mode; if (VECTOR_MODE_P (mode1)) op1 = safe_vector_operand (op1, mode1); op0 = gen_rtx_MEM (mode0, copy_to_mode_reg (Pmode, op0)); op1 = copy_to_mode_reg (mode1, op1); pat = GEN_FCN (icode) (op0, op1); if (pat) emit_insn (pat); return 0; } /* Subroutine of ix86_expand_builtin to take care of unop insns. */ static rtx ix86_expand_unop_builtin (enum insn_code icode, tree arglist, rtx target, int do_load) { rtx pat; tree arg0 = TREE_VALUE (arglist); rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); enum machine_mode tmode = insn_data[icode].operand[0].mode; enum machine_mode mode0 = insn_data[icode].operand[1].mode; if (! target || GET_MODE (target) != tmode + || (do_load && GET_CODE (target) == MEM) || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) target = gen_reg_rtx (tmode); if (do_load) op0 = gen_rtx_MEM (mode0, copy_to_mode_reg (Pmode, op0)); else { if (VECTOR_MODE_P (mode0)) op0 = safe_vector_operand (op0, mode0); if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) op0 = copy_to_mode_reg (mode0, op0); } pat = GEN_FCN (icode) (target, op0); if (! pat) return 0; emit_insn (pat); return target; } /* Subroutine of ix86_expand_builtin to take care of three special unop insns: sqrtss, rsqrtss, rcpss. */ static rtx ix86_expand_unop1_builtin (enum insn_code icode, tree arglist, rtx target) { rtx pat; tree arg0 = TREE_VALUE (arglist); rtx op1, op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); enum machine_mode tmode = insn_data[icode].operand[0].mode; enum machine_mode mode0 = insn_data[icode].operand[1].mode; if (! target || GET_MODE (target) != tmode || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) target = gen_reg_rtx (tmode); if (VECTOR_MODE_P (mode0)) op0 = safe_vector_operand (op0, mode0); if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) op0 = copy_to_mode_reg (mode0, op0); op1 = op0; if (! (*insn_data[icode].operand[2].predicate) (op1, mode0)) op1 = copy_to_mode_reg (mode0, op1); pat = GEN_FCN (icode) (target, op0, op1); if (! pat) return 0; emit_insn (pat); return target; } /* Subroutine of ix86_expand_builtin to take care of comparison insns. */ static rtx ix86_expand_sse_compare (const struct builtin_description *d, tree arglist, rtx target) { rtx pat; tree arg0 = TREE_VALUE (arglist); tree arg1 = TREE_VALUE (TREE_CHAIN (arglist)); rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); rtx op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); rtx op2; enum machine_mode tmode = insn_data[d->icode].operand[0].mode; enum machine_mode mode0 = insn_data[d->icode].operand[1].mode; enum machine_mode mode1 = insn_data[d->icode].operand[2].mode; enum rtx_code comparison = d->comparison; if (VECTOR_MODE_P (mode0)) op0 = safe_vector_operand (op0, mode0); if (VECTOR_MODE_P (mode1)) op1 = safe_vector_operand (op1, mode1); /* Swap operands if we have a comparison that isn't available in hardware. */ if (d->flag) { rtx tmp = gen_reg_rtx (mode1); emit_move_insn (tmp, op1); op1 = op0; op0 = tmp; } if (! target || GET_MODE (target) != tmode || ! (*insn_data[d->icode].operand[0].predicate) (target, tmode)) target = gen_reg_rtx (tmode); if (! (*insn_data[d->icode].operand[1].predicate) (op0, mode0)) op0 = copy_to_mode_reg (mode0, op0); if (! (*insn_data[d->icode].operand[2].predicate) (op1, mode1)) op1 = copy_to_mode_reg (mode1, op1); op2 = gen_rtx_fmt_ee (comparison, mode0, op0, op1); pat = GEN_FCN (d->icode) (target, op0, op1, op2); if (! pat) return 0; emit_insn (pat); return target; } /* Subroutine of ix86_expand_builtin to take care of comi insns. */ static rtx ix86_expand_sse_comi (const struct builtin_description *d, tree arglist, rtx target) { rtx pat; tree arg0 = TREE_VALUE (arglist); tree arg1 = TREE_VALUE (TREE_CHAIN (arglist)); rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); rtx op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); rtx op2; enum machine_mode mode0 = insn_data[d->icode].operand[0].mode; enum machine_mode mode1 = insn_data[d->icode].operand[1].mode; enum rtx_code comparison = d->comparison; if (VECTOR_MODE_P (mode0)) op0 = safe_vector_operand (op0, mode0); if (VECTOR_MODE_P (mode1)) op1 = safe_vector_operand (op1, mode1); /* Swap operands if we have a comparison that isn't available in hardware. */ if (d->flag) { rtx tmp = op1; op1 = op0; op0 = tmp; } target = gen_reg_rtx (SImode); emit_move_insn (target, const0_rtx); target = gen_rtx_SUBREG (QImode, target, 0); if (! (*insn_data[d->icode].operand[0].predicate) (op0, mode0)) op0 = copy_to_mode_reg (mode0, op0); if (! (*insn_data[d->icode].operand[1].predicate) (op1, mode1)) op1 = copy_to_mode_reg (mode1, op1); op2 = gen_rtx_fmt_ee (comparison, mode0, op0, op1); pat = GEN_FCN (d->icode) (op0, op1); if (! pat) return 0; emit_insn (pat); emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_STRICT_LOW_PART (VOIDmode, target), gen_rtx_fmt_ee (comparison, QImode, SET_DEST (pat), const0_rtx))); return SUBREG_REG (target); } /* Expand an expression EXP that calls a built-in function, with result going to TARGET if that's convenient (and in mode MODE if that's convenient). SUBTARGET may be used as the target for computing one of EXP's operands. IGNORE is nonzero if the value is to be ignored. */ rtx ix86_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED, enum machine_mode mode ATTRIBUTE_UNUSED, int ignore ATTRIBUTE_UNUSED) { const struct builtin_description *d; size_t i; enum insn_code icode; tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); tree arglist = TREE_OPERAND (exp, 1); tree arg0, arg1, arg2; rtx op0, op1, op2, pat; enum machine_mode tmode, mode0, mode1, mode2; unsigned int fcode = DECL_FUNCTION_CODE (fndecl); switch (fcode) { case IX86_BUILTIN_EMMS: emit_insn (gen_emms ()); return 0; case IX86_BUILTIN_SFENCE: emit_insn (gen_sfence ()); return 0; case IX86_BUILTIN_PEXTRW: case IX86_BUILTIN_PEXTRW128: icode = (fcode == IX86_BUILTIN_PEXTRW ? CODE_FOR_mmx_pextrw : CODE_FOR_sse2_pextrw); arg0 = TREE_VALUE (arglist); arg1 = TREE_VALUE (TREE_CHAIN (arglist)); op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); tmode = insn_data[icode].operand[0].mode; mode0 = insn_data[icode].operand[1].mode; mode1 = insn_data[icode].operand[2].mode; if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) op0 = copy_to_mode_reg (mode0, op0); if (! (*insn_data[icode].operand[2].predicate) (op1, mode1)) { error ("selector must be an integer constant in the range 0..%i", fcode == IX86_BUILTIN_PEXTRW ? 3:7); return gen_reg_rtx (tmode); } if (target == 0 || GET_MODE (target) != tmode || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) target = gen_reg_rtx (tmode); pat = GEN_FCN (icode) (target, op0, op1); if (! pat) return 0; emit_insn (pat); return target; case IX86_BUILTIN_PINSRW: case IX86_BUILTIN_PINSRW128: icode = (fcode == IX86_BUILTIN_PINSRW ? CODE_FOR_mmx_pinsrw : CODE_FOR_sse2_pinsrw); arg0 = TREE_VALUE (arglist); arg1 = TREE_VALUE (TREE_CHAIN (arglist)); arg2 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); op2 = expand_expr (arg2, NULL_RTX, VOIDmode, 0); tmode = insn_data[icode].operand[0].mode; mode0 = insn_data[icode].operand[1].mode; mode1 = insn_data[icode].operand[2].mode; mode2 = insn_data[icode].operand[3].mode; if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) op0 = copy_to_mode_reg (mode0, op0); if (! (*insn_data[icode].operand[2].predicate) (op1, mode1)) op1 = copy_to_mode_reg (mode1, op1); if (! (*insn_data[icode].operand[3].predicate) (op2, mode2)) { error ("selector must be an integer constant in the range 0..%i", fcode == IX86_BUILTIN_PINSRW ? 15:255); return const0_rtx; } if (target == 0 || GET_MODE (target) != tmode || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) target = gen_reg_rtx (tmode); pat = GEN_FCN (icode) (target, op0, op1, op2); if (! pat) return 0; emit_insn (pat); return target; case IX86_BUILTIN_MASKMOVQ: case IX86_BUILTIN_MASKMOVDQU: icode = (fcode == IX86_BUILTIN_MASKMOVQ ? (TARGET_64BIT ? CODE_FOR_mmx_maskmovq_rex : CODE_FOR_mmx_maskmovq) : (TARGET_64BIT ? CODE_FOR_sse2_maskmovdqu_rex64 : CODE_FOR_sse2_maskmovdqu)); /* Note the arg order is different from the operand order. */ arg1 = TREE_VALUE (arglist); arg2 = TREE_VALUE (TREE_CHAIN (arglist)); arg0 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); op2 = expand_expr (arg2, NULL_RTX, VOIDmode, 0); mode0 = insn_data[icode].operand[0].mode; mode1 = insn_data[icode].operand[1].mode; mode2 = insn_data[icode].operand[2].mode; if (! (*insn_data[icode].operand[0].predicate) (op0, mode0)) op0 = copy_to_mode_reg (mode0, op0); if (! (*insn_data[icode].operand[1].predicate) (op1, mode1)) op1 = copy_to_mode_reg (mode1, op1); if (! (*insn_data[icode].operand[2].predicate) (op2, mode2)) op2 = copy_to_mode_reg (mode2, op2); pat = GEN_FCN (icode) (op0, op1, op2); if (! pat) return 0; emit_insn (pat); return 0; case IX86_BUILTIN_SQRTSS: return ix86_expand_unop1_builtin (CODE_FOR_vmsqrtv4sf2, arglist, target); case IX86_BUILTIN_RSQRTSS: return ix86_expand_unop1_builtin (CODE_FOR_vmrsqrtv4sf2, arglist, target); case IX86_BUILTIN_RCPSS: return ix86_expand_unop1_builtin (CODE_FOR_vmrcpv4sf2, arglist, target); case IX86_BUILTIN_LOADAPS: return ix86_expand_unop_builtin (CODE_FOR_sse_movaps, arglist, target, 1); case IX86_BUILTIN_LOADUPS: return ix86_expand_unop_builtin (CODE_FOR_sse_movups, arglist, target, 1); case IX86_BUILTIN_STOREAPS: return ix86_expand_store_builtin (CODE_FOR_sse_movaps, arglist); case IX86_BUILTIN_STOREUPS: return ix86_expand_store_builtin (CODE_FOR_sse_movups, arglist); case IX86_BUILTIN_LOADSS: return ix86_expand_unop_builtin (CODE_FOR_sse_loadss, arglist, target, 1); case IX86_BUILTIN_STORESS: return ix86_expand_store_builtin (CODE_FOR_sse_storess, arglist); case IX86_BUILTIN_LOADHPS: case IX86_BUILTIN_LOADLPS: case IX86_BUILTIN_LOADHPD: case IX86_BUILTIN_LOADLPD: icode = (fcode == IX86_BUILTIN_LOADHPS ? CODE_FOR_sse_movhps : fcode == IX86_BUILTIN_LOADLPS ? CODE_FOR_sse_movlps : fcode == IX86_BUILTIN_LOADHPD ? CODE_FOR_sse2_movhpd : CODE_FOR_sse2_movsd); arg0 = TREE_VALUE (arglist); arg1 = TREE_VALUE (TREE_CHAIN (arglist)); op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); tmode = insn_data[icode].operand[0].mode; mode0 = insn_data[icode].operand[1].mode; mode1 = insn_data[icode].operand[2].mode; if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) op0 = copy_to_mode_reg (mode0, op0); op1 = gen_rtx_MEM (mode1, copy_to_mode_reg (Pmode, op1)); if (target == 0 || GET_MODE (target) != tmode || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) target = gen_reg_rtx (tmode); pat = GEN_FCN (icode) (target, op0, op1); if (! pat) return 0; emit_insn (pat); return target; case IX86_BUILTIN_STOREHPS: case IX86_BUILTIN_STORELPS: case IX86_BUILTIN_STOREHPD: case IX86_BUILTIN_STORELPD: icode = (fcode == IX86_BUILTIN_STOREHPS ? CODE_FOR_sse_movhps : fcode == IX86_BUILTIN_STORELPS ? CODE_FOR_sse_movlps : fcode == IX86_BUILTIN_STOREHPD ? CODE_FOR_sse2_movhpd : CODE_FOR_sse2_movsd); arg0 = TREE_VALUE (arglist); arg1 = TREE_VALUE (TREE_CHAIN (arglist)); op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); mode0 = insn_data[icode].operand[1].mode; mode1 = insn_data[icode].operand[2].mode; op0 = gen_rtx_MEM (mode0, copy_to_mode_reg (Pmode, op0)); if (! (*insn_data[icode].operand[2].predicate) (op1, mode1)) op1 = copy_to_mode_reg (mode1, op1); pat = GEN_FCN (icode) (op0, op0, op1); if (! pat) return 0; emit_insn (pat); return 0; case IX86_BUILTIN_MOVNTPS: return ix86_expand_store_builtin (CODE_FOR_sse_movntv4sf, arglist); case IX86_BUILTIN_MOVNTQ: return ix86_expand_store_builtin (CODE_FOR_sse_movntdi, arglist); case IX86_BUILTIN_LDMXCSR: op0 = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0); target = assign_386_stack_local (SImode, 0); emit_move_insn (target, op0); emit_insn (gen_ldmxcsr (target)); return 0; case IX86_BUILTIN_STMXCSR: target = assign_386_stack_local (SImode, 0); emit_insn (gen_stmxcsr (target)); return copy_to_mode_reg (SImode, target); case IX86_BUILTIN_SHUFPS: case IX86_BUILTIN_SHUFPD: icode = (fcode == IX86_BUILTIN_SHUFPS ? CODE_FOR_sse_shufps : CODE_FOR_sse2_shufpd); arg0 = TREE_VALUE (arglist); arg1 = TREE_VALUE (TREE_CHAIN (arglist)); arg2 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); op2 = expand_expr (arg2, NULL_RTX, VOIDmode, 0); tmode = insn_data[icode].operand[0].mode; mode0 = insn_data[icode].operand[1].mode; mode1 = insn_data[icode].operand[2].mode; mode2 = insn_data[icode].operand[3].mode; if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) op0 = copy_to_mode_reg (mode0, op0); if (! (*insn_data[icode].operand[2].predicate) (op1, mode1)) op1 = copy_to_mode_reg (mode1, op1); if (! (*insn_data[icode].operand[3].predicate) (op2, mode2)) { /* @@@ better error message */ error ("mask must be an immediate"); return gen_reg_rtx (tmode); } if (target == 0 || GET_MODE (target) != tmode || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) target = gen_reg_rtx (tmode); pat = GEN_FCN (icode) (target, op0, op1, op2); if (! pat) return 0; emit_insn (pat); return target; case IX86_BUILTIN_PSHUFW: case IX86_BUILTIN_PSHUFD: case IX86_BUILTIN_PSHUFHW: case IX86_BUILTIN_PSHUFLW: icode = ( fcode == IX86_BUILTIN_PSHUFHW ? CODE_FOR_sse2_pshufhw : fcode == IX86_BUILTIN_PSHUFLW ? CODE_FOR_sse2_pshuflw : fcode == IX86_BUILTIN_PSHUFD ? CODE_FOR_sse2_pshufd : CODE_FOR_mmx_pshufw); arg0 = TREE_VALUE (arglist); arg1 = TREE_VALUE (TREE_CHAIN (arglist)); op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); tmode = insn_data[icode].operand[0].mode; mode1 = insn_data[icode].operand[1].mode; mode2 = insn_data[icode].operand[2].mode; if (! (*insn_data[icode].operand[1].predicate) (op0, mode1)) op0 = copy_to_mode_reg (mode1, op0); if (! (*insn_data[icode].operand[2].predicate) (op1, mode2)) { /* @@@ better error message */ error ("mask must be an immediate"); return const0_rtx; } if (target == 0 || GET_MODE (target) != tmode || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) target = gen_reg_rtx (tmode); pat = GEN_FCN (icode) (target, op0, op1); if (! pat) return 0; emit_insn (pat); return target; case IX86_BUILTIN_PSLLDQI128: case IX86_BUILTIN_PSRLDQI128: icode = ( fcode == IX86_BUILTIN_PSLLDQI128 ? CODE_FOR_sse2_ashlti3 : CODE_FOR_sse2_lshrti3); arg0 = TREE_VALUE (arglist); arg1 = TREE_VALUE (TREE_CHAIN (arglist)); op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); tmode = insn_data[icode].operand[0].mode; mode1 = insn_data[icode].operand[1].mode; mode2 = insn_data[icode].operand[2].mode; if (! (*insn_data[icode].operand[1].predicate) (op0, mode1)) { op0 = copy_to_reg (op0); op0 = simplify_gen_subreg (mode1, op0, GET_MODE (op0), 0); } if (! (*insn_data[icode].operand[2].predicate) (op1, mode2)) { error ("shift must be an immediate"); return const0_rtx; } target = gen_reg_rtx (V2DImode); pat = GEN_FCN (icode) (simplify_gen_subreg (tmode, target, V2DImode, 0), op0, op1); if (! pat) return 0; emit_insn (pat); return target; case IX86_BUILTIN_FEMMS: emit_insn (gen_femms ()); return NULL_RTX; case IX86_BUILTIN_PAVGUSB: return ix86_expand_binop_builtin (CODE_FOR_pavgusb, arglist, target); case IX86_BUILTIN_PF2ID: return ix86_expand_unop_builtin (CODE_FOR_pf2id, arglist, target, 0); case IX86_BUILTIN_PFACC: return ix86_expand_binop_builtin (CODE_FOR_pfacc, arglist, target); case IX86_BUILTIN_PFADD: return ix86_expand_binop_builtin (CODE_FOR_addv2sf3, arglist, target); case IX86_BUILTIN_PFCMPEQ: return ix86_expand_binop_builtin (CODE_FOR_eqv2sf3, arglist, target); case IX86_BUILTIN_PFCMPGE: return ix86_expand_binop_builtin (CODE_FOR_gev2sf3, arglist, target); case IX86_BUILTIN_PFCMPGT: return ix86_expand_binop_builtin (CODE_FOR_gtv2sf3, arglist, target); case IX86_BUILTIN_PFMAX: return ix86_expand_binop_builtin (CODE_FOR_pfmaxv2sf3, arglist, target); case IX86_BUILTIN_PFMIN: return ix86_expand_binop_builtin (CODE_FOR_pfminv2sf3, arglist, target); case IX86_BUILTIN_PFMUL: return ix86_expand_binop_builtin (CODE_FOR_mulv2sf3, arglist, target); case IX86_BUILTIN_PFRCP: return ix86_expand_unop_builtin (CODE_FOR_pfrcpv2sf2, arglist, target, 0); case IX86_BUILTIN_PFRCPIT1: return ix86_expand_binop_builtin (CODE_FOR_pfrcpit1v2sf3, arglist, target); case IX86_BUILTIN_PFRCPIT2: return ix86_expand_binop_builtin (CODE_FOR_pfrcpit2v2sf3, arglist, target); case IX86_BUILTIN_PFRSQIT1: return ix86_expand_binop_builtin (CODE_FOR_pfrsqit1v2sf3, arglist, target); case IX86_BUILTIN_PFRSQRT: return ix86_expand_unop_builtin (CODE_FOR_pfrsqrtv2sf2, arglist, target, 0); case IX86_BUILTIN_PFSUB: return ix86_expand_binop_builtin (CODE_FOR_subv2sf3, arglist, target); case IX86_BUILTIN_PFSUBR: return ix86_expand_binop_builtin (CODE_FOR_subrv2sf3, arglist, target); case IX86_BUILTIN_PI2FD: return ix86_expand_unop_builtin (CODE_FOR_floatv2si2, arglist, target, 0); case IX86_BUILTIN_PMULHRW: return ix86_expand_binop_builtin (CODE_FOR_pmulhrwv4hi3, arglist, target); case IX86_BUILTIN_PF2IW: return ix86_expand_unop_builtin (CODE_FOR_pf2iw, arglist, target, 0); case IX86_BUILTIN_PFNACC: return ix86_expand_binop_builtin (CODE_FOR_pfnacc, arglist, target); case IX86_BUILTIN_PFPNACC: return ix86_expand_binop_builtin (CODE_FOR_pfpnacc, arglist, target); case IX86_BUILTIN_PI2FW: return ix86_expand_unop_builtin (CODE_FOR_pi2fw, arglist, target, 0); case IX86_BUILTIN_PSWAPDSI: return ix86_expand_unop_builtin (CODE_FOR_pswapdv2si2, arglist, target, 0); case IX86_BUILTIN_PSWAPDSF: return ix86_expand_unop_builtin (CODE_FOR_pswapdv2sf2, arglist, target, 0); case IX86_BUILTIN_SSE_ZERO: target = gen_reg_rtx (V4SFmode); emit_insn (gen_sse_clrv4sf (target, CONST0_RTX (V4SFmode))); return target; case IX86_BUILTIN_MMX_ZERO: target = gen_reg_rtx (DImode); emit_insn (gen_mmx_clrdi (target)); return target; case IX86_BUILTIN_CLRTI: target = gen_reg_rtx (V2DImode); emit_insn (gen_sse2_clrti (simplify_gen_subreg (TImode, target, V2DImode, 0))); return target; case IX86_BUILTIN_SQRTSD: return ix86_expand_unop1_builtin (CODE_FOR_vmsqrtv2df2, arglist, target); case IX86_BUILTIN_LOADAPD: return ix86_expand_unop_builtin (CODE_FOR_sse2_movapd, arglist, target, 1); case IX86_BUILTIN_LOADUPD: return ix86_expand_unop_builtin (CODE_FOR_sse2_movupd, arglist, target, 1); case IX86_BUILTIN_STOREAPD: return ix86_expand_store_builtin (CODE_FOR_sse2_movapd, arglist); case IX86_BUILTIN_STOREUPD: return ix86_expand_store_builtin (CODE_FOR_sse2_movupd, arglist); case IX86_BUILTIN_LOADSD: return ix86_expand_unop_builtin (CODE_FOR_sse2_loadsd, arglist, target, 1); case IX86_BUILTIN_STORESD: return ix86_expand_store_builtin (CODE_FOR_sse2_storesd, arglist); case IX86_BUILTIN_SETPD1: target = assign_386_stack_local (DFmode, 0); arg0 = TREE_VALUE (arglist); emit_move_insn (adjust_address (target, DFmode, 0), expand_expr (arg0, NULL_RTX, VOIDmode, 0)); op0 = gen_reg_rtx (V2DFmode); emit_insn (gen_sse2_loadsd (op0, adjust_address (target, V2DFmode, 0))); emit_insn (gen_sse2_shufpd (op0, op0, op0, GEN_INT (0))); return op0; case IX86_BUILTIN_SETPD: target = assign_386_stack_local (V2DFmode, 0); arg0 = TREE_VALUE (arglist); arg1 = TREE_VALUE (TREE_CHAIN (arglist)); emit_move_insn (adjust_address (target, DFmode, 0), expand_expr (arg0, NULL_RTX, VOIDmode, 0)); emit_move_insn (adjust_address (target, DFmode, 8), expand_expr (arg1, NULL_RTX, VOIDmode, 0)); op0 = gen_reg_rtx (V2DFmode); emit_insn (gen_sse2_movapd (op0, target)); return op0; case IX86_BUILTIN_LOADRPD: target = ix86_expand_unop_builtin (CODE_FOR_sse2_movapd, arglist, gen_reg_rtx (V2DFmode), 1); emit_insn (gen_sse2_shufpd (target, target, target, GEN_INT (1))); return target; case IX86_BUILTIN_LOADPD1: target = ix86_expand_unop_builtin (CODE_FOR_sse2_loadsd, arglist, gen_reg_rtx (V2DFmode), 1); emit_insn (gen_sse2_shufpd (target, target, target, const0_rtx)); return target; case IX86_BUILTIN_STOREPD1: return ix86_expand_store_builtin (CODE_FOR_sse2_movapd, arglist); case IX86_BUILTIN_STORERPD: return ix86_expand_store_builtin (CODE_FOR_sse2_movapd, arglist); case IX86_BUILTIN_CLRPD: target = gen_reg_rtx (V2DFmode); emit_insn (gen_sse_clrv2df (target)); return target; case IX86_BUILTIN_MFENCE: emit_insn (gen_sse2_mfence ()); return 0; case IX86_BUILTIN_LFENCE: emit_insn (gen_sse2_lfence ()); return 0; case IX86_BUILTIN_CLFLUSH: arg0 = TREE_VALUE (arglist); op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); icode = CODE_FOR_sse2_clflush; if (! (*insn_data[icode].operand[0].predicate) (op0, Pmode)) op0 = copy_to_mode_reg (Pmode, op0); emit_insn (gen_sse2_clflush (op0)); return 0; case IX86_BUILTIN_MOVNTPD: return ix86_expand_store_builtin (CODE_FOR_sse2_movntv2df, arglist); case IX86_BUILTIN_MOVNTDQ: return ix86_expand_store_builtin (CODE_FOR_sse2_movntv2di, arglist); case IX86_BUILTIN_MOVNTI: return ix86_expand_store_builtin (CODE_FOR_sse2_movntsi, arglist); case IX86_BUILTIN_LOADDQA: return ix86_expand_unop_builtin (CODE_FOR_sse2_movdqa, arglist, target, 1); case IX86_BUILTIN_LOADDQU: return ix86_expand_unop_builtin (CODE_FOR_sse2_movdqu, arglist, target, 1); case IX86_BUILTIN_LOADD: return ix86_expand_unop_builtin (CODE_FOR_sse2_loadd, arglist, target, 1); case IX86_BUILTIN_STOREDQA: return ix86_expand_store_builtin (CODE_FOR_sse2_movdqa, arglist); case IX86_BUILTIN_STOREDQU: return ix86_expand_store_builtin (CODE_FOR_sse2_movdqu, arglist); case IX86_BUILTIN_STORED: return ix86_expand_store_builtin (CODE_FOR_sse2_stored, arglist); case IX86_BUILTIN_MONITOR: arg0 = TREE_VALUE (arglist); arg1 = TREE_VALUE (TREE_CHAIN (arglist)); arg2 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); op2 = expand_expr (arg2, NULL_RTX, VOIDmode, 0); if (!REG_P (op0)) op0 = copy_to_mode_reg (SImode, op0); if (!REG_P (op1)) op1 = copy_to_mode_reg (SImode, op1); if (!REG_P (op2)) op2 = copy_to_mode_reg (SImode, op2); emit_insn (gen_monitor (op0, op1, op2)); return 0; case IX86_BUILTIN_MWAIT: arg0 = TREE_VALUE (arglist); arg1 = TREE_VALUE (TREE_CHAIN (arglist)); op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); if (!REG_P (op0)) op0 = copy_to_mode_reg (SImode, op0); if (!REG_P (op1)) op1 = copy_to_mode_reg (SImode, op1); emit_insn (gen_mwait (op0, op1)); return 0; case IX86_BUILTIN_LOADDDUP: return ix86_expand_unop_builtin (CODE_FOR_loadddup, arglist, target, 1); case IX86_BUILTIN_LDDQU: return ix86_expand_unop_builtin (CODE_FOR_lddqu, arglist, target, 1); default: break; } for (i = 0, d = bdesc_2arg; i < ARRAY_SIZE (bdesc_2arg); i++, d++) if (d->code == fcode) { /* Compares are treated specially. */ if (d->icode == CODE_FOR_maskcmpv4sf3 || d->icode == CODE_FOR_vmmaskcmpv4sf3 || d->icode == CODE_FOR_maskncmpv4sf3 || d->icode == CODE_FOR_vmmaskncmpv4sf3 || d->icode == CODE_FOR_maskcmpv2df3 || d->icode == CODE_FOR_vmmaskcmpv2df3 || d->icode == CODE_FOR_maskncmpv2df3 || d->icode == CODE_FOR_vmmaskncmpv2df3) return ix86_expand_sse_compare (d, arglist, target); return ix86_expand_binop_builtin (d->icode, arglist, target); } for (i = 0, d = bdesc_1arg; i < ARRAY_SIZE (bdesc_1arg); i++, d++) if (d->code == fcode) return ix86_expand_unop_builtin (d->icode, arglist, target, 0); for (i = 0, d = bdesc_comi; i < ARRAY_SIZE (bdesc_comi); i++, d++) if (d->code == fcode) return ix86_expand_sse_comi (d, arglist, target); /* @@@ Should really do something sensible here. */ return 0; } /* Store OPERAND to the memory after reload is completed. This means that we can't easily use assign_stack_local. */ rtx ix86_force_to_memory (enum machine_mode mode, rtx operand) { rtx result; if (!reload_completed) abort (); if (TARGET_RED_ZONE) { result = gen_rtx_MEM (mode, gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (-RED_ZONE_SIZE))); emit_move_insn (result, operand); } else if (!TARGET_RED_ZONE && TARGET_64BIT) { switch (mode) { case HImode: case SImode: operand = gen_lowpart (DImode, operand); /* FALLTHRU */ case DImode: emit_insn ( gen_rtx_SET (VOIDmode, gen_rtx_MEM (DImode, gen_rtx_PRE_DEC (DImode, stack_pointer_rtx)), operand)); break; default: abort (); } result = gen_rtx_MEM (mode, stack_pointer_rtx); } else { switch (mode) { case DImode: { rtx operands[2]; split_di (&operand, 1, operands, operands + 1); emit_insn ( gen_rtx_SET (VOIDmode, gen_rtx_MEM (SImode, gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx)), operands[1])); emit_insn ( gen_rtx_SET (VOIDmode, gen_rtx_MEM (SImode, gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx)), operands[0])); } break; case HImode: /* It is better to store HImodes as SImodes. */ if (!TARGET_PARTIAL_REG_STALL) operand = gen_lowpart (SImode, operand); /* FALLTHRU */ case SImode: emit_insn ( gen_rtx_SET (VOIDmode, gen_rtx_MEM (GET_MODE (operand), gen_rtx_PRE_DEC (SImode, stack_pointer_rtx)), operand)); break; default: abort (); } result = gen_rtx_MEM (mode, stack_pointer_rtx); } return result; } /* Free operand from the memory. */ void ix86_free_from_memory (enum machine_mode mode) { if (!TARGET_RED_ZONE) { int size; if (mode == DImode || TARGET_64BIT) size = 8; else if (mode == HImode && TARGET_PARTIAL_REG_STALL) size = 2; else size = 4; /* Use LEA to deallocate stack space. In peephole2 it will be converted to pop or add instruction if registers are available. */ emit_insn (gen_rtx_SET (VOIDmode, stack_pointer_rtx, gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (size)))); } } /* Put float CONST_DOUBLE in the constant pool instead of fp regs. QImode must go into class Q_REGS. Narrow ALL_REGS to GENERAL_REGS. This supports allowing movsf and movdf to do mem-to-mem moves through integer regs. */ enum reg_class ix86_preferred_reload_class (rtx x, enum reg_class class) { if (GET_CODE (x) == CONST_VECTOR && x != CONST0_RTX (GET_MODE (x))) return NO_REGS; if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) != VOIDmode) { /* SSE can't load any constant directly yet. */ if (SSE_CLASS_P (class)) return NO_REGS; /* Floats can load 0 and 1. */ if (MAYBE_FLOAT_CLASS_P (class) && standard_80387_constant_p (x)) { /* Limit class to non-SSE. Use GENERAL_REGS if possible. */ if (MAYBE_SSE_CLASS_P (class)) return (reg_class_subset_p (class, GENERAL_REGS) ? GENERAL_REGS : FLOAT_REGS); else return class; } /* General regs can load everything. */ if (reg_class_subset_p (class, GENERAL_REGS)) return GENERAL_REGS; /* In case we haven't resolved FLOAT or SSE yet, give up. */ if (MAYBE_FLOAT_CLASS_P (class) || MAYBE_SSE_CLASS_P (class)) return NO_REGS; } if (MAYBE_MMX_CLASS_P (class) && CONSTANT_P (x)) return NO_REGS; if (GET_MODE (x) == QImode && ! reg_class_subset_p (class, Q_REGS)) return Q_REGS; return class; } /* If we are copying between general and FP registers, we need a memory location. The same is true for SSE and MMX registers. The macro can't work reliably when one of the CLASSES is class containing registers from multiple units (SSE, MMX, integer). We avoid this by never combining those units in single alternative in the machine description. Ensure that this constraint holds to avoid unexpected surprises. When STRICT is false, we are being called from REGISTER_MOVE_COST, so do not enforce these sanity checks. */ int ix86_secondary_memory_needed (enum reg_class class1, enum reg_class class2, enum machine_mode mode, int strict) { if (MAYBE_FLOAT_CLASS_P (class1) != FLOAT_CLASS_P (class1) || MAYBE_FLOAT_CLASS_P (class2) != FLOAT_CLASS_P (class2) || MAYBE_SSE_CLASS_P (class1) != SSE_CLASS_P (class1) || MAYBE_SSE_CLASS_P (class2) != SSE_CLASS_P (class2) || MAYBE_MMX_CLASS_P (class1) != MMX_CLASS_P (class1) || MAYBE_MMX_CLASS_P (class2) != MMX_CLASS_P (class2)) { if (strict) abort (); else return 1; } return (FLOAT_CLASS_P (class1) != FLOAT_CLASS_P (class2) || ((SSE_CLASS_P (class1) != SSE_CLASS_P (class2) || MMX_CLASS_P (class1) != MMX_CLASS_P (class2)) && ((mode != SImode && (mode != DImode || !TARGET_64BIT)) || (!TARGET_INTER_UNIT_MOVES && !optimize_size)))); } /* Return the cost of moving data from a register in class CLASS1 to one in class CLASS2. It is not required that the cost always equal 2 when FROM is the same as TO; on some machines it is expensive to move between registers if they are not general registers. */ int ix86_register_move_cost (enum machine_mode mode, enum reg_class class1, enum reg_class class2) { /* In case we require secondary memory, compute cost of the store followed by load. In order to avoid bad register allocation choices, we need for this to be *at least* as high as the symmetric MEMORY_MOVE_COST. */ if (ix86_secondary_memory_needed (class1, class2, mode, 0)) { int cost = 1; cost += MAX (MEMORY_MOVE_COST (mode, class1, 0), MEMORY_MOVE_COST (mode, class1, 1)); cost += MAX (MEMORY_MOVE_COST (mode, class2, 0), MEMORY_MOVE_COST (mode, class2, 1)); /* In case of copying from general_purpose_register we may emit multiple stores followed by single load causing memory size mismatch stall. Count this as arbitrarily high cost of 20. */ if (CLASS_MAX_NREGS (class1, mode) > CLASS_MAX_NREGS (class2, mode)) cost += 20; /* In the case of FP/MMX moves, the registers actually overlap, and we have to switch modes in order to treat them differently. */ if ((MMX_CLASS_P (class1) && MAYBE_FLOAT_CLASS_P (class2)) || (MMX_CLASS_P (class2) && MAYBE_FLOAT_CLASS_P (class1))) cost += 20; return cost; } /* Moves between SSE/MMX and integer unit are expensive. */ if (MMX_CLASS_P (class1) != MMX_CLASS_P (class2) || SSE_CLASS_P (class1) != SSE_CLASS_P (class2)) return ix86_cost->mmxsse_to_integer; if (MAYBE_FLOAT_CLASS_P (class1)) return ix86_cost->fp_move; if (MAYBE_SSE_CLASS_P (class1)) return ix86_cost->sse_move; if (MAYBE_MMX_CLASS_P (class1)) return ix86_cost->mmx_move; return 2; } /* Return 1 if hard register REGNO can hold a value of machine-mode MODE. */ int ix86_hard_regno_mode_ok (int regno, enum machine_mode mode) { /* Flags and only flags can only hold CCmode values. */ if (CC_REGNO_P (regno)) return GET_MODE_CLASS (mode) == MODE_CC; if (GET_MODE_CLASS (mode) == MODE_CC || GET_MODE_CLASS (mode) == MODE_RANDOM || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT) return 0; if (FP_REGNO_P (regno)) return VALID_FP_MODE_P (mode); if (SSE_REGNO_P (regno)) { /* HACK! We didn't change all of the constraints for SSE1 for the scalar modes on the branch. Fortunately, they're not required for ABI compatibility. */ if (!TARGET_SSE2 && !VECTOR_MODE_P (mode)) return VALID_SSE_REG_MODE (mode); /* We implement the move patterns for all vector modes into and out of SSE registers, even when no operation instructions are available. */ return (VALID_SSE_REG_MODE (mode) || VALID_SSE2_REG_MODE (mode) || VALID_MMX_REG_MODE (mode) || VALID_MMX_REG_MODE_3DNOW (mode)); } if (MMX_REGNO_P (regno)) { /* We implement the move patterns for 3DNOW modes even in MMX mode, so if the register is available at all, then we can move data of the given mode into or out of it. */ return (VALID_MMX_REG_MODE (mode) || VALID_MMX_REG_MODE_3DNOW (mode)); } /* We handle both integer and floats in the general purpose registers. In future we should be able to handle vector modes as well. */ if (!VALID_INT_MODE_P (mode) && !VALID_FP_MODE_P (mode)) return 0; /* Take care for QImode values - they can be in non-QI regs, but then they do cause partial register stalls. */ if (regno < 4 || mode != QImode || TARGET_64BIT) return 1; return reload_in_progress || reload_completed || !TARGET_PARTIAL_REG_STALL; } /* Return the cost of moving data of mode M between a register and memory. A value of 2 is the default; this cost is relative to those in `REGISTER_MOVE_COST'. If moving between registers and memory is more expensive than between two registers, you should define this macro to express the relative cost. Model also increased moving costs of QImode registers in non Q_REGS classes. */ int ix86_memory_move_cost (enum machine_mode mode, enum reg_class class, int in) { if (FLOAT_CLASS_P (class)) { int index; switch (mode) { case SFmode: index = 0; break; case DFmode: index = 1; break; case XFmode: index = 2; break; default: return 100; } return in ? ix86_cost->fp_load [index] : ix86_cost->fp_store [index]; } if (SSE_CLASS_P (class)) { int index; switch (GET_MODE_SIZE (mode)) { case 4: index = 0; break; case 8: index = 1; break; case 16: index = 2; break; default: return 100; } return in ? ix86_cost->sse_load [index] : ix86_cost->sse_store [index]; } if (MMX_CLASS_P (class)) { int index; switch (GET_MODE_SIZE (mode)) { case 4: index = 0; break; case 8: index = 1; break; default: return 100; } return in ? ix86_cost->mmx_load [index] : ix86_cost->mmx_store [index]; } switch (GET_MODE_SIZE (mode)) { case 1: if (in) return (Q_CLASS_P (class) ? ix86_cost->int_load[0] : ix86_cost->movzbl_load); else return (Q_CLASS_P (class) ? ix86_cost->int_store[0] : ix86_cost->int_store[0] + 4); break; case 2: return in ? ix86_cost->int_load[1] : ix86_cost->int_store[1]; default: /* Compute number of 32bit moves needed. TFmode is moved as XFmode. */ if (mode == TFmode) mode = XFmode; return ((in ? ix86_cost->int_load[2] : ix86_cost->int_store[2]) * (((int) GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)); } } /* Compute a (partial) cost for rtx X. Return true if the complete cost has been computed, and false if subexpressions should be scanned. In either case, *TOTAL contains the cost result. */ static bool ix86_rtx_costs (rtx x, int code, int outer_code, int *total) { enum machine_mode mode = GET_MODE (x); switch (code) { case CONST_INT: case CONST: case LABEL_REF: case SYMBOL_REF: if (TARGET_64BIT && !x86_64_sign_extended_value (x)) *total = 3; else if (TARGET_64BIT && !x86_64_zero_extended_value (x)) *total = 2; else if (flag_pic && SYMBOLIC_CONST (x) && (!TARGET_64BIT || (!GET_CODE (x) != LABEL_REF && (GET_CODE (x) != SYMBOL_REF || !SYMBOL_REF_LOCAL_P (x))))) *total = 1; else *total = 0; return true; case CONST_DOUBLE: if (mode == VOIDmode) *total = 0; else switch (standard_80387_constant_p (x)) { case 1: /* 0.0 */ *total = 1; break; default: /* Other constants */ *total = 2; break; case 0: case -1: /* Start with (MEM (SYMBOL_REF)), since that's where it'll probably end up. Add a penalty for size. */ *total = (COSTS_N_INSNS (1) + (flag_pic != 0 && !TARGET_64BIT) + (mode == SFmode ? 0 : mode == DFmode ? 1 : 2)); break; } return true; case ZERO_EXTEND: /* The zero extensions is often completely free on x86_64, so make it as cheap as possible. */ if (TARGET_64BIT && mode == DImode && GET_MODE (XEXP (x, 0)) == SImode) *total = 1; else if (TARGET_ZERO_EXTEND_WITH_AND) *total = COSTS_N_INSNS (ix86_cost->add); else *total = COSTS_N_INSNS (ix86_cost->movzx); return false; case SIGN_EXTEND: *total = COSTS_N_INSNS (ix86_cost->movsx); return false; case ASHIFT: if (GET_CODE (XEXP (x, 1)) == CONST_INT && (GET_MODE (XEXP (x, 0)) != DImode || TARGET_64BIT)) { HOST_WIDE_INT value = INTVAL (XEXP (x, 1)); if (value == 1) { *total = COSTS_N_INSNS (ix86_cost->add); return false; } if ((value == 2 || value == 3) && !TARGET_DECOMPOSE_LEA && ix86_cost->lea <= ix86_cost->shift_const) { *total = COSTS_N_INSNS (ix86_cost->lea); return false; } } /* FALLTHRU */ case ROTATE: case ASHIFTRT: case LSHIFTRT: case ROTATERT: if (!TARGET_64BIT && GET_MODE (XEXP (x, 0)) == DImode) { if (GET_CODE (XEXP (x, 1)) == CONST_INT) { if (INTVAL (XEXP (x, 1)) > 32) *total = COSTS_N_INSNS(ix86_cost->shift_const + 2); else *total = COSTS_N_INSNS(ix86_cost->shift_const * 2); } else { if (GET_CODE (XEXP (x, 1)) == AND) *total = COSTS_N_INSNS(ix86_cost->shift_var * 2); else *total = COSTS_N_INSNS(ix86_cost->shift_var * 6 + 2); } } else { if (GET_CODE (XEXP (x, 1)) == CONST_INT) *total = COSTS_N_INSNS (ix86_cost->shift_const); else *total = COSTS_N_INSNS (ix86_cost->shift_var); } return false; case MULT: if (FLOAT_MODE_P (mode)) *total = COSTS_N_INSNS (ix86_cost->fmul); else if (GET_CODE (XEXP (x, 1)) == CONST_INT) { unsigned HOST_WIDE_INT value = INTVAL (XEXP (x, 1)); int nbits; for (nbits = 0; value != 0; value >>= 1) nbits++; *total = COSTS_N_INSNS (ix86_cost->mult_init[MODE_INDEX (mode)] + nbits * ix86_cost->mult_bit); } else { /* This is arbitrary */ *total = COSTS_N_INSNS (ix86_cost->mult_init[MODE_INDEX (mode)] + 7 * ix86_cost->mult_bit); } return false; case DIV: case UDIV: case MOD: case UMOD: if (FLOAT_MODE_P (mode)) *total = COSTS_N_INSNS (ix86_cost->fdiv); else *total = COSTS_N_INSNS (ix86_cost->divide[MODE_INDEX (mode)]); return false; case PLUS: if (FLOAT_MODE_P (mode)) *total = COSTS_N_INSNS (ix86_cost->fadd); else if (!TARGET_DECOMPOSE_LEA && GET_MODE_CLASS (mode) == MODE_INT && GET_MODE_BITSIZE (mode) <= GET_MODE_BITSIZE (Pmode)) { if (GET_CODE (XEXP (x, 0)) == PLUS && GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT && CONSTANT_P (XEXP (x, 1))) { HOST_WIDE_INT val = INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1)); if (val == 2 || val == 4 || val == 8) { *total = COSTS_N_INSNS (ix86_cost->lea); *total += rtx_cost (XEXP (XEXP (x, 0), 1), outer_code); *total += rtx_cost (XEXP (XEXP (XEXP (x, 0), 0), 0), outer_code); *total += rtx_cost (XEXP (x, 1), outer_code); return true; } } else if (GET_CODE (XEXP (x, 0)) == MULT && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT) { HOST_WIDE_INT val = INTVAL (XEXP (XEXP (x, 0), 1)); if (val == 2 || val == 4 || val == 8) { *total = COSTS_N_INSNS (ix86_cost->lea); *total += rtx_cost (XEXP (XEXP (x, 0), 0), outer_code); *total += rtx_cost (XEXP (x, 1), outer_code); return true; } } else if (GET_CODE (XEXP (x, 0)) == PLUS) { *total = COSTS_N_INSNS (ix86_cost->lea); *total += rtx_cost (XEXP (XEXP (x, 0), 0), outer_code); *total += rtx_cost (XEXP (XEXP (x, 0), 1), outer_code); *total += rtx_cost (XEXP (x, 1), outer_code); return true; } } /* FALLTHRU */ case MINUS: if (FLOAT_MODE_P (mode)) { *total = COSTS_N_INSNS (ix86_cost->fadd); return false; } /* FALLTHRU */ case AND: case IOR: case XOR: if (!TARGET_64BIT && mode == DImode) { *total = (COSTS_N_INSNS (ix86_cost->add) * 2 + (rtx_cost (XEXP (x, 0), outer_code) << (GET_MODE (XEXP (x, 0)) != DImode)) + (rtx_cost (XEXP (x, 1), outer_code) << (GET_MODE (XEXP (x, 1)) != DImode))); return true; } /* FALLTHRU */ case NEG: if (FLOAT_MODE_P (mode)) { *total = COSTS_N_INSNS (ix86_cost->fchs); return false; } /* FALLTHRU */ case NOT: if (!TARGET_64BIT && mode == DImode) *total = COSTS_N_INSNS (ix86_cost->add * 2); else *total = COSTS_N_INSNS (ix86_cost->add); return false; case FLOAT_EXTEND: if (!TARGET_SSE_MATH || mode == XFmode || (mode == DFmode && !TARGET_SSE2)) *total = 0; return false; case ABS: if (FLOAT_MODE_P (mode)) *total = COSTS_N_INSNS (ix86_cost->fabs); return false; case SQRT: if (FLOAT_MODE_P (mode)) *total = COSTS_N_INSNS (ix86_cost->fsqrt); return false; case UNSPEC: if (XINT (x, 1) == UNSPEC_TP) *total = 0; return false; default: return false; } } #if defined (DO_GLOBAL_CTORS_BODY) && defined (HAS_INIT_SECTION) static void ix86_svr3_asm_out_constructor (rtx symbol, int priority ATTRIBUTE_UNUSED) { init_section (); fputs ("\tpushl $", asm_out_file); assemble_name (asm_out_file, XSTR (symbol, 0)); fputc ('\n', asm_out_file); } #endif #if TARGET_MACHO static int current_machopic_label_num; /* Given a symbol name and its associated stub, write out the definition of the stub. */ void machopic_output_stub (FILE *file, const char *symb, const char *stub) { unsigned int length; char *binder_name, *symbol_name, lazy_ptr_name[32]; int label = ++current_machopic_label_num; /* Lose our funky encoding stuff so it doesn't contaminate the stub. */ symb = (*targetm.strip_name_encoding) (symb); length = strlen (stub); binder_name = alloca (length + 32); GEN_BINDER_NAME_FOR_STUB (binder_name, stub, length); length = strlen (symb); symbol_name = alloca (length + 32); GEN_SYMBOL_NAME_FOR_SYMBOL (symbol_name, symb, length); sprintf (lazy_ptr_name, "L%d$lz", label); if (MACHOPIC_PURE) machopic_picsymbol_stub_section (); else machopic_symbol_stub_section (); fprintf (file, "%s:\n", stub); fprintf (file, "\t.indirect_symbol %s\n", symbol_name); if (MACHOPIC_PURE) { fprintf (file, "\tcall LPC$%d\nLPC$%d:\tpopl %%eax\n", label, label); fprintf (file, "\tmovl %s-LPC$%d(%%eax),%%edx\n", lazy_ptr_name, label); fprintf (file, "\tjmp %%edx\n"); } else fprintf (file, "\tjmp *%s\n", lazy_ptr_name); fprintf (file, "%s:\n", binder_name); if (MACHOPIC_PURE) { fprintf (file, "\tlea %s-LPC$%d(%%eax),%%eax\n", lazy_ptr_name, label); fprintf (file, "\tpushl %%eax\n"); } else fprintf (file, "\t pushl $%s\n", lazy_ptr_name); fprintf (file, "\tjmp dyld_stub_binding_helper\n"); machopic_lazy_symbol_ptr_section (); fprintf (file, "%s:\n", lazy_ptr_name); fprintf (file, "\t.indirect_symbol %s\n", symbol_name); fprintf (file, "\t.long %s\n", binder_name); } #endif /* TARGET_MACHO */ /* Order the registers for register allocator. */ void x86_order_regs_for_local_alloc (void) { int pos = 0; int i; /* First allocate the local general purpose registers. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) if (GENERAL_REGNO_P (i) && call_used_regs[i]) reg_alloc_order [pos++] = i; /* Global general purpose registers. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) if (GENERAL_REGNO_P (i) && !call_used_regs[i]) reg_alloc_order [pos++] = i; /* x87 registers come first in case we are doing FP math using them. */ if (!TARGET_SSE_MATH) for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++) reg_alloc_order [pos++] = i; /* SSE registers. */ for (i = FIRST_SSE_REG; i <= LAST_SSE_REG; i++) reg_alloc_order [pos++] = i; for (i = FIRST_REX_SSE_REG; i <= LAST_REX_SSE_REG; i++) reg_alloc_order [pos++] = i; /* x87 registers. */ if (TARGET_SSE_MATH) for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++) reg_alloc_order [pos++] = i; for (i = FIRST_MMX_REG; i <= LAST_MMX_REG; i++) reg_alloc_order [pos++] = i; /* Initialize the rest of array as we do not allocate some registers at all. */ while (pos < FIRST_PSEUDO_REGISTER) reg_alloc_order [pos++] = 0; } #ifndef TARGET_USE_MS_BITFIELD_LAYOUT #define TARGET_USE_MS_BITFIELD_LAYOUT 0 #endif /* Handle a "ms_struct" or "gcc_struct" attribute; arguments as in struct attribute_spec.handler. */ static tree ix86_handle_struct_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { tree *type = NULL; if (DECL_P (*node)) { if (TREE_CODE (*node) == TYPE_DECL) type = &TREE_TYPE (*node); } else type = node; if (!(type && (TREE_CODE (*type) == RECORD_TYPE || TREE_CODE (*type) == UNION_TYPE))) { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } else if ((is_attribute_p ("ms_struct", name) && lookup_attribute ("gcc_struct", TYPE_ATTRIBUTES (*type))) || ((is_attribute_p ("gcc_struct", name) && lookup_attribute ("ms_struct", TYPE_ATTRIBUTES (*type))))) { warning ("`%s' incompatible attribute ignored", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE; } static bool ix86_ms_bitfield_layout_p (tree record_type) { return (TARGET_USE_MS_BITFIELD_LAYOUT && !lookup_attribute ("gcc_struct", TYPE_ATTRIBUTES (record_type))) || lookup_attribute ("ms_struct", TYPE_ATTRIBUTES (record_type)); } /* Returns an expression indicating where the this parameter is located on entry to the FUNCTION. */ static rtx x86_this_parameter (tree function) { tree type = TREE_TYPE (function); if (TARGET_64BIT) { int n = aggregate_value_p (TREE_TYPE (type), type) != 0; return gen_rtx_REG (DImode, x86_64_int_parameter_registers[n]); } if (ix86_function_regparm (type, function) > 0) { tree parm; parm = TYPE_ARG_TYPES (type); /* Figure out whether or not the function has a variable number of arguments. */ for (; parm; parm = TREE_CHAIN (parm)) if (TREE_VALUE (parm) == void_type_node) break; /* If not, the this parameter is in the first argument. */ if (parm) { int regno = 0; if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (type))) regno = 2; return gen_rtx_REG (SImode, regno); } } if (aggregate_value_p (TREE_TYPE (type), type)) return gen_rtx_MEM (SImode, plus_constant (stack_pointer_rtx, 8)); else return gen_rtx_MEM (SImode, plus_constant (stack_pointer_rtx, 4)); } /* Determine whether x86_output_mi_thunk can succeed. */ static bool x86_can_output_mi_thunk (tree thunk ATTRIBUTE_UNUSED, HOST_WIDE_INT delta ATTRIBUTE_UNUSED, HOST_WIDE_INT vcall_offset, tree function) { /* 64-bit can handle anything. */ if (TARGET_64BIT) return true; /* For 32-bit, everything's fine if we have one free register. */ if (ix86_function_regparm (TREE_TYPE (function), function) < 3) return true; /* Need a free register for vcall_offset. */ if (vcall_offset) return false; /* Need a free register for GOT references. */ if (flag_pic && !(*targetm.binds_local_p) (function)) return false; /* Otherwise ok. */ return true; } /* Output the assembler code for a thunk function. THUNK_DECL is the declaration for the thunk function itself, FUNCTION is the decl for the target function. DELTA is an immediate constant offset to be added to THIS. If VCALL_OFFSET is nonzero, the word at *(*this + vcall_offset) should be added to THIS. */ static void x86_output_mi_thunk (FILE *file ATTRIBUTE_UNUSED, tree thunk ATTRIBUTE_UNUSED, HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset, tree function) { rtx xops[3]; rtx this = x86_this_parameter (function); rtx this_reg, tmp; /* If VCALL_OFFSET, we'll need THIS in a register. Might as well pull it in now and let DELTA benefit. */ if (REG_P (this)) this_reg = this; else if (vcall_offset) { /* Put the this parameter into %eax. */ xops[0] = this; xops[1] = this_reg = gen_rtx_REG (Pmode, 0); output_asm_insn ("mov{l}\t{%0, %1|%1, %0}", xops); } else this_reg = NULL_RTX; /* Adjust the this parameter by a fixed constant. */ if (delta) { xops[0] = GEN_INT (delta); xops[1] = this_reg ? this_reg : this; if (TARGET_64BIT) { if (!x86_64_general_operand (xops[0], DImode)) { tmp = gen_rtx_REG (DImode, FIRST_REX_INT_REG + 2 /* R10 */); xops[1] = tmp; output_asm_insn ("mov{q}\t{%1, %0|%0, %1}", xops); xops[0] = tmp; xops[1] = this; } output_asm_insn ("add{q}\t{%0, %1|%1, %0}", xops); } else output_asm_insn ("add{l}\t{%0, %1|%1, %0}", xops); } /* Adjust the this parameter by a value stored in the vtable. */ if (vcall_offset) { if (TARGET_64BIT) tmp = gen_rtx_REG (DImode, FIRST_REX_INT_REG + 2 /* R10 */); else { int tmp_regno = 2 /* ECX */; if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (TREE_TYPE (function)))) tmp_regno = 0 /* EAX */; tmp = gen_rtx_REG (SImode, tmp_regno); } xops[0] = gen_rtx_MEM (Pmode, this_reg); xops[1] = tmp; if (TARGET_64BIT) output_asm_insn ("mov{q}\t{%0, %1|%1, %0}", xops); else output_asm_insn ("mov{l}\t{%0, %1|%1, %0}", xops); /* Adjust the this parameter. */ xops[0] = gen_rtx_MEM (Pmode, plus_constant (tmp, vcall_offset)); if (TARGET_64BIT && !memory_operand (xops[0], Pmode)) { rtx tmp2 = gen_rtx_REG (DImode, FIRST_REX_INT_REG + 3 /* R11 */); xops[0] = GEN_INT (vcall_offset); xops[1] = tmp2; output_asm_insn ("mov{q}\t{%0, %1|%1, %0}", xops); xops[0] = gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, tmp, tmp2)); } xops[1] = this_reg; if (TARGET_64BIT) output_asm_insn ("add{q}\t{%0, %1|%1, %0}", xops); else output_asm_insn ("add{l}\t{%0, %1|%1, %0}", xops); } /* If necessary, drop THIS back to its stack slot. */ if (this_reg && this_reg != this) { xops[0] = this_reg; xops[1] = this; output_asm_insn ("mov{l}\t{%0, %1|%1, %0}", xops); } xops[0] = XEXP (DECL_RTL (function), 0); if (TARGET_64BIT) { if (!flag_pic || (*targetm.binds_local_p) (function)) output_asm_insn ("jmp\t%P0", xops); else { tmp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, xops[0]), UNSPEC_GOTPCREL); tmp = gen_rtx_CONST (Pmode, tmp); tmp = gen_rtx_MEM (QImode, tmp); xops[0] = tmp; output_asm_insn ("jmp\t%A0", xops); } } else { if (!flag_pic || (*targetm.binds_local_p) (function)) output_asm_insn ("jmp\t%P0", xops); else #if TARGET_MACHO if (TARGET_MACHO) { const char *ip = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (function)); tmp = gen_rtx_SYMBOL_REF (Pmode, machopic_stub_name (ip)); tmp = gen_rtx_MEM (QImode, tmp); xops[0] = tmp; output_asm_insn ("jmp\t%0", xops); } else #endif /* TARGET_MACHO */ { tmp = gen_rtx_REG (SImode, 2 /* ECX */); output_set_got (tmp); xops[1] = tmp; output_asm_insn ("mov{l}\t{%0@GOT(%1), %1|%1, %0@GOT[%1]}", xops); output_asm_insn ("jmp\t{*}%1", xops); } } } static void x86_file_start (void) { default_file_start (); if (X86_FILE_START_VERSION_DIRECTIVE) fputs ("\t.version\t\"01.01\"\n", asm_out_file); if (X86_FILE_START_FLTUSED) fputs ("\t.global\t__fltused\n", asm_out_file); if (ix86_asm_dialect == ASM_INTEL) fputs ("\t.intel_syntax\n", asm_out_file); } int x86_field_alignment (tree field, int computed) { enum machine_mode mode; tree type = TREE_TYPE (field); if (TARGET_64BIT || TARGET_ALIGN_DOUBLE) return computed; mode = TYPE_MODE (TREE_CODE (type) == ARRAY_TYPE ? get_inner_array_type (type) : type); if (mode == DFmode || mode == DCmode || GET_MODE_CLASS (mode) == MODE_INT || GET_MODE_CLASS (mode) == MODE_COMPLEX_INT) return MIN (32, computed); return computed; } /* Output assembler code to FILE to increment profiler label # LABELNO for profiling a function entry. */ void x86_function_profiler (FILE *file, int labelno ATTRIBUTE_UNUSED) { if (TARGET_64BIT) if (flag_pic) { #ifndef NO_PROFILE_COUNTERS fprintf (file, "\tleaq\t%sP%d@(%%rip),%%r11\n", LPREFIX, labelno); #endif fprintf (file, "\tcall\t*%s@GOTPCREL(%%rip)\n", MCOUNT_NAME); } else { #ifndef NO_PROFILE_COUNTERS fprintf (file, "\tmovq\t$%sP%d,%%r11\n", LPREFIX, labelno); #endif fprintf (file, "\tcall\t%s\n", MCOUNT_NAME); } else if (flag_pic) { #ifndef NO_PROFILE_COUNTERS fprintf (file, "\tleal\t%sP%d@GOTOFF(%%ebx),%%%s\n", LPREFIX, labelno, PROFILE_COUNT_REGISTER); #endif fprintf (file, "\tcall\t*%s@GOT(%%ebx)\n", MCOUNT_NAME); } else { #ifndef NO_PROFILE_COUNTERS fprintf (file, "\tmovl\t$%sP%d,%%%s\n", LPREFIX, labelno, PROFILE_COUNT_REGISTER); #endif fprintf (file, "\tcall\t%s\n", MCOUNT_NAME); } } /* We don't have exact information about the insn sizes, but we may assume quite safely that we are informed about all 1 byte insns and memory address sizes. This is enough to eliminate unnecessary padding in 99% of cases. */ static int min_insn_size (rtx insn) { int l = 0; if (!INSN_P (insn) || !active_insn_p (insn)) return 0; /* Discard alignments we've emit and jump instructions. */ if (GET_CODE (PATTERN (insn)) == UNSPEC_VOLATILE && XINT (PATTERN (insn), 1) == UNSPECV_ALIGN) return 0; if (GET_CODE (insn) == JUMP_INSN && (GET_CODE (PATTERN (insn)) == ADDR_VEC || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC)) return 0; /* Important case - calls are always 5 bytes. It is common to have many calls in the row. */ if (GET_CODE (insn) == CALL_INSN && symbolic_reference_mentioned_p (PATTERN (insn)) && !SIBLING_CALL_P (insn)) return 5; if (get_attr_length (insn) <= 1) return 1; /* For normal instructions we may rely on the sizes of addresses and the presence of symbol to require 4 bytes of encoding. This is not the case for jumps where references are PC relative. */ if (GET_CODE (insn) != JUMP_INSN) { l = get_attr_length_address (insn); if (l < 4 && symbolic_reference_mentioned_p (PATTERN (insn))) l = 4; } if (l) return 1+l; else return 2; } /* AMD K8 core mispredicts jumps when there are more than 3 jumps in 16 byte window. */ static void k8_avoid_jump_misspredicts (void) { rtx insn, start = get_insns (); int nbytes = 0, njumps = 0; int isjump = 0; /* Look for all minimal intervals of instructions containing 4 jumps. The intervals are bounded by START and INSN. NBYTES is the total size of instructions in the interval including INSN and not including START. When the NBYTES is smaller than 16 bytes, it is possible that the end of START and INSN ends up in the same 16byte page. The smallest offset in the page INSN can start is the case where START ends on the offset 0. Offset of INSN is then NBYTES - sizeof (INSN). We add p2align to 16byte window with maxskip 17 - NBYTES + sizeof (INSN). */ for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) { nbytes += min_insn_size (insn); if (rtl_dump_file) fprintf(rtl_dump_file, "Insn %i estimated to %i bytes\n", INSN_UID (insn), min_insn_size (insn)); if ((GET_CODE (insn) == JUMP_INSN && GET_CODE (PATTERN (insn)) != ADDR_VEC && GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC) || GET_CODE (insn) == CALL_INSN) njumps++; else continue; while (njumps > 3) { start = NEXT_INSN (start); if ((GET_CODE (start) == JUMP_INSN && GET_CODE (PATTERN (start)) != ADDR_VEC && GET_CODE (PATTERN (start)) != ADDR_DIFF_VEC) || GET_CODE (start) == CALL_INSN) njumps--, isjump = 1; else isjump = 0; nbytes -= min_insn_size (start); } if (njumps < 0) abort (); if (rtl_dump_file) fprintf(rtl_dump_file, "Interval %i to %i has %i bytes\n", INSN_UID (start), INSN_UID (insn), nbytes); if (njumps == 3 && isjump && nbytes < 16) { int padsize = 15 - nbytes + min_insn_size (insn); if (rtl_dump_file) fprintf (rtl_dump_file, "Padding insn %i by %i bytes!\n", INSN_UID (insn), padsize); emit_insn_before (gen_align (GEN_INT (padsize)), insn); } } } /* Implement machine specific optimizations. At the moment we implement single transformation: AMD Athlon works faster when RET is not destination of conditional jump or directly preceded by other jump instruction. We avoid the penalty by inserting NOP just before the RET instructions in such cases. */ static void ix86_reorg (void) { edge e; if (!TARGET_ATHLON_K8 || !optimize || optimize_size) return; for (e = EXIT_BLOCK_PTR->pred; e; e = e->pred_next) { basic_block bb = e->src; rtx ret = BB_END (bb); rtx prev; bool replace = false; if (GET_CODE (ret) != JUMP_INSN || GET_CODE (PATTERN (ret)) != RETURN || !maybe_hot_bb_p (bb)) continue; for (prev = PREV_INSN (ret); prev; prev = PREV_INSN (prev)) if (active_insn_p (prev) || GET_CODE (prev) == CODE_LABEL) break; if (prev && GET_CODE (prev) == CODE_LABEL) { edge e; for (e = bb->pred; e; e = e->pred_next) if (EDGE_FREQUENCY (e) && e->src->index >= 0 && !(e->flags & EDGE_FALLTHRU)) replace = true; } if (!replace) { prev = prev_active_insn (ret); if (prev && ((GET_CODE (prev) == JUMP_INSN && any_condjump_p (prev)) || GET_CODE (prev) == CALL_INSN)) replace = true; /* Empty functions get branch mispredict even when the jump destination is not visible to us. */ if (!prev && cfun->function_frequency > FUNCTION_FREQUENCY_UNLIKELY_EXECUTED) replace = true; } if (replace) { emit_insn_before (gen_return_internal_long (), ret); delete_insn (ret); } } k8_avoid_jump_misspredicts (); } /* Return nonzero when QImode register that must be represented via REX prefix is used. */ bool x86_extended_QIreg_mentioned_p (rtx insn) { int i; extract_insn_cached (insn); for (i = 0; i < recog_data.n_operands; i++) if (REG_P (recog_data.operand[i]) && REGNO (recog_data.operand[i]) >= 4) return true; return false; } /* Return nonzero when P points to register encoded via REX prefix. Called via for_each_rtx. */ static int extended_reg_mentioned_1 (rtx *p, void *data ATTRIBUTE_UNUSED) { unsigned int regno; if (!REG_P (*p)) return 0; regno = REGNO (*p); return REX_INT_REGNO_P (regno) || REX_SSE_REGNO_P (regno); } /* Return true when INSN mentions register that must be encoded using REX prefix. */ bool x86_extended_reg_mentioned_p (rtx insn) { return for_each_rtx (&PATTERN (insn), extended_reg_mentioned_1, NULL); } /* Generate an unsigned DImode/SImode to FP conversion. This is the same code optabs would emit if we didn't have TFmode patterns. */ void x86_emit_floatuns (rtx operands[2]) { rtx neglab, donelab, i0, i1, f0, in, out; enum machine_mode mode, inmode; inmode = GET_MODE (operands[1]); if (inmode != SImode && inmode != DImode) abort (); out = operands[0]; in = force_reg (inmode, operands[1]); mode = GET_MODE (out); neglab = gen_label_rtx (); donelab = gen_label_rtx (); i1 = gen_reg_rtx (Pmode); f0 = gen_reg_rtx (mode); emit_cmp_and_jump_insns (in, const0_rtx, LT, const0_rtx, Pmode, 0, neglab); emit_insn (gen_rtx_SET (VOIDmode, out, gen_rtx_FLOAT (mode, in))); emit_jump_insn (gen_jump (donelab)); emit_barrier (); emit_label (neglab); i0 = expand_simple_binop (Pmode, LSHIFTRT, in, const1_rtx, NULL, 1, OPTAB_DIRECT); i1 = expand_simple_binop (Pmode, AND, in, const1_rtx, NULL, 1, OPTAB_DIRECT); i0 = expand_simple_binop (Pmode, IOR, i0, i1, i0, 1, OPTAB_DIRECT); expand_float (f0, i0, 0); emit_insn (gen_rtx_SET (VOIDmode, out, gen_rtx_PLUS (mode, f0, f0))); emit_label (donelab); } /* Return if we do not know how to pass TYPE solely in registers. */ bool ix86_must_pass_in_stack (enum machine_mode mode, tree type) { if (default_must_pass_in_stack (mode, type)) return true; return (!TARGET_64BIT && type && mode == TImode); } /* Initialize vector TARGET via VALS. */ void ix86_expand_vector_init (rtx target, rtx vals) { enum machine_mode mode = GET_MODE (target); int elt_size = GET_MODE_SIZE (GET_MODE_INNER (mode)); int n_elts = (GET_MODE_SIZE (mode) / elt_size); int i; for (i = n_elts - 1; i >= 0; i--) if (GET_CODE (XVECEXP (vals, 0, i)) != CONST_INT && GET_CODE (XVECEXP (vals, 0, i)) != CONST_DOUBLE) break; /* Few special cases first... ... constants are best loaded from constant pool. */ if (i < 0) { emit_move_insn (target, gen_rtx_CONST_VECTOR (mode, XVEC (vals, 0))); return; } /* ... values where only first field is non-constant are best loaded from the pool and overwriten via move later. */ if (!i) { rtx op = simplify_gen_subreg (mode, XVECEXP (vals, 0, 0), GET_MODE_INNER (mode), 0); op = force_reg (mode, op); XVECEXP (vals, 0, 0) = CONST0_RTX (GET_MODE_INNER (mode)); emit_move_insn (target, gen_rtx_CONST_VECTOR (mode, XVEC (vals, 0))); switch (GET_MODE (target)) { case V2DFmode: emit_insn (gen_sse2_movsd (target, target, op)); break; case V4SFmode: emit_insn (gen_sse_movss (target, target, op)); break; default: break; } return; } /* And the busy sequence doing rotations. */ switch (GET_MODE (target)) { case V2DFmode: { rtx vecop0 = simplify_gen_subreg (V2DFmode, XVECEXP (vals, 0, 0), DFmode, 0); rtx vecop1 = simplify_gen_subreg (V2DFmode, XVECEXP (vals, 0, 1), DFmode, 0); vecop0 = force_reg (V2DFmode, vecop0); vecop1 = force_reg (V2DFmode, vecop1); emit_insn (gen_sse2_unpcklpd (target, vecop0, vecop1)); } break; case V4SFmode: { rtx vecop0 = simplify_gen_subreg (V4SFmode, XVECEXP (vals, 0, 0), SFmode, 0); rtx vecop1 = simplify_gen_subreg (V4SFmode, XVECEXP (vals, 0, 1), SFmode, 0); rtx vecop2 = simplify_gen_subreg (V4SFmode, XVECEXP (vals, 0, 2), SFmode, 0); rtx vecop3 = simplify_gen_subreg (V4SFmode, XVECEXP (vals, 0, 3), SFmode, 0); rtx tmp1 = gen_reg_rtx (V4SFmode); rtx tmp2 = gen_reg_rtx (V4SFmode); vecop0 = force_reg (V4SFmode, vecop0); vecop1 = force_reg (V4SFmode, vecop1); vecop2 = force_reg (V4SFmode, vecop2); vecop3 = force_reg (V4SFmode, vecop3); emit_insn (gen_sse_unpcklps (tmp1, vecop1, vecop3)); emit_insn (gen_sse_unpcklps (tmp2, vecop0, vecop2)); emit_insn (gen_sse_unpcklps (target, tmp2, tmp1)); } break; default: abort (); } } #include "gt-i386.h" diff --git a/contrib/gcc/config/i386/i386.md b/contrib/gcc/config/i386/i386.md index 93d9dcdba150..0fbe00b66e3c 100644 --- a/contrib/gcc/config/i386/i386.md +++ b/contrib/gcc/config/i386/i386.md @@ -1,23021 +1,23021 @@ ;; GCC machine description for IA-32 and x86-64. ;; Copyright (C) 1988, 1994, 1995, 1996, 1997, 1998, 1999, 2000, ;; 2001, 2002, 2003, 2004 ;; Free Software Foundation, Inc. ;; Mostly by William Schelter. ;; x86_64 support added by Jan Hubicka ;; ;; This file is part of GCC. ;; ;; GCC is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; ;; GCC is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with GCC; see the file COPYING. If not, write to ;; the Free Software Foundation, 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. */ ;; ;; The original PO technology requires these to be ordered by speed, ;; so that assigner will pick the fastest. ;; ;; See file "rtl.def" for documentation on define_insn, match_*, et. al. ;; ;; Macro REG_CLASS_FROM_LETTER in file i386.h defines the register ;; constraint letters. ;; ;; The special asm out single letter directives following a '%' are: ;; 'z' mov%z1 would be movl, movw, or movb depending on the mode of ;; operands[1]. ;; 'L' Print the opcode suffix for a 32-bit integer opcode. ;; 'W' Print the opcode suffix for a 16-bit integer opcode. ;; 'B' Print the opcode suffix for an 8-bit integer opcode. ;; 'Q' Print the opcode suffix for a 64-bit float opcode. ;; 'S' Print the opcode suffix for a 32-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. ;; 'w' Likewise, print the HImode name of the register. ;; '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. ;; UNSPEC usage: (define_constants [; Relocation specifiers (UNSPEC_GOT 0) (UNSPEC_GOTOFF 1) (UNSPEC_GOTPCREL 2) (UNSPEC_GOTTPOFF 3) (UNSPEC_TPOFF 4) (UNSPEC_NTPOFF 5) (UNSPEC_DTPOFF 6) (UNSPEC_GOTNTPOFF 7) (UNSPEC_INDNTPOFF 8) ; Prologue support (UNSPEC_STACK_ALLOC 11) (UNSPEC_SET_GOT 12) (UNSPEC_SSE_PROLOGUE_SAVE 13) ; TLS support (UNSPEC_TP 15) (UNSPEC_TLS_GD 16) (UNSPEC_TLS_LD_BASE 17) ; Other random patterns (UNSPEC_SCAS 20) (UNSPEC_SIN 21) (UNSPEC_COS 22) (UNSPEC_FNSTSW 24) (UNSPEC_SAHF 25) (UNSPEC_FSTCW 26) (UNSPEC_ADD_CARRY 27) (UNSPEC_FLDCW 28) ; For SSE/MMX support: (UNSPEC_FIX 30) (UNSPEC_MASKMOV 32) (UNSPEC_MOVMSK 33) (UNSPEC_MOVNT 34) (UNSPEC_MOVA 38) (UNSPEC_MOVU 39) (UNSPEC_SHUFFLE 41) (UNSPEC_RCP 42) (UNSPEC_RSQRT 43) (UNSPEC_SFENCE 44) (UNSPEC_NOP 45) ; prevents combiner cleverness (UNSPEC_PAVGUSB 49) (UNSPEC_PFRCP 50) (UNSPEC_PFRCPIT1 51) (UNSPEC_PFRCPIT2 52) (UNSPEC_PFRSQRT 53) (UNSPEC_PFRSQIT1 54) (UNSPEC_PSHUFLW 55) (UNSPEC_PSHUFHW 56) (UNSPEC_MFENCE 59) (UNSPEC_LFENCE 60) (UNSPEC_PSADBW 61) (UNSPEC_ADDSUB 71) (UNSPEC_HADD 72) (UNSPEC_HSUB 73) (UNSPEC_MOVSHDUP 74) (UNSPEC_MOVSLDUP 75) (UNSPEC_LDQQU 76) (UNSPEC_MOVDDUP 77) ; x87 Floating point (UNSPEC_FPATAN 65) (UNSPEC_FYL2X 66) (UNSPEC_FSCALE 67) (UNSPEC_FRNDINT 68) (UNSPEC_F2XM1 69) ; REP instruction (UNSPEC_REP 75) ]) (define_constants [(UNSPECV_BLOCKAGE 0) (UNSPECV_STACK_PROBE 10) (UNSPECV_EH_RETURN 13) (UNSPECV_EMMS 31) (UNSPECV_LDMXCSR 37) (UNSPECV_STMXCSR 40) (UNSPECV_FEMMS 46) (UNSPECV_CLFLUSH 57) (UNSPECV_ALIGN 68) (UNSPECV_MONITOR 69) (UNSPECV_MWAIT 70) ]) ;; Insns whose names begin with "x86_" are emitted by gen_FOO calls ;; from i386.c. ;; In C guard expressions, put expressions which may be compile-time ;; constants first. This allows for better optimization. For ;; example, write "TARGET_64BIT && reload_completed", not ;; "reload_completed && TARGET_64BIT". ;; Processor type. This attribute must exactly match the processor_type ;; enumeration in i386.h. (define_attr "cpu" "i386,i486,pentium,pentiumpro,k6,athlon,pentium4,k8" (const (symbol_ref "ix86_tune"))) ;; A basic instruction type. Refinements due to arguments to be ;; provided in other attributes. (define_attr "type" "other,multi, alu,alu1,negnot,imov,imovx,lea, incdec,ishift,ishift1,rotate,rotate1,imul,idiv, icmp,test,ibr,setcc,icmov, push,pop,call,callv,leave, str,cld, fmov,fop,fsgn,fmul,fdiv,fpspc,fcmov,fcmp,fxch,fistp, sselog,sseiadd,sseishft,sseimul, sse,ssemov,sseadd,ssemul,ssecmp,ssecomi,ssecvt,sseicvt,ssediv, mmx,mmxmov,mmxadd,mmxmul,mmxcmp,mmxcvt,mmxshft" (const_string "other")) ;; Main data type used by the insn (define_attr "mode" "unknown,none,QI,HI,SI,DI,SF,DF,XF,TI,V4SF,V2DF,V2SF" (const_string "unknown")) ;; The CPU unit operations uses. (define_attr "unit" "integer,i387,sse,mmx,unknown" (cond [(eq_attr "type" "fmov,fop,fsgn,fmul,fdiv,fpspc,fcmov,fcmp,fxch,fistp") (const_string "i387") (eq_attr "type" "sselog,sseiadd,sseishft,sseimul, sse,ssemov,sseadd,ssemul,ssecmp,ssecomi,ssecvt,sseicvt,ssediv") (const_string "sse") (eq_attr "type" "mmx,mmxmov,mmxadd,mmxmul,mmxcmp,mmxcvt,mmxshft") (const_string "mmx") (eq_attr "type" "other") (const_string "unknown")] (const_string "integer"))) ;; The (bounding maximum) length of an instruction immediate. (define_attr "length_immediate" "" (cond [(eq_attr "type" "incdec,setcc,icmov,str,cld,lea,other,multi,idiv,leave") (const_int 0) (eq_attr "unit" "i387,sse,mmx") (const_int 0) (eq_attr "type" "alu,alu1,negnot,imovx,ishift,rotate,ishift1,rotate1, imul,icmp,push,pop") (symbol_ref "ix86_attr_length_immediate_default(insn,1)") (eq_attr "type" "imov,test") (symbol_ref "ix86_attr_length_immediate_default(insn,0)") (eq_attr "type" "call") (if_then_else (match_operand 0 "constant_call_address_operand" "") (const_int 4) (const_int 0)) (eq_attr "type" "callv") (if_then_else (match_operand 1 "constant_call_address_operand" "") (const_int 4) (const_int 0)) ;; We don't know the size before shorten_branches. Expect ;; the instruction to fit for better scheduling. (eq_attr "type" "ibr") (const_int 1) ] (symbol_ref "/* Update immediate_length and other attributes! */ abort(),1"))) ;; The (bounding maximum) length of an instruction address. (define_attr "length_address" "" (cond [(eq_attr "type" "str,cld,other,multi,fxch") (const_int 0) (and (eq_attr "type" "call") (match_operand 0 "constant_call_address_operand" "")) (const_int 0) (and (eq_attr "type" "callv") (match_operand 1 "constant_call_address_operand" "")) (const_int 0) ] (symbol_ref "ix86_attr_length_address_default (insn)"))) ;; Set when length prefix is used. (define_attr "prefix_data16" "" (if_then_else (ior (eq_attr "mode" "HI") (and (eq_attr "unit" "sse") (eq_attr "mode" "V2DF"))) (const_int 1) (const_int 0))) ;; Set when string REP prefix is used. (define_attr "prefix_rep" "" (if_then_else (and (eq_attr "unit" "sse") (eq_attr "mode" "SF,DF")) (const_int 1) (const_int 0))) ;; Set when 0f opcode prefix is used. (define_attr "prefix_0f" "" (if_then_else (ior (eq_attr "type" "imovx,setcc,icmov") (eq_attr "unit" "sse,mmx")) (const_int 1) (const_int 0))) ;; Set when 0f opcode prefix is used. (define_attr "prefix_rex" "" (cond [(and (eq_attr "mode" "DI") (eq_attr "type" "!push,pop,call,callv,leave,ibr")) (const_int 1) (and (eq_attr "mode" "QI") (ne (symbol_ref "x86_extended_QIreg_mentioned_p (insn)") (const_int 0))) (const_int 1) (ne (symbol_ref "x86_extended_reg_mentioned_p (insn)") (const_int 0)) (const_int 1) ] (const_int 0))) ;; Set when modrm byte is used. (define_attr "modrm" "" (cond [(eq_attr "type" "str,cld,leave") (const_int 0) (eq_attr "unit" "i387") (const_int 0) (and (eq_attr "type" "incdec") (ior (match_operand:SI 1 "register_operand" "") (match_operand:HI 1 "register_operand" ""))) (const_int 0) (and (eq_attr "type" "push") (not (match_operand 1 "memory_operand" ""))) (const_int 0) (and (eq_attr "type" "pop") (not (match_operand 0 "memory_operand" ""))) (const_int 0) (and (eq_attr "type" "imov") (and (match_operand 0 "register_operand" "") (match_operand 1 "immediate_operand" ""))) (const_int 0) (and (eq_attr "type" "call") (match_operand 0 "constant_call_address_operand" "")) (const_int 0) (and (eq_attr "type" "callv") (match_operand 1 "constant_call_address_operand" "")) (const_int 0) ] (const_int 1))) ;; The (bounding maximum) length of an instruction in bytes. ;; ??? fistp is in fact fldcw/fistp/fldcw sequence. Later we may want ;; to split it and compute proper length as for other insns. (define_attr "length" "" (cond [(eq_attr "type" "other,multi,fistp") (const_int 16) (eq_attr "type" "fcmp") (const_int 4) (eq_attr "unit" "i387") (plus (const_int 2) (plus (attr "prefix_data16") (attr "length_address")))] (plus (plus (attr "modrm") (plus (attr "prefix_0f") (plus (attr "prefix_rex") (const_int 1)))) (plus (attr "prefix_rep") (plus (attr "prefix_data16") (plus (attr "length_immediate") (attr "length_address"))))))) ;; The `memory' attribute is `none' if no memory is referenced, `load' or ;; `store' if there is a simple memory reference therein, or `unknown' ;; if the instruction is complex. (define_attr "memory" "none,load,store,both,unknown" (cond [(eq_attr "type" "other,multi,str") (const_string "unknown") (eq_attr "type" "lea,fcmov,fpspc,cld") (const_string "none") (eq_attr "type" "fistp,leave") (const_string "both") (eq_attr "type" "push") (if_then_else (match_operand 1 "memory_operand" "") (const_string "both") (const_string "store")) (eq_attr "type" "pop") (if_then_else (match_operand 0 "memory_operand" "") (const_string "both") (const_string "load")) (eq_attr "type" "setcc") (if_then_else (match_operand 0 "memory_operand" "") (const_string "store") (const_string "none")) (eq_attr "type" "icmp,test,ssecmp,ssecomi,mmxcmp,fcmp") (if_then_else (ior (match_operand 0 "memory_operand" "") (match_operand 1 "memory_operand" "")) (const_string "load") (const_string "none")) (eq_attr "type" "ibr") (if_then_else (match_operand 0 "memory_operand" "") (const_string "load") (const_string "none")) (eq_attr "type" "call") (if_then_else (match_operand 0 "constant_call_address_operand" "") (const_string "none") (const_string "load")) (eq_attr "type" "callv") (if_then_else (match_operand 1 "constant_call_address_operand" "") (const_string "none") (const_string "load")) (and (eq_attr "type" "alu1,negnot,ishift1") (match_operand 1 "memory_operand" "")) (const_string "both") (and (match_operand 0 "memory_operand" "") (match_operand 1 "memory_operand" "")) (const_string "both") (match_operand 0 "memory_operand" "") (const_string "store") (match_operand 1 "memory_operand" "") (const_string "load") (and (eq_attr "type" "!alu1,negnot,ishift1, imov,imovx,icmp,test, fmov,fcmp,fsgn, sse,ssemov,ssecmp,ssecomi,ssecvt,sseicvt, mmx,mmxmov,mmxcmp,mmxcvt") (match_operand 2 "memory_operand" "")) (const_string "load") (and (eq_attr "type" "icmov") (match_operand 3 "memory_operand" "")) (const_string "load") ] (const_string "none"))) ;; Indicates if an instruction has both an immediate and a displacement. (define_attr "imm_disp" "false,true,unknown" (cond [(eq_attr "type" "other,multi") (const_string "unknown") (and (eq_attr "type" "icmp,test,imov,alu1,ishift1,rotate1") (and (match_operand 0 "memory_displacement_operand" "") (match_operand 1 "immediate_operand" ""))) (const_string "true") (and (eq_attr "type" "alu,ishift,rotate,imul,idiv") (and (match_operand 0 "memory_displacement_operand" "") (match_operand 2 "immediate_operand" ""))) (const_string "true") ] (const_string "false"))) ;; Indicates if an FP operation has an integer source. (define_attr "fp_int_src" "false,true" (const_string "false")) ;; Describe a user's asm statement. (define_asm_attributes [(set_attr "length" "128") (set_attr "type" "multi")]) (include "pentium.md") (include "ppro.md") (include "k6.md") (include "athlon.md") ;; Compare instructions. ;; All compare insns have expanders that save the operands away without ;; actually generating RTL. The bCOND or sCOND (emitted immediately ;; after the cmp) will actually emit the cmpM. (define_expand "cmpdi" [(set (reg:CC 17) (compare:CC (match_operand:DI 0 "nonimmediate_operand" "") (match_operand:DI 1 "x86_64_general_operand" "")))] "" { if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) operands[0] = force_reg (DImode, operands[0]); ix86_compare_op0 = operands[0]; ix86_compare_op1 = operands[1]; DONE; }) (define_expand "cmpsi" [(set (reg:CC 17) (compare:CC (match_operand:SI 0 "cmpsi_operand" "") (match_operand:SI 1 "general_operand" "")))] "" { if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) operands[0] = force_reg (SImode, operands[0]); ix86_compare_op0 = operands[0]; ix86_compare_op1 = operands[1]; DONE; }) (define_expand "cmphi" [(set (reg:CC 17) (compare:CC (match_operand:HI 0 "nonimmediate_operand" "") (match_operand:HI 1 "general_operand" "")))] "" { if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) operands[0] = force_reg (HImode, operands[0]); ix86_compare_op0 = operands[0]; ix86_compare_op1 = operands[1]; DONE; }) (define_expand "cmpqi" [(set (reg:CC 17) (compare:CC (match_operand:QI 0 "nonimmediate_operand" "") (match_operand:QI 1 "general_operand" "")))] "TARGET_QIMODE_MATH" { if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) operands[0] = force_reg (QImode, operands[0]); ix86_compare_op0 = operands[0]; ix86_compare_op1 = operands[1]; DONE; }) (define_insn "cmpdi_ccno_1_rex64" [(set (reg 17) (compare (match_operand:DI 0 "nonimmediate_operand" "r,?mr") (match_operand:DI 1 "const0_operand" "n,n")))] "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode)" "@ test{q}\t{%0, %0|%0, %0} cmp{q}\t{%1, %0|%0, %1}" [(set_attr "type" "test,icmp") (set_attr "length_immediate" "0,1") (set_attr "mode" "DI")]) (define_insn "*cmpdi_minus_1_rex64" [(set (reg 17) (compare (minus:DI (match_operand:DI 0 "nonimmediate_operand" "rm,r") (match_operand:DI 1 "x86_64_general_operand" "re,mr")) (const_int 0)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCGOCmode)" "cmp{q}\t{%1, %0|%0, %1}" [(set_attr "type" "icmp") (set_attr "mode" "DI")]) (define_expand "cmpdi_1_rex64" [(set (reg:CC 17) (compare:CC (match_operand:DI 0 "nonimmediate_operand" "") (match_operand:DI 1 "general_operand" "")))] "TARGET_64BIT" "") (define_insn "cmpdi_1_insn_rex64" [(set (reg 17) (compare (match_operand:DI 0 "nonimmediate_operand" "mr,r") (match_operand:DI 1 "x86_64_general_operand" "re,mr")))] "TARGET_64BIT && ix86_match_ccmode (insn, CCmode)" "cmp{q}\t{%1, %0|%0, %1}" [(set_attr "type" "icmp") (set_attr "mode" "DI")]) (define_insn "*cmpsi_ccno_1" [(set (reg 17) (compare (match_operand:SI 0 "nonimmediate_operand" "r,?mr") (match_operand:SI 1 "const0_operand" "n,n")))] "ix86_match_ccmode (insn, CCNOmode)" "@ test{l}\t{%0, %0|%0, %0} cmp{l}\t{%1, %0|%0, %1}" [(set_attr "type" "test,icmp") (set_attr "length_immediate" "0,1") (set_attr "mode" "SI")]) (define_insn "*cmpsi_minus_1" [(set (reg 17) (compare (minus:SI (match_operand:SI 0 "nonimmediate_operand" "rm,r") (match_operand:SI 1 "general_operand" "ri,mr")) (const_int 0)))] "ix86_match_ccmode (insn, CCGOCmode)" "cmp{l}\t{%1, %0|%0, %1}" [(set_attr "type" "icmp") (set_attr "mode" "SI")]) (define_expand "cmpsi_1" [(set (reg:CC 17) (compare:CC (match_operand:SI 0 "nonimmediate_operand" "rm,r") (match_operand:SI 1 "general_operand" "ri,mr")))] "" "") (define_insn "*cmpsi_1_insn" [(set (reg 17) (compare (match_operand:SI 0 "nonimmediate_operand" "rm,r") (match_operand:SI 1 "general_operand" "ri,mr")))] "(GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) && ix86_match_ccmode (insn, CCmode)" "cmp{l}\t{%1, %0|%0, %1}" [(set_attr "type" "icmp") (set_attr "mode" "SI")]) (define_insn "*cmphi_ccno_1" [(set (reg 17) (compare (match_operand:HI 0 "nonimmediate_operand" "r,?mr") (match_operand:HI 1 "const0_operand" "n,n")))] "ix86_match_ccmode (insn, CCNOmode)" "@ test{w}\t{%0, %0|%0, %0} cmp{w}\t{%1, %0|%0, %1}" [(set_attr "type" "test,icmp") (set_attr "length_immediate" "0,1") (set_attr "mode" "HI")]) (define_insn "*cmphi_minus_1" [(set (reg 17) (compare (minus:HI (match_operand:HI 0 "nonimmediate_operand" "rm,r") (match_operand:HI 1 "general_operand" "ri,mr")) (const_int 0)))] "ix86_match_ccmode (insn, CCGOCmode)" "cmp{w}\t{%1, %0|%0, %1}" [(set_attr "type" "icmp") (set_attr "mode" "HI")]) (define_insn "*cmphi_1" [(set (reg 17) (compare (match_operand:HI 0 "nonimmediate_operand" "rm,r") (match_operand:HI 1 "general_operand" "ri,mr")))] "(GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) && ix86_match_ccmode (insn, CCmode)" "cmp{w}\t{%1, %0|%0, %1}" [(set_attr "type" "icmp") (set_attr "mode" "HI")]) (define_insn "*cmpqi_ccno_1" [(set (reg 17) (compare (match_operand:QI 0 "nonimmediate_operand" "q,?mq") (match_operand:QI 1 "const0_operand" "n,n")))] "ix86_match_ccmode (insn, CCNOmode)" "@ test{b}\t{%0, %0|%0, %0} cmp{b}\t{$0, %0|%0, 0}" [(set_attr "type" "test,icmp") (set_attr "length_immediate" "0,1") (set_attr "mode" "QI")]) (define_insn "*cmpqi_1" [(set (reg 17) (compare (match_operand:QI 0 "nonimmediate_operand" "qm,q") (match_operand:QI 1 "general_operand" "qi,mq")))] "(GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) && ix86_match_ccmode (insn, CCmode)" "cmp{b}\t{%1, %0|%0, %1}" [(set_attr "type" "icmp") (set_attr "mode" "QI")]) (define_insn "*cmpqi_minus_1" [(set (reg 17) (compare (minus:QI (match_operand:QI 0 "nonimmediate_operand" "qm,q") (match_operand:QI 1 "general_operand" "qi,mq")) (const_int 0)))] "ix86_match_ccmode (insn, CCGOCmode)" "cmp{b}\t{%1, %0|%0, %1}" [(set_attr "type" "icmp") (set_attr "mode" "QI")]) (define_insn "*cmpqi_ext_1" [(set (reg 17) (compare (match_operand:QI 0 "general_operand" "Qm") (subreg:QI (zero_extract:SI (match_operand 1 "ext_register_operand" "Q") (const_int 8) (const_int 8)) 0)))] "!TARGET_64BIT && ix86_match_ccmode (insn, CCmode)" "cmp{b}\t{%h1, %0|%0, %h1}" [(set_attr "type" "icmp") (set_attr "mode" "QI")]) (define_insn "*cmpqi_ext_1_rex64" [(set (reg 17) (compare (match_operand:QI 0 "register_operand" "Q") (subreg:QI (zero_extract:SI (match_operand 1 "ext_register_operand" "Q") (const_int 8) (const_int 8)) 0)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCmode)" "cmp{b}\t{%h1, %0|%0, %h1}" [(set_attr "type" "icmp") (set_attr "mode" "QI")]) (define_insn "*cmpqi_ext_2" [(set (reg 17) (compare (subreg:QI (zero_extract:SI (match_operand 0 "ext_register_operand" "Q") (const_int 8) (const_int 8)) 0) (match_operand:QI 1 "const0_operand" "n")))] "ix86_match_ccmode (insn, CCNOmode)" "test{b}\t%h0, %h0" [(set_attr "type" "test") (set_attr "length_immediate" "0") (set_attr "mode" "QI")]) (define_expand "cmpqi_ext_3" [(set (reg:CC 17) (compare:CC (subreg:QI (zero_extract:SI (match_operand 0 "ext_register_operand" "") (const_int 8) (const_int 8)) 0) (match_operand:QI 1 "general_operand" "")))] "" "") (define_insn "cmpqi_ext_3_insn" [(set (reg 17) (compare (subreg:QI (zero_extract:SI (match_operand 0 "ext_register_operand" "Q") (const_int 8) (const_int 8)) 0) (match_operand:QI 1 "general_operand" "Qmn")))] "!TARGET_64BIT && ix86_match_ccmode (insn, CCmode)" "cmp{b}\t{%1, %h0|%h0, %1}" [(set_attr "type" "icmp") (set_attr "mode" "QI")]) (define_insn "cmpqi_ext_3_insn_rex64" [(set (reg 17) (compare (subreg:QI (zero_extract:SI (match_operand 0 "ext_register_operand" "Q") (const_int 8) (const_int 8)) 0) (match_operand:QI 1 "nonmemory_operand" "Qn")))] "TARGET_64BIT && ix86_match_ccmode (insn, CCmode)" "cmp{b}\t{%1, %h0|%h0, %1}" [(set_attr "type" "icmp") (set_attr "mode" "QI")]) (define_insn "*cmpqi_ext_4" [(set (reg 17) (compare (subreg:QI (zero_extract:SI (match_operand 0 "ext_register_operand" "Q") (const_int 8) (const_int 8)) 0) (subreg:QI (zero_extract:SI (match_operand 1 "ext_register_operand" "Q") (const_int 8) (const_int 8)) 0)))] "ix86_match_ccmode (insn, CCmode)" "cmp{b}\t{%h1, %h0|%h0, %h1}" [(set_attr "type" "icmp") (set_attr "mode" "QI")]) ;; These implement float point compares. ;; %%% See if we can get away with VOIDmode operands on the actual insns, ;; which would allow mix and match FP modes on the compares. Which is what ;; the old patterns did, but with many more of them. (define_expand "cmpxf" [(set (reg:CC 17) (compare:CC (match_operand:XF 0 "cmp_fp_expander_operand" "") (match_operand:XF 1 "cmp_fp_expander_operand" "")))] "TARGET_80387" { ix86_compare_op0 = operands[0]; ix86_compare_op1 = operands[1]; DONE; }) (define_expand "cmpdf" [(set (reg:CC 17) (compare:CC (match_operand:DF 0 "cmp_fp_expander_operand" "") (match_operand:DF 1 "cmp_fp_expander_operand" "")))] "TARGET_80387 || TARGET_SSE2" { ix86_compare_op0 = operands[0]; ix86_compare_op1 = operands[1]; DONE; }) (define_expand "cmpsf" [(set (reg:CC 17) (compare:CC (match_operand:SF 0 "cmp_fp_expander_operand" "") (match_operand:SF 1 "cmp_fp_expander_operand" "")))] "TARGET_80387 || TARGET_SSE" { ix86_compare_op0 = operands[0]; ix86_compare_op1 = operands[1]; DONE; }) ;; FP compares, step 1: ;; Set the FP condition codes. ;; ;; CCFPmode compare with exceptions ;; CCFPUmode compare with no exceptions ;; %%% It is an unfortunate fact that ftst has no non-popping variant, ;; and that fp moves clobber the condition codes, and that there is ;; currently no way to describe this fact to reg-stack. So there are ;; no splitters yet for this. ;; %%% YIKES! This scheme does not retain a strong connection between ;; the real compare and the ultimate cc0 user, so CC_REVERSE does not ;; work! Only allow tos/mem with tos in op 0. ;; ;; Hmm, of course, this is what the actual _hardware_ does. Perhaps ;; things aren't as bad as they sound... (define_insn "*cmpfp_0" [(set (match_operand:HI 0 "register_operand" "=a") (unspec:HI [(compare:CCFP (match_operand 1 "register_operand" "f") (match_operand 2 "const0_operand" "X"))] UNSPEC_FNSTSW))] "TARGET_80387 && FLOAT_MODE_P (GET_MODE (operands[1])) && GET_MODE (operands[1]) == GET_MODE (operands[2])" { if (find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "ftst\;fnstsw\t%0\;fstp\t%y0"; else return "ftst\;fnstsw\t%0"; } [(set_attr "type" "multi") (set (attr "mode") (cond [(match_operand:SF 1 "" "") (const_string "SF") (match_operand:DF 1 "" "") (const_string "DF") ] (const_string "XF")))]) ;; We may not use "#" to split and emit these, since the REG_DEAD notes ;; used to manage the reg stack popping would not be preserved. (define_insn "*cmpfp_2_sf" [(set (reg:CCFP 18) (compare:CCFP (match_operand:SF 0 "register_operand" "f") (match_operand:SF 1 "nonimmediate_operand" "fm")))] "TARGET_80387" "* return output_fp_compare (insn, operands, 0, 0);" [(set_attr "type" "fcmp") (set_attr "mode" "SF")]) (define_insn "*cmpfp_2_sf_1" [(set (match_operand:HI 0 "register_operand" "=a") (unspec:HI [(compare:CCFP (match_operand:SF 1 "register_operand" "f") (match_operand:SF 2 "nonimmediate_operand" "fm"))] UNSPEC_FNSTSW))] "TARGET_80387" "* return output_fp_compare (insn, operands, 2, 0);" [(set_attr "type" "fcmp") (set_attr "mode" "SF")]) (define_insn "*cmpfp_2_df" [(set (reg:CCFP 18) (compare:CCFP (match_operand:DF 0 "register_operand" "f") (match_operand:DF 1 "nonimmediate_operand" "fm")))] "TARGET_80387" "* return output_fp_compare (insn, operands, 0, 0);" [(set_attr "type" "fcmp") (set_attr "mode" "DF")]) (define_insn "*cmpfp_2_df_1" [(set (match_operand:HI 0 "register_operand" "=a") (unspec:HI [(compare:CCFP (match_operand:DF 1 "register_operand" "f") (match_operand:DF 2 "nonimmediate_operand" "fm"))] UNSPEC_FNSTSW))] "TARGET_80387" "* return output_fp_compare (insn, operands, 2, 0);" [(set_attr "type" "multi") (set_attr "mode" "DF")]) (define_insn "*cmpfp_2_xf" [(set (reg:CCFP 18) (compare:CCFP (match_operand:XF 0 "register_operand" "f") (match_operand:XF 1 "register_operand" "f")))] "TARGET_80387" "* return output_fp_compare (insn, operands, 0, 0);" [(set_attr "type" "fcmp") (set_attr "mode" "XF")]) (define_insn "*cmpfp_2_xf_1" [(set (match_operand:HI 0 "register_operand" "=a") (unspec:HI [(compare:CCFP (match_operand:XF 1 "register_operand" "f") (match_operand:XF 2 "register_operand" "f"))] UNSPEC_FNSTSW))] "TARGET_80387" "* return output_fp_compare (insn, operands, 2, 0);" [(set_attr "type" "multi") (set_attr "mode" "XF")]) (define_insn "*cmpfp_2u" [(set (reg:CCFPU 18) (compare:CCFPU (match_operand 0 "register_operand" "f") (match_operand 1 "register_operand" "f")))] "TARGET_80387 && FLOAT_MODE_P (GET_MODE (operands[0])) && GET_MODE (operands[0]) == GET_MODE (operands[1])" "* return output_fp_compare (insn, operands, 0, 1);" [(set_attr "type" "fcmp") (set (attr "mode") (cond [(match_operand:SF 1 "" "") (const_string "SF") (match_operand:DF 1 "" "") (const_string "DF") ] (const_string "XF")))]) (define_insn "*cmpfp_2u_1" [(set (match_operand:HI 0 "register_operand" "=a") (unspec:HI [(compare:CCFPU (match_operand 1 "register_operand" "f") (match_operand 2 "register_operand" "f"))] UNSPEC_FNSTSW))] "TARGET_80387 && FLOAT_MODE_P (GET_MODE (operands[1])) && GET_MODE (operands[1]) == GET_MODE (operands[2])" "* return output_fp_compare (insn, operands, 2, 1);" [(set_attr "type" "multi") (set (attr "mode") (cond [(match_operand:SF 1 "" "") (const_string "SF") (match_operand:DF 1 "" "") (const_string "DF") ] (const_string "XF")))]) ;; Patterns to match the SImode-in-memory ficom instructions. ;; ;; %%% Play games with accepting gp registers, as otherwise we have to ;; force them to memory during rtl generation, which is no good. We ;; can get rid of this once we teach reload to do memory input reloads ;; via pushes. (define_insn "*ficom_1" [(set (reg:CCFP 18) (compare:CCFP (match_operand 0 "register_operand" "f,f") (float (match_operand:SI 1 "nonimmediate_operand" "m,?r"))))] "0 && TARGET_80387 && FLOAT_MODE_P (GET_MODE (operands[0])) && GET_MODE (XEXP (SET_SRC (PATTERN (insn)), 1)) == GET_MODE (operands[0])" "#") ;; Split the not-really-implemented gp register case into a ;; push-op-pop sequence. ;; ;; %%% This is most efficient, but am I gonna get in trouble ;; for separating cc0_setter and cc0_user? (define_split [(set (reg:CCFP 18) (compare:CCFP (match_operand:SF 0 "register_operand" "") (float (match_operand:SI 1 "register_operand" ""))))] "0 && TARGET_80387 && reload_completed" [(set (mem:SI (pre_dec:SI (reg:SI 7))) (match_dup 1)) (set (reg:CCFP 18) (compare:CCFP (match_dup 0) (match_dup 2))) (parallel [(set (match_dup 1) (mem:SI (reg:SI 7))) (set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4)))])] "operands[2] = gen_rtx_MEM (Pmode, stack_pointer_rtx); operands[2] = gen_rtx_FLOAT (GET_MODE (operands[0]), operands[2]);") ;; FP compares, step 2 ;; Move the fpsw to ax. (define_insn "*x86_fnstsw_1" [(set (match_operand:HI 0 "register_operand" "=a") (unspec:HI [(reg 18)] UNSPEC_FNSTSW))] "TARGET_80387" "fnstsw\t%0" [(set_attr "length" "2") (set_attr "mode" "SI") (set_attr "unit" "i387") (set_attr "ppro_uops" "few")]) ;; FP compares, step 3 ;; Get ax into flags, general case. (define_insn "x86_sahf_1" [(set (reg:CC 17) (unspec:CC [(match_operand:HI 0 "register_operand" "a")] UNSPEC_SAHF))] "!TARGET_64BIT" "sahf" [(set_attr "length" "1") (set_attr "athlon_decode" "vector") (set_attr "mode" "SI") (set_attr "ppro_uops" "one")]) ;; Pentium Pro can do steps 1 through 3 in one go. (define_insn "*cmpfp_i" [(set (reg:CCFP 17) (compare:CCFP (match_operand 0 "register_operand" "f") (match_operand 1 "register_operand" "f")))] "TARGET_80387 && TARGET_CMOVE && !SSE_FLOAT_MODE_P (GET_MODE (operands[0])) && FLOAT_MODE_P (GET_MODE (operands[0])) && GET_MODE (operands[0]) == GET_MODE (operands[0])" "* return output_fp_compare (insn, operands, 1, 0);" [(set_attr "type" "fcmp") (set (attr "mode") (cond [(match_operand:SF 1 "" "") (const_string "SF") (match_operand:DF 1 "" "") (const_string "DF") ] (const_string "XF"))) (set_attr "athlon_decode" "vector")]) (define_insn "*cmpfp_i_sse" [(set (reg:CCFP 17) (compare:CCFP (match_operand 0 "register_operand" "f#x,x#f") (match_operand 1 "nonimmediate_operand" "f#x,xm#f")))] "TARGET_80387 && SSE_FLOAT_MODE_P (GET_MODE (operands[0])) && GET_MODE (operands[0]) == GET_MODE (operands[0])" "* return output_fp_compare (insn, operands, 1, 0);" [(set_attr "type" "fcmp,ssecomi") (set (attr "mode") (if_then_else (match_operand:SF 1 "" "") (const_string "SF") (const_string "DF"))) (set_attr "athlon_decode" "vector")]) (define_insn "*cmpfp_i_sse_only" [(set (reg:CCFP 17) (compare:CCFP (match_operand 0 "register_operand" "x") (match_operand 1 "nonimmediate_operand" "xm")))] "SSE_FLOAT_MODE_P (GET_MODE (operands[0])) && GET_MODE (operands[0]) == GET_MODE (operands[0])" "* return output_fp_compare (insn, operands, 1, 0);" [(set_attr "type" "ssecomi") (set (attr "mode") (if_then_else (match_operand:SF 1 "" "") (const_string "SF") (const_string "DF"))) (set_attr "athlon_decode" "vector")]) (define_insn "*cmpfp_iu" [(set (reg:CCFPU 17) (compare:CCFPU (match_operand 0 "register_operand" "f") (match_operand 1 "register_operand" "f")))] "TARGET_80387 && TARGET_CMOVE && !SSE_FLOAT_MODE_P (GET_MODE (operands[0])) && FLOAT_MODE_P (GET_MODE (operands[0])) && GET_MODE (operands[0]) == GET_MODE (operands[1])" "* return output_fp_compare (insn, operands, 1, 1);" [(set_attr "type" "fcmp") (set (attr "mode") (cond [(match_operand:SF 1 "" "") (const_string "SF") (match_operand:DF 1 "" "") (const_string "DF") ] (const_string "XF"))) (set_attr "athlon_decode" "vector")]) (define_insn "*cmpfp_iu_sse" [(set (reg:CCFPU 17) (compare:CCFPU (match_operand 0 "register_operand" "f#x,x#f") (match_operand 1 "nonimmediate_operand" "f#x,xm#f")))] "TARGET_80387 && SSE_FLOAT_MODE_P (GET_MODE (operands[0])) && GET_MODE (operands[0]) == GET_MODE (operands[1])" "* return output_fp_compare (insn, operands, 1, 1);" [(set_attr "type" "fcmp,ssecomi") (set (attr "mode") (if_then_else (match_operand:SF 1 "" "") (const_string "SF") (const_string "DF"))) (set_attr "athlon_decode" "vector")]) (define_insn "*cmpfp_iu_sse_only" [(set (reg:CCFPU 17) (compare:CCFPU (match_operand 0 "register_operand" "x") (match_operand 1 "nonimmediate_operand" "xm")))] "SSE_FLOAT_MODE_P (GET_MODE (operands[0])) && GET_MODE (operands[0]) == GET_MODE (operands[1])" "* return output_fp_compare (insn, operands, 1, 1);" [(set_attr "type" "ssecomi") (set (attr "mode") (if_then_else (match_operand:SF 1 "" "") (const_string "SF") (const_string "DF"))) (set_attr "athlon_decode" "vector")]) ;; Move instructions. ;; General case of fullword move. (define_expand "movsi" [(set (match_operand:SI 0 "nonimmediate_operand" "") (match_operand:SI 1 "general_operand" ""))] "" "ix86_expand_move (SImode, operands); DONE;") ;; Push/pop instructions. They are separate since autoinc/dec is not a ;; general_operand. ;; ;; %%% We don't use a post-inc memory reference because x86 is not a ;; general AUTO_INC_DEC host, which impacts how it is treated in flow. ;; Changing this impacts compiler performance on other non-AUTO_INC_DEC ;; targets without our curiosities, and it is just as easy to represent ;; this differently. (define_insn "*pushsi2" [(set (match_operand:SI 0 "push_operand" "=<") (match_operand:SI 1 "general_no_elim_operand" "ri*m"))] "!TARGET_64BIT" "push{l}\t%1" [(set_attr "type" "push") (set_attr "mode" "SI")]) ;; For 64BIT abi we always round up to 8 bytes. (define_insn "*pushsi2_rex64" [(set (match_operand:SI 0 "push_operand" "=X") (match_operand:SI 1 "nonmemory_no_elim_operand" "ri"))] "TARGET_64BIT" "push{q}\t%q1" [(set_attr "type" "push") (set_attr "mode" "SI")]) (define_insn "*pushsi2_prologue" [(set (match_operand:SI 0 "push_operand" "=<") (match_operand:SI 1 "general_no_elim_operand" "ri*m")) (clobber (mem:BLK (scratch)))] "!TARGET_64BIT" "push{l}\t%1" [(set_attr "type" "push") (set_attr "mode" "SI")]) (define_insn "*popsi1_epilogue" [(set (match_operand:SI 0 "nonimmediate_operand" "=r*m") (mem:SI (reg:SI 7))) (set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4))) (clobber (mem:BLK (scratch)))] "!TARGET_64BIT" "pop{l}\t%0" [(set_attr "type" "pop") (set_attr "mode" "SI")]) (define_insn "popsi1" [(set (match_operand:SI 0 "nonimmediate_operand" "=r*m") (mem:SI (reg:SI 7))) (set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4)))] "!TARGET_64BIT" "pop{l}\t%0" [(set_attr "type" "pop") (set_attr "mode" "SI")]) (define_insn "*movsi_xor" [(set (match_operand:SI 0 "register_operand" "=r") (match_operand:SI 1 "const0_operand" "i")) (clobber (reg:CC 17))] "reload_completed && (!TARGET_USE_MOV0 || optimize_size)" "xor{l}\t{%0, %0|%0, %0}" [(set_attr "type" "alu1") (set_attr "mode" "SI") (set_attr "length_immediate" "0")]) (define_insn "*movsi_or" [(set (match_operand:SI 0 "register_operand" "=r") (match_operand:SI 1 "immediate_operand" "i")) (clobber (reg:CC 17))] "reload_completed && operands[1] == constm1_rtx && (TARGET_PENTIUM || optimize_size)" { operands[1] = constm1_rtx; return "or{l}\t{%1, %0|%0, %1}"; } [(set_attr "type" "alu1") (set_attr "mode" "SI") (set_attr "length_immediate" "1")]) (define_insn "*movsi_1" [(set (match_operand:SI 0 "nonimmediate_operand" "=r,m,!*y,!rm,!*y,!*Y,!rm,!*Y") (match_operand:SI 1 "general_operand" "rinm,rin,*y,*y,rm,*Y,*Y,rm"))] "(TARGET_INTER_UNIT_MOVES || optimize_size) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" { switch (get_attr_type (insn)) { case TYPE_SSEMOV: if (get_attr_mode (insn) == MODE_TI) return "movdqa\t{%1, %0|%0, %1}"; return "movd\t{%1, %0|%0, %1}"; case TYPE_MMXMOV: if (get_attr_mode (insn) == MODE_DI) return "movq\t{%1, %0|%0, %1}"; return "movd\t{%1, %0|%0, %1}"; case TYPE_LEA: return "lea{l}\t{%1, %0|%0, %1}"; default: if (flag_pic && !LEGITIMATE_PIC_OPERAND_P (operands[1])) abort(); return "mov{l}\t{%1, %0|%0, %1}"; } } [(set (attr "type") (cond [(eq_attr "alternative" "2,3,4") (const_string "mmxmov") (eq_attr "alternative" "5,6,7") (const_string "ssemov") (and (ne (symbol_ref "flag_pic") (const_int 0)) (match_operand:SI 1 "symbolic_operand" "")) (const_string "lea") ] (const_string "imov"))) (set_attr "mode" "SI,SI,DI,SI,SI,TI,SI,SI")]) (define_insn "*movsi_1_nointernunit" [(set (match_operand:SI 0 "nonimmediate_operand" "=r,m,!*y,!m,!*y,!*Y,!m,!*Y") (match_operand:SI 1 "general_operand" "rinm,rin,*y,*y,m,*Y,*Y,m"))] "(!TARGET_INTER_UNIT_MOVES && !optimize_size) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" { switch (get_attr_type (insn)) { case TYPE_SSEMOV: if (get_attr_mode (insn) == MODE_TI) return "movdqa\t{%1, %0|%0, %1}"; return "movd\t{%1, %0|%0, %1}"; case TYPE_MMXMOV: if (get_attr_mode (insn) == MODE_DI) return "movq\t{%1, %0|%0, %1}"; return "movd\t{%1, %0|%0, %1}"; case TYPE_LEA: return "lea{l}\t{%1, %0|%0, %1}"; default: if (flag_pic && !LEGITIMATE_PIC_OPERAND_P (operands[1])) abort(); return "mov{l}\t{%1, %0|%0, %1}"; } } [(set (attr "type") (cond [(eq_attr "alternative" "2,3,4") (const_string "mmxmov") (eq_attr "alternative" "5,6,7") (const_string "ssemov") (and (ne (symbol_ref "flag_pic") (const_int 0)) (match_operand:SI 1 "symbolic_operand" "")) (const_string "lea") ] (const_string "imov"))) (set_attr "mode" "SI,SI,DI,SI,SI,TI,SI,SI")]) ;; Stores and loads of ax to arbitrary constant address. ;; We fake an second form of instruction to force reload to load address ;; into register when rax is not available (define_insn "*movabssi_1_rex64" [(set (mem:SI (match_operand:DI 0 "x86_64_movabs_operand" "i,r")) (match_operand:SI 1 "nonmemory_operand" "a,er"))] "TARGET_64BIT && ix86_check_movabs (insn, 0)" "@ movabs{l}\t{%1, %P0|%P0, %1} mov{l}\t{%1, %a0|%a0, %1}" [(set_attr "type" "imov") (set_attr "modrm" "0,*") (set_attr "length_address" "8,0") (set_attr "length_immediate" "0,*") (set_attr "memory" "store") (set_attr "mode" "SI")]) (define_insn "*movabssi_2_rex64" [(set (match_operand:SI 0 "register_operand" "=a,r") (mem:SI (match_operand:DI 1 "x86_64_movabs_operand" "i,r")))] "TARGET_64BIT && ix86_check_movabs (insn, 1)" "@ movabs{l}\t{%P1, %0|%0, %P1} mov{l}\t{%a1, %0|%0, %a1}" [(set_attr "type" "imov") (set_attr "modrm" "0,*") (set_attr "length_address" "8,0") (set_attr "length_immediate" "0") (set_attr "memory" "load") (set_attr "mode" "SI")]) (define_insn "*swapsi" [(set (match_operand:SI 0 "register_operand" "+r") (match_operand:SI 1 "register_operand" "+r")) (set (match_dup 1) (match_dup 0))] "" "xchg{l}\t%1, %0" [(set_attr "type" "imov") (set_attr "mode" "SI") (set_attr "pent_pair" "np") (set_attr "athlon_decode" "vector") (set_attr "ppro_uops" "few")]) (define_expand "movhi" [(set (match_operand:HI 0 "nonimmediate_operand" "") (match_operand:HI 1 "general_operand" ""))] "" "ix86_expand_move (HImode, operands); DONE;") (define_insn "*pushhi2" [(set (match_operand:HI 0 "push_operand" "=<,<") (match_operand:HI 1 "general_no_elim_operand" "n,r*m"))] "!TARGET_64BIT" "@ push{w}\t{|WORD PTR }%1 push{w}\t%1" [(set_attr "type" "push") (set_attr "mode" "HI")]) ;; For 64BIT abi we always round up to 8 bytes. (define_insn "*pushhi2_rex64" [(set (match_operand:HI 0 "push_operand" "=X") (match_operand:HI 1 "nonmemory_no_elim_operand" "ri"))] "TARGET_64BIT" "push{q}\t%q1" [(set_attr "type" "push") (set_attr "mode" "QI")]) (define_insn "*movhi_1" [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,r,m") (match_operand:HI 1 "general_operand" "r,rn,rm,rn"))] "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM" { switch (get_attr_type (insn)) { case TYPE_IMOVX: /* movzwl is faster than movw on p2 due to partial word stalls, though not as fast as an aligned movl. */ return "movz{wl|x}\t{%1, %k0|%k0, %1}"; default: if (get_attr_mode (insn) == MODE_SI) return "mov{l}\t{%k1, %k0|%k0, %k1}"; else return "mov{w}\t{%1, %0|%0, %1}"; } } [(set (attr "type") (cond [(and (eq_attr "alternative" "0") (ior (eq (symbol_ref "TARGET_PARTIAL_REG_STALL") (const_int 0)) (eq (symbol_ref "TARGET_HIMODE_MATH") (const_int 0)))) (const_string "imov") (and (eq_attr "alternative" "1,2") (match_operand:HI 1 "aligned_operand" "")) (const_string "imov") (and (ne (symbol_ref "TARGET_MOVX") (const_int 0)) (eq_attr "alternative" "0,2")) (const_string "imovx") ] (const_string "imov"))) (set (attr "mode") (cond [(eq_attr "type" "imovx") (const_string "SI") (and (eq_attr "alternative" "1,2") (match_operand:HI 1 "aligned_operand" "")) (const_string "SI") (and (eq_attr "alternative" "0") (ior (eq (symbol_ref "TARGET_PARTIAL_REG_STALL") (const_int 0)) (eq (symbol_ref "TARGET_HIMODE_MATH") (const_int 0)))) (const_string "SI") ] (const_string "HI")))]) ;; Stores and loads of ax to arbitrary constant address. ;; We fake an second form of instruction to force reload to load address ;; into register when rax is not available (define_insn "*movabshi_1_rex64" [(set (mem:HI (match_operand:DI 0 "x86_64_movabs_operand" "i,r")) (match_operand:HI 1 "nonmemory_operand" "a,er"))] "TARGET_64BIT && ix86_check_movabs (insn, 0)" "@ movabs{w}\t{%1, %P0|%P0, %1} mov{w}\t{%1, %a0|%a0, %1}" [(set_attr "type" "imov") (set_attr "modrm" "0,*") (set_attr "length_address" "8,0") (set_attr "length_immediate" "0,*") (set_attr "memory" "store") (set_attr "mode" "HI")]) (define_insn "*movabshi_2_rex64" [(set (match_operand:HI 0 "register_operand" "=a,r") (mem:HI (match_operand:DI 1 "x86_64_movabs_operand" "i,r")))] "TARGET_64BIT && ix86_check_movabs (insn, 1)" "@ movabs{w}\t{%P1, %0|%0, %P1} mov{w}\t{%a1, %0|%0, %a1}" [(set_attr "type" "imov") (set_attr "modrm" "0,*") (set_attr "length_address" "8,0") (set_attr "length_immediate" "0") (set_attr "memory" "load") (set_attr "mode" "HI")]) (define_insn "*swaphi_1" [(set (match_operand:HI 0 "register_operand" "+r") (match_operand:HI 1 "register_operand" "+r")) (set (match_dup 1) (match_dup 0))] "!TARGET_PARTIAL_REG_STALL || optimize_size" "xchg{l}\t%k1, %k0" [(set_attr "type" "imov") (set_attr "mode" "SI") (set_attr "pent_pair" "np") (set_attr "athlon_decode" "vector") (set_attr "ppro_uops" "few")]) (define_insn "*swaphi_2" [(set (match_operand:HI 0 "register_operand" "+r") (match_operand:HI 1 "register_operand" "+r")) (set (match_dup 1) (match_dup 0))] "TARGET_PARTIAL_REG_STALL" "xchg{w}\t%1, %0" [(set_attr "type" "imov") (set_attr "mode" "HI") (set_attr "pent_pair" "np") (set_attr "athlon_decode" "vector") (set_attr "ppro_uops" "few")]) (define_expand "movstricthi" [(set (strict_low_part (match_operand:HI 0 "nonimmediate_operand" "")) (match_operand:HI 1 "general_operand" ""))] "! TARGET_PARTIAL_REG_STALL || optimize_size" { /* Don't generate memory->memory moves, go through a register */ if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) operands[1] = force_reg (HImode, operands[1]); }) (define_insn "*movstricthi_1" [(set (strict_low_part (match_operand:HI 0 "nonimmediate_operand" "+rm,r")) (match_operand:HI 1 "general_operand" "rn,m"))] "(! TARGET_PARTIAL_REG_STALL || optimize_size) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "mov{w}\t{%1, %0|%0, %1}" [(set_attr "type" "imov") (set_attr "mode" "HI")]) (define_insn "*movstricthi_xor" [(set (strict_low_part (match_operand:HI 0 "register_operand" "+r")) (match_operand:HI 1 "const0_operand" "i")) (clobber (reg:CC 17))] "reload_completed && ((!TARGET_USE_MOV0 && !TARGET_PARTIAL_REG_STALL) || optimize_size)" "xor{w}\t{%0, %0|%0, %0}" [(set_attr "type" "alu1") (set_attr "mode" "HI") (set_attr "length_immediate" "0")]) (define_expand "movqi" [(set (match_operand:QI 0 "nonimmediate_operand" "") (match_operand:QI 1 "general_operand" ""))] "" "ix86_expand_move (QImode, operands); DONE;") ;; emit_push_insn when it calls move_by_pieces requires an insn to ;; "push a byte". But actually we use pushw, which has the effect ;; of rounding the amount pushed up to a halfword. (define_insn "*pushqi2" [(set (match_operand:QI 0 "push_operand" "=X,X") (match_operand:QI 1 "nonmemory_no_elim_operand" "n,r"))] "!TARGET_64BIT" "@ push{w}\t{|word ptr }%1 push{w}\t%w1" [(set_attr "type" "push") (set_attr "mode" "HI")]) ;; For 64BIT abi we always round up to 8 bytes. (define_insn "*pushqi2_rex64" [(set (match_operand:QI 0 "push_operand" "=X") (match_operand:QI 1 "nonmemory_no_elim_operand" "qi"))] "TARGET_64BIT" "push{q}\t%q1" [(set_attr "type" "push") (set_attr "mode" "QI")]) ;; Situation is quite tricky about when to choose full sized (SImode) move ;; over QImode moves. For Q_REG -> Q_REG move we use full size only for ;; partial register dependency machines (such as AMD Athlon), where QImode ;; moves issue extra dependency and for partial register stalls machines ;; that don't use QImode patterns (and QImode move cause stall on the next ;; instruction). ;; ;; For loads of Q_REG to NONQ_REG we use full sized moves except for partial ;; register stall machines with, where we use QImode instructions, since ;; partial register stall can be caused there. Then we use movzx. (define_insn "*movqi_1" [(set (match_operand:QI 0 "nonimmediate_operand" "=q,q ,q ,r,r ,?r,m") (match_operand:QI 1 "general_operand" " q,qn,qm,q,rn,qm,qn"))] "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM" { switch (get_attr_type (insn)) { case TYPE_IMOVX: if (!ANY_QI_REG_P (operands[1]) && GET_CODE (operands[1]) != MEM) abort (); return "movz{bl|x}\t{%1, %k0|%k0, %1}"; default: if (get_attr_mode (insn) == MODE_SI) return "mov{l}\t{%k1, %k0|%k0, %k1}"; else return "mov{b}\t{%1, %0|%0, %1}"; } } [(set (attr "type") (cond [(and (eq_attr "alternative" "3") (ior (eq (symbol_ref "TARGET_PARTIAL_REG_STALL") (const_int 0)) (eq (symbol_ref "TARGET_QIMODE_MATH") (const_int 0)))) (const_string "imov") (eq_attr "alternative" "3,5") (const_string "imovx") (and (ne (symbol_ref "TARGET_MOVX") (const_int 0)) (eq_attr "alternative" "2")) (const_string "imovx") ] (const_string "imov"))) (set (attr "mode") (cond [(eq_attr "alternative" "3,4,5") (const_string "SI") (eq_attr "alternative" "6") (const_string "QI") (eq_attr "type" "imovx") (const_string "SI") (and (eq_attr "type" "imov") (and (eq_attr "alternative" "0,1,2") (ne (symbol_ref "TARGET_PARTIAL_REG_DEPENDENCY") (const_int 0)))) (const_string "SI") ;; Avoid partial register stalls when not using QImode arithmetic (and (eq_attr "type" "imov") (and (eq_attr "alternative" "0,1,2") (and (ne (symbol_ref "TARGET_PARTIAL_REG_STALL") (const_int 0)) (eq (symbol_ref "TARGET_QIMODE_MATH") (const_int 0))))) (const_string "SI") ] (const_string "QI")))]) (define_expand "reload_outqi" [(parallel [(match_operand:QI 0 "" "=m") (match_operand:QI 1 "register_operand" "r") (match_operand:QI 2 "register_operand" "=&q")])] "" { rtx op0, op1, op2; op0 = operands[0]; op1 = operands[1]; op2 = operands[2]; if (reg_overlap_mentioned_p (op2, op0)) abort (); if (! q_regs_operand (op1, QImode)) { emit_insn (gen_movqi (op2, op1)); op1 = op2; } emit_insn (gen_movqi (op0, op1)); DONE; }) (define_insn "*swapqi_1" [(set (match_operand:QI 0 "register_operand" "+r") (match_operand:QI 1 "register_operand" "+r")) (set (match_dup 1) (match_dup 0))] "!TARGET_PARTIAL_REG_STALL || optimize_size" "xchg{l}\t%k1, %k0" [(set_attr "type" "imov") (set_attr "mode" "SI") (set_attr "pent_pair" "np") (set_attr "athlon_decode" "vector") (set_attr "ppro_uops" "few")]) (define_insn "*swapqi_2" [(set (match_operand:QI 0 "register_operand" "+q") (match_operand:QI 1 "register_operand" "+q")) (set (match_dup 1) (match_dup 0))] "TARGET_PARTIAL_REG_STALL" "xchg{b}\t%1, %0" [(set_attr "type" "imov") (set_attr "mode" "QI") (set_attr "pent_pair" "np") (set_attr "athlon_decode" "vector") (set_attr "ppro_uops" "few")]) (define_expand "movstrictqi" [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "")) (match_operand:QI 1 "general_operand" ""))] "! TARGET_PARTIAL_REG_STALL || optimize_size" { /* Don't generate memory->memory moves, go through a register. */ if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) operands[1] = force_reg (QImode, operands[1]); }) (define_insn "*movstrictqi_1" [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "+qm,q")) (match_operand:QI 1 "general_operand" "*qn,m"))] "(! TARGET_PARTIAL_REG_STALL || optimize_size) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "mov{b}\t{%1, %0|%0, %1}" [(set_attr "type" "imov") (set_attr "mode" "QI")]) (define_insn "*movstrictqi_xor" [(set (strict_low_part (match_operand:QI 0 "q_regs_operand" "+q")) (match_operand:QI 1 "const0_operand" "i")) (clobber (reg:CC 17))] "reload_completed && (!TARGET_USE_MOV0 || optimize_size)" "xor{b}\t{%0, %0|%0, %0}" [(set_attr "type" "alu1") (set_attr "mode" "QI") (set_attr "length_immediate" "0")]) (define_insn "*movsi_extv_1" [(set (match_operand:SI 0 "register_operand" "=R") (sign_extract:SI (match_operand 1 "ext_register_operand" "Q") (const_int 8) (const_int 8)))] "" "movs{bl|x}\t{%h1, %0|%0, %h1}" [(set_attr "type" "imovx") (set_attr "mode" "SI")]) (define_insn "*movhi_extv_1" [(set (match_operand:HI 0 "register_operand" "=R") (sign_extract:HI (match_operand 1 "ext_register_operand" "Q") (const_int 8) (const_int 8)))] "" "movs{bl|x}\t{%h1, %k0|%k0, %h1}" [(set_attr "type" "imovx") (set_attr "mode" "SI")]) (define_insn "*movqi_extv_1" [(set (match_operand:QI 0 "nonimmediate_operand" "=Qm,?r") (sign_extract:QI (match_operand 1 "ext_register_operand" "Q,Q") (const_int 8) (const_int 8)))] "!TARGET_64BIT" { switch (get_attr_type (insn)) { case TYPE_IMOVX: return "movs{bl|x}\t{%h1, %k0|%k0, %h1}"; default: return "mov{b}\t{%h1, %0|%0, %h1}"; } } [(set (attr "type") (if_then_else (and (match_operand:QI 0 "register_operand" "") (ior (not (match_operand:QI 0 "q_regs_operand" "")) (ne (symbol_ref "TARGET_MOVX") (const_int 0)))) (const_string "imovx") (const_string "imov"))) (set (attr "mode") (if_then_else (eq_attr "type" "imovx") (const_string "SI") (const_string "QI")))]) (define_insn "*movqi_extv_1_rex64" [(set (match_operand:QI 0 "register_operand" "=Q,?R") (sign_extract:QI (match_operand 1 "ext_register_operand" "Q,Q") (const_int 8) (const_int 8)))] "TARGET_64BIT" { switch (get_attr_type (insn)) { case TYPE_IMOVX: return "movs{bl|x}\t{%h1, %k0|%k0, %h1}"; default: return "mov{b}\t{%h1, %0|%0, %h1}"; } } [(set (attr "type") (if_then_else (and (match_operand:QI 0 "register_operand" "") (ior (not (match_operand:QI 0 "q_regs_operand" "")) (ne (symbol_ref "TARGET_MOVX") (const_int 0)))) (const_string "imovx") (const_string "imov"))) (set (attr "mode") (if_then_else (eq_attr "type" "imovx") (const_string "SI") (const_string "QI")))]) ;; Stores and loads of ax to arbitrary constant address. ;; We fake an second form of instruction to force reload to load address ;; into register when rax is not available (define_insn "*movabsqi_1_rex64" [(set (mem:QI (match_operand:DI 0 "x86_64_movabs_operand" "i,r")) (match_operand:QI 1 "nonmemory_operand" "a,er"))] "TARGET_64BIT && ix86_check_movabs (insn, 0)" "@ movabs{b}\t{%1, %P0|%P0, %1} mov{b}\t{%1, %a0|%a0, %1}" [(set_attr "type" "imov") (set_attr "modrm" "0,*") (set_attr "length_address" "8,0") (set_attr "length_immediate" "0,*") (set_attr "memory" "store") (set_attr "mode" "QI")]) (define_insn "*movabsqi_2_rex64" [(set (match_operand:QI 0 "register_operand" "=a,r") (mem:QI (match_operand:DI 1 "x86_64_movabs_operand" "i,r")))] "TARGET_64BIT && ix86_check_movabs (insn, 1)" "@ movabs{b}\t{%P1, %0|%0, %P1} mov{b}\t{%a1, %0|%0, %a1}" [(set_attr "type" "imov") (set_attr "modrm" "0,*") (set_attr "length_address" "8,0") (set_attr "length_immediate" "0") (set_attr "memory" "load") (set_attr "mode" "QI")]) (define_insn "*movsi_extzv_1" [(set (match_operand:SI 0 "register_operand" "=R") (zero_extract:SI (match_operand 1 "ext_register_operand" "Q") (const_int 8) (const_int 8)))] "" "movz{bl|x}\t{%h1, %0|%0, %h1}" [(set_attr "type" "imovx") (set_attr "mode" "SI")]) (define_insn "*movqi_extzv_2" [(set (match_operand:QI 0 "nonimmediate_operand" "=Qm,?R") (subreg:QI (zero_extract:SI (match_operand 1 "ext_register_operand" "Q,Q") (const_int 8) (const_int 8)) 0))] "!TARGET_64BIT" { switch (get_attr_type (insn)) { case TYPE_IMOVX: return "movz{bl|x}\t{%h1, %k0|%k0, %h1}"; default: return "mov{b}\t{%h1, %0|%0, %h1}"; } } [(set (attr "type") (if_then_else (and (match_operand:QI 0 "register_operand" "") (ior (not (match_operand:QI 0 "q_regs_operand" "")) (ne (symbol_ref "TARGET_MOVX") (const_int 0)))) (const_string "imovx") (const_string "imov"))) (set (attr "mode") (if_then_else (eq_attr "type" "imovx") (const_string "SI") (const_string "QI")))]) (define_insn "*movqi_extzv_2_rex64" [(set (match_operand:QI 0 "register_operand" "=Q,?R") (subreg:QI (zero_extract:SI (match_operand 1 "ext_register_operand" "Q,Q") (const_int 8) (const_int 8)) 0))] "TARGET_64BIT" { switch (get_attr_type (insn)) { case TYPE_IMOVX: return "movz{bl|x}\t{%h1, %k0|%k0, %h1}"; default: return "mov{b}\t{%h1, %0|%0, %h1}"; } } [(set (attr "type") (if_then_else (ior (not (match_operand:QI 0 "q_regs_operand" "")) (ne (symbol_ref "TARGET_MOVX") (const_int 0))) (const_string "imovx") (const_string "imov"))) (set (attr "mode") (if_then_else (eq_attr "type" "imovx") (const_string "SI") (const_string "QI")))]) (define_insn "movsi_insv_1" [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "+Q") (const_int 8) (const_int 8)) (match_operand:SI 1 "general_operand" "Qmn"))] "!TARGET_64BIT" "mov{b}\t{%b1, %h0|%h0, %b1}" [(set_attr "type" "imov") (set_attr "mode" "QI")]) (define_insn "movdi_insv_1_rex64" [(set (zero_extract:DI (match_operand 0 "ext_register_operand" "+Q") (const_int 8) (const_int 8)) (match_operand:DI 1 "nonmemory_operand" "Qn"))] "TARGET_64BIT" "mov{b}\t{%b1, %h0|%h0, %b1}" [(set_attr "type" "imov") (set_attr "mode" "QI")]) (define_insn "*movqi_insv_2" [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "+Q") (const_int 8) (const_int 8)) (lshiftrt:SI (match_operand:SI 1 "register_operand" "Q") (const_int 8)))] "" "mov{b}\t{%h1, %h0|%h0, %h1}" [(set_attr "type" "imov") (set_attr "mode" "QI")]) (define_expand "movdi" [(set (match_operand:DI 0 "nonimmediate_operand" "") (match_operand:DI 1 "general_operand" ""))] "" "ix86_expand_move (DImode, operands); DONE;") (define_insn "*pushdi" [(set (match_operand:DI 0 "push_operand" "=<") (match_operand:DI 1 "general_no_elim_operand" "riF*m"))] "!TARGET_64BIT" "#") (define_insn "pushdi2_rex64" [(set (match_operand:DI 0 "push_operand" "=<,!<") (match_operand:DI 1 "general_no_elim_operand" "re*m,n"))] "TARGET_64BIT" "@ push{q}\t%1 #" [(set_attr "type" "push,multi") (set_attr "mode" "DI")]) ;; Convert impossible pushes of immediate to existing instructions. ;; First try to get scratch register and go through it. In case this ;; fails, push sign extended lower part first and then overwrite ;; upper part by 32bit move. (define_peephole2 [(match_scratch:DI 2 "r") (set (match_operand:DI 0 "push_operand" "") (match_operand:DI 1 "immediate_operand" ""))] "TARGET_64BIT && !symbolic_operand (operands[1], DImode) && !x86_64_immediate_operand (operands[1], DImode)" [(set (match_dup 2) (match_dup 1)) (set (match_dup 0) (match_dup 2))] "") ;; We need to define this as both peepholer and splitter for case ;; peephole2 pass is not run. (define_peephole2 [(set (match_operand:DI 0 "push_operand" "") (match_operand:DI 1 "immediate_operand" ""))] "TARGET_64BIT && !symbolic_operand (operands[1], DImode) && !x86_64_immediate_operand (operands[1], DImode) && 1" [(set (match_dup 0) (match_dup 1)) (set (match_dup 2) (match_dup 3))] "split_di (operands + 1, 1, operands + 2, operands + 3); operands[1] = gen_lowpart (DImode, operands[2]); operands[2] = gen_rtx_MEM (SImode, gen_rtx_PLUS (DImode, stack_pointer_rtx, GEN_INT (4))); ") (define_split [(set (match_operand:DI 0 "push_operand" "") (match_operand:DI 1 "immediate_operand" ""))] - "TARGET_64BIT && (flow2_completed || (reload_completed && !flag_peephole2)) + "TARGET_64BIT && ((optimize > 0 && flag_peephole2) + ? flow2_completed : reload_completed) && !symbolic_operand (operands[1], DImode) && !x86_64_immediate_operand (operands[1], DImode)" [(set (match_dup 0) (match_dup 1)) (set (match_dup 2) (match_dup 3))] "split_di (operands + 1, 1, operands + 2, operands + 3); operands[1] = gen_lowpart (DImode, operands[2]); operands[2] = gen_rtx_MEM (SImode, gen_rtx_PLUS (DImode, stack_pointer_rtx, GEN_INT (4))); ") (define_insn "*pushdi2_prologue_rex64" [(set (match_operand:DI 0 "push_operand" "=<") (match_operand:DI 1 "general_no_elim_operand" "re*m")) (clobber (mem:BLK (scratch)))] "TARGET_64BIT" "push{q}\t%1" [(set_attr "type" "push") (set_attr "mode" "DI")]) (define_insn "*popdi1_epilogue_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=r*m") (mem:DI (reg:DI 7))) (set (reg:DI 7) (plus:DI (reg:DI 7) (const_int 8))) (clobber (mem:BLK (scratch)))] "TARGET_64BIT" "pop{q}\t%0" [(set_attr "type" "pop") (set_attr "mode" "DI")]) (define_insn "popdi1" [(set (match_operand:DI 0 "nonimmediate_operand" "=r*m") (mem:DI (reg:DI 7))) (set (reg:DI 7) (plus:DI (reg:DI 7) (const_int 8)))] "TARGET_64BIT" "pop{q}\t%0" [(set_attr "type" "pop") (set_attr "mode" "DI")]) (define_insn "*movdi_xor_rex64" [(set (match_operand:DI 0 "register_operand" "=r") (match_operand:DI 1 "const0_operand" "i")) (clobber (reg:CC 17))] "TARGET_64BIT && (!TARGET_USE_MOV0 || optimize_size) && reload_completed" "xor{l}\t{%k0, %k0|%k0, %k0}" [(set_attr "type" "alu1") (set_attr "mode" "SI") (set_attr "length_immediate" "0")]) (define_insn "*movdi_or_rex64" [(set (match_operand:DI 0 "register_operand" "=r") (match_operand:DI 1 "const_int_operand" "i")) (clobber (reg:CC 17))] "TARGET_64BIT && (TARGET_PENTIUM || optimize_size) && reload_completed && operands[1] == constm1_rtx" { operands[1] = constm1_rtx; return "or{q}\t{%1, %0|%0, %1}"; } [(set_attr "type" "alu1") (set_attr "mode" "DI") (set_attr "length_immediate" "1")]) (define_insn "*movdi_2" [(set (match_operand:DI 0 "nonimmediate_operand" "=r,o,!m*y,!*y,!m,!*Y,!*Y") (match_operand:DI 1 "general_operand" "riFo,riF,*y,m,*Y,*Y,m"))] "!TARGET_64BIT && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "@ # # movq\t{%1, %0|%0, %1} movq\t{%1, %0|%0, %1} movq\t{%1, %0|%0, %1} movdqa\t{%1, %0|%0, %1} movq\t{%1, %0|%0, %1}" [(set_attr "type" "*,*,mmx,mmx,ssemov,ssemov,ssemov") (set_attr "mode" "DI,DI,DI,DI,DI,TI,DI")]) (define_split [(set (match_operand:DI 0 "push_operand" "") (match_operand:DI 1 "general_operand" ""))] "!TARGET_64BIT && reload_completed && (! MMX_REG_P (operands[1]) && !SSE_REG_P (operands[1]))" [(const_int 0)] "ix86_split_long_move (operands); DONE;") ;; %%% This multiword shite has got to go. (define_split [(set (match_operand:DI 0 "nonimmediate_operand" "") (match_operand:DI 1 "general_operand" ""))] "!TARGET_64BIT && reload_completed && (!MMX_REG_P (operands[0]) && !SSE_REG_P (operands[0])) && (!MMX_REG_P (operands[1]) && !SSE_REG_P (operands[1]))" [(const_int 0)] "ix86_split_long_move (operands); DONE;") (define_insn "*movdi_1_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,r,mr,!mr,!*y,!rm,!*y,!*Y,!rm,!*Y") (match_operand:DI 1 "general_operand" "Z,rem,i,re,n,*y,*y,rm,*Y,*Y,rm"))] "TARGET_64BIT && (TARGET_INTER_UNIT_MOVES || optimize_size) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" { switch (get_attr_type (insn)) { case TYPE_SSEMOV: if (get_attr_mode (insn) == MODE_TI) return "movdqa\t{%1, %0|%0, %1}"; /* FALLTHRU */ case TYPE_MMXMOV: /* Moves from and into integer register is done using movd opcode with REX prefix. */ if (GENERAL_REG_P (operands[0]) || GENERAL_REG_P (operands[1])) return "movd\t{%1, %0|%0, %1}"; return "movq\t{%1, %0|%0, %1}"; case TYPE_MULTI: return "#"; case TYPE_LEA: return "lea{q}\t{%a1, %0|%0, %a1}"; default: if (flag_pic && !LEGITIMATE_PIC_OPERAND_P (operands[1])) abort (); if (get_attr_mode (insn) == MODE_SI) return "mov{l}\t{%k1, %k0|%k0, %k1}"; else if (which_alternative == 2) return "movabs{q}\t{%1, %0|%0, %1}"; else return "mov{q}\t{%1, %0|%0, %1}"; } } [(set (attr "type") (cond [(eq_attr "alternative" "5,6,7") (const_string "mmxmov") (eq_attr "alternative" "8,9,10") (const_string "ssemov") (eq_attr "alternative" "4") (const_string "multi") (and (ne (symbol_ref "flag_pic") (const_int 0)) (match_operand:DI 1 "symbolic_operand" "")) (const_string "lea") ] (const_string "imov"))) (set_attr "modrm" "*,0,0,*,*,*,*,*,*,*,*") (set_attr "length_immediate" "*,4,8,*,*,*,*,*,*,*,*") (set_attr "mode" "SI,DI,DI,DI,SI,DI,DI,DI,TI,DI,DI")]) (define_insn "*movdi_1_rex64_nointerunit" [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,r,mr,!mr,!*y,!m,!*y,!*Y,!m,!*Y") (match_operand:DI 1 "general_operand" "Z,rem,i,re,n,*y,*y,m,*Y,*Y,m"))] "TARGET_64BIT && (!TARGET_INTER_UNIT_MOVES && !optimize_size) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" { switch (get_attr_type (insn)) { case TYPE_SSEMOV: if (get_attr_mode (insn) == MODE_TI) return "movdqa\t{%1, %0|%0, %1}"; /* FALLTHRU */ case TYPE_MMXMOV: return "movq\t{%1, %0|%0, %1}"; case TYPE_MULTI: return "#"; case TYPE_LEA: return "lea{q}\t{%a1, %0|%0, %a1}"; default: if (flag_pic && !LEGITIMATE_PIC_OPERAND_P (operands[1])) abort (); if (get_attr_mode (insn) == MODE_SI) return "mov{l}\t{%k1, %k0|%k0, %k1}"; else if (which_alternative == 2) return "movabs{q}\t{%1, %0|%0, %1}"; else return "mov{q}\t{%1, %0|%0, %1}"; } } [(set (attr "type") (cond [(eq_attr "alternative" "5,6,7") (const_string "mmxmov") (eq_attr "alternative" "8,9,10") (const_string "ssemov") (eq_attr "alternative" "4") (const_string "multi") (and (ne (symbol_ref "flag_pic") (const_int 0)) (match_operand:DI 1 "symbolic_operand" "")) (const_string "lea") ] (const_string "imov"))) (set_attr "modrm" "*,0,0,*,*,*,*,*,*,*,*") (set_attr "length_immediate" "*,4,8,*,*,*,*,*,*,*,*") (set_attr "mode" "SI,DI,DI,DI,SI,DI,DI,DI,TI,DI,DI")]) ;; Stores and loads of ax to arbitrary constant address. ;; We fake an second form of instruction to force reload to load address ;; into register when rax is not available (define_insn "*movabsdi_1_rex64" [(set (mem:DI (match_operand:DI 0 "x86_64_movabs_operand" "i,r")) (match_operand:DI 1 "nonmemory_operand" "a,er"))] "TARGET_64BIT && ix86_check_movabs (insn, 0)" "@ movabs{q}\t{%1, %P0|%P0, %1} mov{q}\t{%1, %a0|%a0, %1}" [(set_attr "type" "imov") (set_attr "modrm" "0,*") (set_attr "length_address" "8,0") (set_attr "length_immediate" "0,*") (set_attr "memory" "store") (set_attr "mode" "DI")]) (define_insn "*movabsdi_2_rex64" [(set (match_operand:DI 0 "register_operand" "=a,r") (mem:DI (match_operand:DI 1 "x86_64_movabs_operand" "i,r")))] "TARGET_64BIT && ix86_check_movabs (insn, 1)" "@ movabs{q}\t{%P1, %0|%0, %P1} mov{q}\t{%a1, %0|%0, %a1}" [(set_attr "type" "imov") (set_attr "modrm" "0,*") (set_attr "length_address" "8,0") (set_attr "length_immediate" "0") (set_attr "memory" "load") (set_attr "mode" "DI")]) ;; Convert impossible stores of immediate to existing instructions. ;; First try to get scratch register and go through it. In case this ;; fails, move by 32bit parts. (define_peephole2 [(match_scratch:DI 2 "r") (set (match_operand:DI 0 "memory_operand" "") (match_operand:DI 1 "immediate_operand" ""))] "TARGET_64BIT && !symbolic_operand (operands[1], DImode) && !x86_64_immediate_operand (operands[1], DImode)" [(set (match_dup 2) (match_dup 1)) (set (match_dup 0) (match_dup 2))] "") ;; We need to define this as both peepholer and splitter for case ;; peephole2 pass is not run. (define_peephole2 [(set (match_operand:DI 0 "memory_operand" "") (match_operand:DI 1 "immediate_operand" ""))] "TARGET_64BIT && !symbolic_operand (operands[1], DImode) && !x86_64_immediate_operand (operands[1], DImode) && 1" [(set (match_dup 2) (match_dup 3)) (set (match_dup 4) (match_dup 5))] "split_di (operands, 2, operands + 2, operands + 4);") (define_split [(set (match_operand:DI 0 "memory_operand" "") (match_operand:DI 1 "immediate_operand" ""))] - "TARGET_64BIT && (flow2_completed || (reload_completed && !flag_peephole2)) + "TARGET_64BIT && ((optimize > 0 && flag_peephole2) + ? flow2_completed : reload_completed) && !symbolic_operand (operands[1], DImode) && !x86_64_immediate_operand (operands[1], DImode)" [(set (match_dup 2) (match_dup 3)) (set (match_dup 4) (match_dup 5))] "split_di (operands, 2, operands + 2, operands + 4);") (define_insn "*swapdi_rex64" [(set (match_operand:DI 0 "register_operand" "+r") (match_operand:DI 1 "register_operand" "+r")) (set (match_dup 1) (match_dup 0))] "TARGET_64BIT" "xchg{q}\t%1, %0" [(set_attr "type" "imov") (set_attr "mode" "DI") (set_attr "pent_pair" "np") (set_attr "athlon_decode" "vector") (set_attr "ppro_uops" "few")]) (define_expand "movsf" [(set (match_operand:SF 0 "nonimmediate_operand" "") (match_operand:SF 1 "general_operand" ""))] "" "ix86_expand_move (SFmode, operands); DONE;") (define_insn "*pushsf" [(set (match_operand:SF 0 "push_operand" "=<,<,<") (match_operand:SF 1 "general_no_elim_operand" "f#rx,rFm#fx,x#rf"))] "!TARGET_64BIT" { switch (which_alternative) { case 1: return "push{l}\t%1"; default: /* This insn should be already split before reg-stack. */ abort (); } } [(set_attr "type" "multi,push,multi") (set_attr "mode" "SF,SI,SF")]) (define_insn "*pushsf_rex64" [(set (match_operand:SF 0 "push_operand" "=X,X,X") (match_operand:SF 1 "nonmemory_no_elim_operand" "f#rx,rF#fx,x#rf"))] "TARGET_64BIT" { switch (which_alternative) { case 1: return "push{q}\t%q1"; default: /* This insn should be already split before reg-stack. */ abort (); } } [(set_attr "type" "multi,push,multi") (set_attr "mode" "SF,DI,SF")]) (define_split [(set (match_operand:SF 0 "push_operand" "") (match_operand:SF 1 "memory_operand" ""))] "reload_completed && GET_CODE (operands[1]) == MEM - && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF - && CONSTANT_POOL_ADDRESS_P (XEXP (operands[1], 0))" + && constant_pool_reference_p (operands[1])" [(set (match_dup 0) (match_dup 1))] - "operands[1] = get_pool_constant (XEXP (operands[1], 0));") + "operands[1] = avoid_constant_pool_reference (operands[1]);") ;; %%% Kill this when call knows how to work this out. (define_split [(set (match_operand:SF 0 "push_operand" "") (match_operand:SF 1 "any_fp_register_operand" ""))] "!TARGET_64BIT" [(set (reg:SI 7) (plus:SI (reg:SI 7) (const_int -4))) (set (mem:SF (reg:SI 7)) (match_dup 1))]) (define_split [(set (match_operand:SF 0 "push_operand" "") (match_operand:SF 1 "any_fp_register_operand" ""))] "TARGET_64BIT" [(set (reg:DI 7) (plus:DI (reg:DI 7) (const_int -8))) (set (mem:SF (reg:DI 7)) (match_dup 1))]) (define_insn "*movsf_1" [(set (match_operand:SF 0 "nonimmediate_operand" "=f#xr,m,f#xr,r#xf,m,x#rf,x#rf,x#rf,m,!*y,!rm,!*y") (match_operand:SF 1 "general_operand" "fm#rx,f#rx,G,rmF#fx,Fr#fx,C,x,xm#rf,x#rf,rm,*y,*y"))] "(TARGET_INTER_UNIT_MOVES || optimize_size) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) && (reload_in_progress || reload_completed || (ix86_cmodel == CM_MEDIUM || ix86_cmodel == CM_LARGE) || GET_CODE (operands[1]) != CONST_DOUBLE || memory_operand (operands[0], SFmode))" { switch (which_alternative) { case 0: if (REG_P (operands[1]) && find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp\t%y0"; else if (STACK_TOP_P (operands[0])) return "fld%z1\t%y1"; else return "fst\t%y0"; case 1: if (find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp%z0\t%y0"; else return "fst%z0\t%y0"; case 2: return standard_80387_constant_opcode (operands[1]); case 3: case 4: return "mov{l}\t{%1, %0|%0, %1}"; case 5: if (get_attr_mode (insn) == MODE_TI) return "pxor\t%0, %0"; else return "xorps\t%0, %0"; case 6: if (get_attr_mode (insn) == MODE_V4SF) return "movaps\t{%1, %0|%0, %1}"; else return "movss\t{%1, %0|%0, %1}"; case 7: case 8: return "movss\t{%1, %0|%0, %1}"; case 9: case 10: return "movd\t{%1, %0|%0, %1}"; case 11: return "movq\t{%1, %0|%0, %1}"; default: abort(); } } [(set_attr "type" "fmov,fmov,fmov,imov,imov,ssemov,ssemov,ssemov,ssemov,mmxmov,mmxmov,mmxmov") (set (attr "mode") (cond [(eq_attr "alternative" "3,4,9,10") (const_string "SI") (eq_attr "alternative" "5") (if_then_else (and (and (ne (symbol_ref "TARGET_SSE_LOAD0_BY_PXOR") (const_int 0)) (ne (symbol_ref "TARGET_SSE2") (const_int 0))) (eq (symbol_ref "optimize_size") (const_int 0))) (const_string "TI") (const_string "V4SF")) /* For architectures resolving dependencies on whole SSE registers use APS move to break dependency chains, otherwise use short move to avoid extra work. Do the same for architectures resolving dependencies on the parts. While in DF mode it is better to always handle just register parts, the SF mode is different due to lack of instructions to load just part of the register. It is better to maintain the whole registers in single format to avoid problems on using packed logical operations. */ (eq_attr "alternative" "6") (if_then_else (ior (ne (symbol_ref "TARGET_SSE_PARTIAL_REG_DEPENDENCY") (const_int 0)) (ne (symbol_ref "TARGET_SSE_PARTIAL_REGS") (const_int 0))) (const_string "V4SF") (const_string "SF")) (eq_attr "alternative" "11") (const_string "DI")] (const_string "SF")))]) (define_insn "*movsf_1_nointerunit" [(set (match_operand:SF 0 "nonimmediate_operand" "=f#xr,m,f#xr,r#xf,m,x#rf,x#rf,x#rf,m,!*y,!m,!*y") (match_operand:SF 1 "general_operand" "fm#rx,f#rx,G,rmF#fx,Fr#fx,C,x,xm#rf,x#rf,m,*y,*y"))] "(!TARGET_INTER_UNIT_MOVES && !optimize_size) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) && (reload_in_progress || reload_completed || (ix86_cmodel == CM_MEDIUM || ix86_cmodel == CM_LARGE) || GET_CODE (operands[1]) != CONST_DOUBLE || memory_operand (operands[0], SFmode))" { switch (which_alternative) { case 0: if (REG_P (operands[1]) && find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) { if (REGNO (operands[0]) == FIRST_STACK_REG && TARGET_USE_FFREEP) return "ffreep\t%y0"; return "fstp\t%y0"; } else if (STACK_TOP_P (operands[0])) return "fld%z1\t%y1"; else return "fst\t%y0"; case 1: if (find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp%z0\t%y0"; else return "fst%z0\t%y0"; case 2: return standard_80387_constant_opcode (operands[1]); case 3: case 4: return "mov{l}\t{%1, %0|%0, %1}"; case 5: if (get_attr_mode (insn) == MODE_TI) return "pxor\t%0, %0"; else return "xorps\t%0, %0"; case 6: if (get_attr_mode (insn) == MODE_V4SF) return "movaps\t{%1, %0|%0, %1}"; else return "movss\t{%1, %0|%0, %1}"; case 7: case 8: return "movss\t{%1, %0|%0, %1}"; case 9: case 10: return "movd\t{%1, %0|%0, %1}"; case 11: return "movq\t{%1, %0|%0, %1}"; default: abort(); } } [(set_attr "type" "fmov,fmov,fmov,imov,imov,ssemov,ssemov,ssemov,ssemov,mmxmov,mmxmov,mmxmov") (set (attr "mode") (cond [(eq_attr "alternative" "3,4,9,10") (const_string "SI") (eq_attr "alternative" "5") (if_then_else (and (and (ne (symbol_ref "TARGET_SSE_LOAD0_BY_PXOR") (const_int 0)) (ne (symbol_ref "TARGET_SSE2") (const_int 0))) (eq (symbol_ref "optimize_size") (const_int 0))) (const_string "TI") (const_string "V4SF")) /* For architectures resolving dependencies on whole SSE registers use APS move to break dependency chains, otherwise use short move to avoid extra work. Do the same for architectures resolving dependencies on the parts. While in DF mode it is better to always handle just register parts, the SF mode is different due to lack of instructions to load just part of the register. It is better to maintain the whole registers in single format to avoid problems on using packed logical operations. */ (eq_attr "alternative" "6") (if_then_else (ior (ne (symbol_ref "TARGET_SSE_PARTIAL_REG_DEPENDENCY") (const_int 0)) (ne (symbol_ref "TARGET_SSE_PARTIAL_REGS") (const_int 0))) (const_string "V4SF") (const_string "SF")) (eq_attr "alternative" "11") (const_string "DI")] (const_string "SF")))]) (define_insn "*swapsf" [(set (match_operand:SF 0 "register_operand" "+f") (match_operand:SF 1 "register_operand" "+f")) (set (match_dup 1) (match_dup 0))] "reload_completed || !TARGET_SSE" { if (STACK_TOP_P (operands[0])) return "fxch\t%1"; else return "fxch\t%0"; } [(set_attr "type" "fxch") (set_attr "mode" "SF")]) (define_expand "movdf" [(set (match_operand:DF 0 "nonimmediate_operand" "") (match_operand:DF 1 "general_operand" ""))] "" "ix86_expand_move (DFmode, operands); DONE;") ;; Size of pushdf is 3 (for sub) + 2 (for fstp) + memory operand size. ;; Size of pushdf using integer instructions is 2+2*memory operand size ;; On the average, pushdf using integers can be still shorter. Allow this ;; pattern for optimize_size too. (define_insn "*pushdf_nointeger" [(set (match_operand:DF 0 "push_operand" "=<,<,<,<") (match_operand:DF 1 "general_no_elim_operand" "f#Y,Fo#fY,*r#fY,Y#f"))] "!TARGET_64BIT && !TARGET_INTEGER_DFMODE_MOVES" { /* This insn should be already split before reg-stack. */ abort (); } [(set_attr "type" "multi") (set_attr "mode" "DF,SI,SI,DF")]) (define_insn "*pushdf_integer" [(set (match_operand:DF 0 "push_operand" "=<,<,<") (match_operand:DF 1 "general_no_elim_operand" "f#rY,rFo#fY,Y#rf"))] "TARGET_64BIT || TARGET_INTEGER_DFMODE_MOVES" { /* This insn should be already split before reg-stack. */ abort (); } [(set_attr "type" "multi") (set_attr "mode" "DF,SI,DF")]) ;; %%% Kill this when call knows how to work this out. (define_split [(set (match_operand:DF 0 "push_operand" "") (match_operand:DF 1 "any_fp_register_operand" ""))] "!TARGET_64BIT && reload_completed" [(set (reg:SI 7) (plus:SI (reg:SI 7) (const_int -8))) (set (mem:DF (reg:SI 7)) (match_dup 1))] "") (define_split [(set (match_operand:DF 0 "push_operand" "") (match_operand:DF 1 "any_fp_register_operand" ""))] "TARGET_64BIT && reload_completed" [(set (reg:DI 7) (plus:DI (reg:DI 7) (const_int -8))) (set (mem:DF (reg:DI 7)) (match_dup 1))] "") (define_split [(set (match_operand:DF 0 "push_operand" "") (match_operand:DF 1 "general_operand" ""))] "reload_completed" [(const_int 0)] "ix86_split_long_move (operands); DONE;") ;; Moving is usually shorter when only FP registers are used. This separate ;; movdf pattern avoids the use of integer registers for FP operations ;; when optimizing for size. (define_insn "*movdf_nointeger" [(set (match_operand:DF 0 "nonimmediate_operand" "=f#Y,m,f#Y,*r,o,Y#f,Y#f,Y#f,m") (match_operand:DF 1 "general_operand" "fm#Y,f#Y,G,*roF,F*r,C,Y#f,YHm#f,Y#f"))] "(GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) && ((optimize_size || !TARGET_INTEGER_DFMODE_MOVES) && !TARGET_64BIT) && (reload_in_progress || reload_completed || (ix86_cmodel == CM_MEDIUM || ix86_cmodel == CM_LARGE) || GET_CODE (operands[1]) != CONST_DOUBLE || memory_operand (operands[0], DFmode))" { switch (which_alternative) { case 0: if (REG_P (operands[1]) && find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) { if (REGNO (operands[0]) == FIRST_STACK_REG && TARGET_USE_FFREEP) return "ffreep\t%y0"; return "fstp\t%y0"; } else if (STACK_TOP_P (operands[0])) return "fld%z1\t%y1"; else return "fst\t%y0"; case 1: if (find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp%z0\t%y0"; else return "fst%z0\t%y0"; case 2: return standard_80387_constant_opcode (operands[1]); case 3: case 4: return "#"; case 5: switch (get_attr_mode (insn)) { case MODE_V4SF: return "xorps\t%0, %0"; case MODE_V2DF: return "xorpd\t%0, %0"; case MODE_TI: return "pxor\t%0, %0"; default: abort (); } case 6: switch (get_attr_mode (insn)) { case MODE_V4SF: return "movaps\t{%1, %0|%0, %1}"; case MODE_V2DF: return "movapd\t{%1, %0|%0, %1}"; case MODE_DF: return "movsd\t{%1, %0|%0, %1}"; default: abort (); } case 7: if (get_attr_mode (insn) == MODE_V2DF) return "movlpd\t{%1, %0|%0, %1}"; else return "movsd\t{%1, %0|%0, %1}"; case 8: return "movsd\t{%1, %0|%0, %1}"; default: abort(); } } [(set_attr "type" "fmov,fmov,fmov,multi,multi,ssemov,ssemov,ssemov,ssemov") (set (attr "mode") (cond [(eq_attr "alternative" "3,4") (const_string "SI") /* xorps is one byte shorter. */ (eq_attr "alternative" "5") (cond [(ne (symbol_ref "optimize_size") (const_int 0)) (const_string "V4SF") (ne (symbol_ref "TARGET_SSE_LOAD0_BY_PXOR") (const_int 0)) (const_string "TI")] (const_string "V2DF")) /* For architectures resolving dependencies on whole SSE registers use APD move to break dependency chains, otherwise use short move to avoid extra work. movaps encodes one byte shorter. */ (eq_attr "alternative" "6") (cond [(ne (symbol_ref "optimize_size") (const_int 0)) (const_string "V4SF") (ne (symbol_ref "TARGET_SSE_PARTIAL_REG_DEPENDENCY") (const_int 0)) (const_string "V2DF")] (const_string "DF")) /* For architectures resolving dependencies on register parts we may avoid extra work to zero out upper part of register. */ (eq_attr "alternative" "7") (if_then_else (ne (symbol_ref "TARGET_SSE_PARTIAL_REGS") (const_int 0)) (const_string "V2DF") (const_string "DF"))] (const_string "DF")))]) (define_insn "*movdf_integer" [(set (match_operand:DF 0 "nonimmediate_operand" "=f#Yr,m,f#Yr,r#Yf,o,Y#rf,Y#rf,Y#rf,m") (match_operand:DF 1 "general_operand" "fm#Yr,f#Yr,G,roF#Yf,Fr#Yf,C,Y#rf,Ym#rf,Y#rf"))] "(GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) && ((!optimize_size && TARGET_INTEGER_DFMODE_MOVES) || TARGET_64BIT) && (reload_in_progress || reload_completed || (ix86_cmodel == CM_MEDIUM || ix86_cmodel == CM_LARGE) || GET_CODE (operands[1]) != CONST_DOUBLE || memory_operand (operands[0], DFmode))" { switch (which_alternative) { case 0: if (REG_P (operands[1]) && find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) { if (REGNO (operands[0]) == FIRST_STACK_REG && TARGET_USE_FFREEP) return "ffreep\t%y0"; return "fstp\t%y0"; } else if (STACK_TOP_P (operands[0])) return "fld%z1\t%y1"; else return "fst\t%y0"; case 1: if (find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp%z0\t%y0"; else return "fst%z0\t%y0"; case 2: return standard_80387_constant_opcode (operands[1]); case 3: case 4: return "#"; case 5: switch (get_attr_mode (insn)) { case MODE_V4SF: return "xorps\t%0, %0"; case MODE_V2DF: return "xorpd\t%0, %0"; case MODE_TI: return "pxor\t%0, %0"; default: abort (); } case 6: switch (get_attr_mode (insn)) { case MODE_V4SF: return "movaps\t{%1, %0|%0, %1}"; case MODE_V2DF: return "movapd\t{%1, %0|%0, %1}"; case MODE_DF: return "movsd\t{%1, %0|%0, %1}"; default: abort (); } case 7: if (get_attr_mode (insn) == MODE_V2DF) return "movlpd\t{%1, %0|%0, %1}"; else return "movsd\t{%1, %0|%0, %1}"; case 8: return "movsd\t{%1, %0|%0, %1}"; default: abort(); } } [(set_attr "type" "fmov,fmov,fmov,multi,multi,ssemov,ssemov,ssemov,ssemov") (set (attr "mode") (cond [(eq_attr "alternative" "3,4") (const_string "SI") /* xorps is one byte shorter. */ (eq_attr "alternative" "5") (cond [(ne (symbol_ref "optimize_size") (const_int 0)) (const_string "V4SF") (ne (symbol_ref "TARGET_SSE_LOAD0_BY_PXOR") (const_int 0)) (const_string "TI")] (const_string "V2DF")) /* For architectures resolving dependencies on whole SSE registers use APD move to break dependency chains, otherwise use short move to avoid extra work. movaps encodes one byte shorter. */ (eq_attr "alternative" "6") (cond [(ne (symbol_ref "optimize_size") (const_int 0)) (const_string "V4SF") (ne (symbol_ref "TARGET_SSE_PARTIAL_REG_DEPENDENCY") (const_int 0)) (const_string "V2DF")] (const_string "DF")) /* For architectures resolving dependencies on register parts we may avoid extra work to zero out upper part of register. */ (eq_attr "alternative" "7") (if_then_else (ne (symbol_ref "TARGET_SSE_PARTIAL_REGS") (const_int 0)) (const_string "V2DF") (const_string "DF"))] (const_string "DF")))]) (define_split [(set (match_operand:DF 0 "nonimmediate_operand" "") (match_operand:DF 1 "general_operand" ""))] "reload_completed && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) && ! (ANY_FP_REG_P (operands[0]) || (GET_CODE (operands[0]) == SUBREG && ANY_FP_REG_P (SUBREG_REG (operands[0])))) && ! (ANY_FP_REG_P (operands[1]) || (GET_CODE (operands[1]) == SUBREG && ANY_FP_REG_P (SUBREG_REG (operands[1]))))" [(const_int 0)] "ix86_split_long_move (operands); DONE;") (define_insn "*swapdf" [(set (match_operand:DF 0 "register_operand" "+f") (match_operand:DF 1 "register_operand" "+f")) (set (match_dup 1) (match_dup 0))] "reload_completed || !TARGET_SSE2" { if (STACK_TOP_P (operands[0])) return "fxch\t%1"; else return "fxch\t%0"; } [(set_attr "type" "fxch") (set_attr "mode" "DF")]) (define_expand "movxf" [(set (match_operand:XF 0 "nonimmediate_operand" "") (match_operand:XF 1 "general_operand" ""))] "" "ix86_expand_move (XFmode, operands); DONE;") ;; Size of pushdf is 3 (for sub) + 2 (for fstp) + memory operand size. ;; Size of pushdf using integer instructions is 3+3*memory operand size ;; Pushing using integer instructions is longer except for constants ;; and direct memory references. ;; (assuming that any given constant is pushed only once, but this ought to be ;; handled elsewhere). (define_insn "*pushxf_nointeger" [(set (match_operand:XF 0 "push_operand" "=X,X,X") (match_operand:XF 1 "general_no_elim_operand" "f,Fo,*r"))] "optimize_size" { /* This insn should be already split before reg-stack. */ abort (); } [(set_attr "type" "multi") (set_attr "mode" "XF,SI,SI")]) (define_insn "*pushxf_integer" [(set (match_operand:XF 0 "push_operand" "=<,<") (match_operand:XF 1 "general_no_elim_operand" "f#r,ro#f"))] "!optimize_size" { /* This insn should be already split before reg-stack. */ abort (); } [(set_attr "type" "multi") (set_attr "mode" "XF,SI")]) (define_split [(set (match_operand 0 "push_operand" "") (match_operand 1 "general_operand" ""))] "reload_completed && (GET_MODE (operands[0]) == XFmode || GET_MODE (operands[0]) == DFmode) && !ANY_FP_REG_P (operands[1])" [(const_int 0)] "ix86_split_long_move (operands); DONE;") (define_split [(set (match_operand:XF 0 "push_operand" "") (match_operand:XF 1 "any_fp_register_operand" ""))] "!TARGET_64BIT" [(set (reg:SI 7) (plus:SI (reg:SI 7) (match_dup 2))) (set (mem:XF (reg:SI 7)) (match_dup 1))] "operands[2] = GEN_INT (TARGET_128BIT_LONG_DOUBLE ? -16 : -12);") (define_split [(set (match_operand:XF 0 "push_operand" "") (match_operand:XF 1 "any_fp_register_operand" ""))] "TARGET_64BIT" [(set (reg:DI 7) (plus:DI (reg:DI 7) (match_dup 2))) (set (mem:XF (reg:DI 7)) (match_dup 1))] "operands[2] = GEN_INT (TARGET_128BIT_LONG_DOUBLE ? -16 : -12);") ;; Do not use integer registers when optimizing for size (define_insn "*movxf_nointeger" [(set (match_operand:XF 0 "nonimmediate_operand" "=f,m,f,*r,o") (match_operand:XF 1 "general_operand" "fm,f,G,*roF,F*r"))] "optimize_size && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) && (reload_in_progress || reload_completed || GET_CODE (operands[1]) != CONST_DOUBLE || memory_operand (operands[0], XFmode))" { switch (which_alternative) { case 0: if (REG_P (operands[1]) && find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) { if (REGNO (operands[0]) == FIRST_STACK_REG && TARGET_USE_FFREEP) return "ffreep\t%y0"; return "fstp\t%y0"; } else if (STACK_TOP_P (operands[0])) return "fld%z1\t%y1"; else return "fst\t%y0"; case 1: /* There is no non-popping store to memory for XFmode. So if we need one, follow the store with a load. */ if (! find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp%z0\t%y0\;fld%z0\t%y0"; else return "fstp%z0\t%y0"; case 2: return standard_80387_constant_opcode (operands[1]); case 3: case 4: return "#"; } abort(); } [(set_attr "type" "fmov,fmov,fmov,multi,multi") (set_attr "mode" "XF,XF,XF,SI,SI")]) (define_insn "*movxf_integer" [(set (match_operand:XF 0 "nonimmediate_operand" "=f#r,m,f#r,r#f,o") (match_operand:XF 1 "general_operand" "fm#r,f#r,G,roF#f,Fr#f"))] "!optimize_size && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) && (reload_in_progress || reload_completed || GET_CODE (operands[1]) != CONST_DOUBLE || memory_operand (operands[0], XFmode))" { switch (which_alternative) { case 0: if (REG_P (operands[1]) && find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) { if (REGNO (operands[0]) == FIRST_STACK_REG && TARGET_USE_FFREEP) return "ffreep\t%y0"; return "fstp\t%y0"; } else if (STACK_TOP_P (operands[0])) return "fld%z1\t%y1"; else return "fst\t%y0"; case 1: /* There is no non-popping store to memory for XFmode. So if we need one, follow the store with a load. */ if (! find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp%z0\t%y0\;fld%z0\t%y0"; else return "fstp%z0\t%y0"; case 2: return standard_80387_constant_opcode (operands[1]); case 3: case 4: return "#"; } abort(); } [(set_attr "type" "fmov,fmov,fmov,multi,multi") (set_attr "mode" "XF,XF,XF,SI,SI")]) (define_split [(set (match_operand 0 "nonimmediate_operand" "") (match_operand 1 "general_operand" ""))] "reload_completed && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) && GET_MODE (operands[0]) == XFmode && ! (ANY_FP_REG_P (operands[0]) || (GET_CODE (operands[0]) == SUBREG && ANY_FP_REG_P (SUBREG_REG (operands[0])))) && ! (ANY_FP_REG_P (operands[1]) || (GET_CODE (operands[1]) == SUBREG && ANY_FP_REG_P (SUBREG_REG (operands[1]))))" [(const_int 0)] "ix86_split_long_move (operands); DONE;") (define_split [(set (match_operand 0 "register_operand" "") (match_operand 1 "memory_operand" ""))] "reload_completed && GET_CODE (operands[1]) == MEM && (GET_MODE (operands[0]) == XFmode || GET_MODE (operands[0]) == SFmode || GET_MODE (operands[0]) == DFmode) - && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF - && CONSTANT_POOL_ADDRESS_P (XEXP (operands[1], 0))" + && constant_pool_reference_p (operands[1])" [(set (match_dup 0) (match_dup 1))] { - rtx c = get_pool_constant (XEXP (operands[1], 0)); + rtx c = avoid_constant_pool_reference (operands[1]); rtx r = operands[0]; if (GET_CODE (r) == SUBREG) r = SUBREG_REG (r); if (SSE_REG_P (r)) { if (!standard_sse_constant_p (c)) FAIL; } else if (FP_REG_P (r)) { if (!standard_80387_constant_p (c)) FAIL; } else if (MMX_REG_P (r)) FAIL; operands[1] = c; }) (define_insn "swapxf" [(set (match_operand:XF 0 "register_operand" "+f") (match_operand:XF 1 "register_operand" "+f")) (set (match_dup 1) (match_dup 0))] "" { if (STACK_TOP_P (operands[0])) return "fxch\t%1"; else return "fxch\t%0"; } [(set_attr "type" "fxch") (set_attr "mode" "XF")]) ;; Zero extension instructions (define_expand "zero_extendhisi2" [(set (match_operand:SI 0 "register_operand" "") (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "")))] "" { if (TARGET_ZERO_EXTEND_WITH_AND && !optimize_size) { operands[1] = force_reg (HImode, operands[1]); emit_insn (gen_zero_extendhisi2_and (operands[0], operands[1])); DONE; } }) (define_insn "zero_extendhisi2_and" [(set (match_operand:SI 0 "register_operand" "=r") (zero_extend:SI (match_operand:HI 1 "register_operand" "0"))) (clobber (reg:CC 17))] "TARGET_ZERO_EXTEND_WITH_AND && !optimize_size" "#" [(set_attr "type" "alu1") (set_attr "mode" "SI")]) (define_split [(set (match_operand:SI 0 "register_operand" "") (zero_extend:SI (match_operand:HI 1 "register_operand" ""))) (clobber (reg:CC 17))] "reload_completed && TARGET_ZERO_EXTEND_WITH_AND && !optimize_size" [(parallel [(set (match_dup 0) (and:SI (match_dup 0) (const_int 65535))) (clobber (reg:CC 17))])] "") (define_insn "*zero_extendhisi2_movzwl" [(set (match_operand:SI 0 "register_operand" "=r") (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "rm")))] "!TARGET_ZERO_EXTEND_WITH_AND || optimize_size" "movz{wl|x}\t{%1, %0|%0, %1}" [(set_attr "type" "imovx") (set_attr "mode" "SI")]) (define_expand "zero_extendqihi2" [(parallel [(set (match_operand:HI 0 "register_operand" "") (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" ""))) (clobber (reg:CC 17))])] "" "") (define_insn "*zero_extendqihi2_and" [(set (match_operand:HI 0 "register_operand" "=r,?&q") (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "0,qm"))) (clobber (reg:CC 17))] "TARGET_ZERO_EXTEND_WITH_AND && !optimize_size" "#" [(set_attr "type" "alu1") (set_attr "mode" "HI")]) (define_insn "*zero_extendqihi2_movzbw_and" [(set (match_operand:HI 0 "register_operand" "=r,r") (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "qm,0"))) (clobber (reg:CC 17))] "!TARGET_ZERO_EXTEND_WITH_AND || optimize_size" "#" [(set_attr "type" "imovx,alu1") (set_attr "mode" "HI")]) (define_insn "*zero_extendqihi2_movzbw" [(set (match_operand:HI 0 "register_operand" "=r") (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "qm")))] "(!TARGET_ZERO_EXTEND_WITH_AND || optimize_size) && reload_completed" "movz{bw|x}\t{%1, %0|%0, %1}" [(set_attr "type" "imovx") (set_attr "mode" "HI")]) ;; For the movzbw case strip only the clobber (define_split [(set (match_operand:HI 0 "register_operand" "") (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" ""))) (clobber (reg:CC 17))] "reload_completed && (!TARGET_ZERO_EXTEND_WITH_AND || optimize_size) && (!REG_P (operands[1]) || ANY_QI_REG_P (operands[1]))" [(set (match_operand:HI 0 "register_operand" "") (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "")))]) ;; When source and destination does not overlap, clear destination ;; first and then do the movb (define_split [(set (match_operand:HI 0 "register_operand" "") (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" ""))) (clobber (reg:CC 17))] "reload_completed && ANY_QI_REG_P (operands[0]) && (TARGET_ZERO_EXTEND_WITH_AND && !optimize_size) && !reg_overlap_mentioned_p (operands[0], operands[1])" [(set (match_dup 0) (const_int 0)) (set (strict_low_part (match_dup 2)) (match_dup 1))] "operands[2] = gen_lowpart (QImode, operands[0]);") ;; Rest is handled by single and. (define_split [(set (match_operand:HI 0 "register_operand" "") (zero_extend:HI (match_operand:QI 1 "register_operand" ""))) (clobber (reg:CC 17))] "reload_completed && true_regnum (operands[0]) == true_regnum (operands[1])" [(parallel [(set (match_dup 0) (and:HI (match_dup 0) (const_int 255))) (clobber (reg:CC 17))])] "") (define_expand "zero_extendqisi2" [(parallel [(set (match_operand:SI 0 "register_operand" "") (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" ""))) (clobber (reg:CC 17))])] "" "") (define_insn "*zero_extendqisi2_and" [(set (match_operand:SI 0 "register_operand" "=r,?&q") (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "0,qm"))) (clobber (reg:CC 17))] "TARGET_ZERO_EXTEND_WITH_AND && !optimize_size" "#" [(set_attr "type" "alu1") (set_attr "mode" "SI")]) (define_insn "*zero_extendqisi2_movzbw_and" [(set (match_operand:SI 0 "register_operand" "=r,r") (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "qm,0"))) (clobber (reg:CC 17))] "!TARGET_ZERO_EXTEND_WITH_AND || optimize_size" "#" [(set_attr "type" "imovx,alu1") (set_attr "mode" "SI")]) (define_insn "*zero_extendqisi2_movzbw" [(set (match_operand:SI 0 "register_operand" "=r") (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "qm")))] "(!TARGET_ZERO_EXTEND_WITH_AND || optimize_size) && reload_completed" "movz{bl|x}\t{%1, %0|%0, %1}" [(set_attr "type" "imovx") (set_attr "mode" "SI")]) ;; For the movzbl case strip only the clobber (define_split [(set (match_operand:SI 0 "register_operand" "") (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" ""))) (clobber (reg:CC 17))] "reload_completed && (!TARGET_ZERO_EXTEND_WITH_AND || optimize_size) && (!REG_P (operands[1]) || ANY_QI_REG_P (operands[1]))" [(set (match_dup 0) (zero_extend:SI (match_dup 1)))]) ;; When source and destination does not overlap, clear destination ;; first and then do the movb (define_split [(set (match_operand:SI 0 "register_operand" "") (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" ""))) (clobber (reg:CC 17))] "reload_completed && ANY_QI_REG_P (operands[0]) && (ANY_QI_REG_P (operands[1]) || GET_CODE (operands[1]) == MEM) && (TARGET_ZERO_EXTEND_WITH_AND && !optimize_size) && !reg_overlap_mentioned_p (operands[0], operands[1])" [(set (match_dup 0) (const_int 0)) (set (strict_low_part (match_dup 2)) (match_dup 1))] "operands[2] = gen_lowpart (QImode, operands[0]);") ;; Rest is handled by single and. (define_split [(set (match_operand:SI 0 "register_operand" "") (zero_extend:SI (match_operand:QI 1 "register_operand" ""))) (clobber (reg:CC 17))] "reload_completed && true_regnum (operands[0]) == true_regnum (operands[1])" [(parallel [(set (match_dup 0) (and:SI (match_dup 0) (const_int 255))) (clobber (reg:CC 17))])] "") ;; %%% Kill me once multi-word ops are sane. (define_expand "zero_extendsidi2" [(set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "rm")))] "" "if (!TARGET_64BIT) { emit_insn (gen_zero_extendsidi2_32 (operands[0], operands[1])); DONE; } ") (define_insn "zero_extendsidi2_32" [(set (match_operand:DI 0 "nonimmediate_operand" "=r,?r,?*o,!?y,!?Y") (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "0,rm,r,m,m"))) (clobber (reg:CC 17))] "!TARGET_64BIT && !TARGET_INTER_UNIT_MOVES" "@ # # # movd\t{%1, %0|%0, %1} movd\t{%1, %0|%0, %1}" [(set_attr "mode" "SI,SI,SI,DI,TI") (set_attr "type" "multi,multi,multi,mmxmov,ssemov")]) (define_insn "*zero_extendsidi2_32_1" [(set (match_operand:DI 0 "nonimmediate_operand" "=r,?r,?*o,!?y,!?Y") (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "0,rm,r,rm,rm"))) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_INTER_UNIT_MOVES" "@ # # # movd\t{%1, %0|%0, %1} movd\t{%1, %0|%0, %1}" [(set_attr "mode" "SI,SI,SI,DI,TI") (set_attr "type" "multi,multi,multi,mmxmov,ssemov")]) (define_insn "zero_extendsidi2_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=r,o,!?y,!?Y") (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "rm,0,m,m")))] "TARGET_64BIT && !TARGET_INTER_UNIT_MOVES" "@ mov\t{%k1, %k0|%k0, %k1} # movd\t{%1, %0|%0, %1} movd\t{%1, %0|%0, %1}" [(set_attr "type" "imovx,imov,mmxmov,ssemov") (set_attr "mode" "SI,DI,DI,TI")]) (define_insn "*zero_extendsidi2_rex64_1" [(set (match_operand:DI 0 "nonimmediate_operand" "=r,o,!?y,!*?") (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "rm,0,rm,rm")))] "TARGET_64BIT && TARGET_INTER_UNIT_MOVES" "@ mov\t{%k1, %k0|%k0, %k1} # movd\t{%1, %0|%0, %1} movd\t{%1, %0|%0, %1}" [(set_attr "type" "imovx,imov,mmxmov,ssemov") (set_attr "mode" "SI,DI,SI,SI")]) (define_split [(set (match_operand:DI 0 "memory_operand" "") (zero_extend:DI (match_dup 0)))] "TARGET_64BIT" [(set (match_dup 4) (const_int 0))] "split_di (&operands[0], 1, &operands[3], &operands[4]);") (define_split [(set (match_operand:DI 0 "register_operand" "") (zero_extend:DI (match_operand:SI 1 "register_operand" ""))) (clobber (reg:CC 17))] "!TARGET_64BIT && reload_completed && true_regnum (operands[0]) == true_regnum (operands[1])" [(set (match_dup 4) (const_int 0))] "split_di (&operands[0], 1, &operands[3], &operands[4]);") (define_split [(set (match_operand:DI 0 "nonimmediate_operand" "") (zero_extend:DI (match_operand:SI 1 "general_operand" ""))) (clobber (reg:CC 17))] "!TARGET_64BIT && reload_completed && !SSE_REG_P (operands[0]) && !MMX_REG_P (operands[0])" [(set (match_dup 3) (match_dup 1)) (set (match_dup 4) (const_int 0))] "split_di (&operands[0], 1, &operands[3], &operands[4]);") (define_insn "zero_extendhidi2" [(set (match_operand:DI 0 "register_operand" "=r,r") (zero_extend:DI (match_operand:HI 1 "nonimmediate_operand" "r,m")))] "TARGET_64BIT" "@ movz{wl|x}\t{%1, %k0|%k0, %1} movz{wq|x}\t{%1, %0|%0, %1}" [(set_attr "type" "imovx") (set_attr "mode" "SI,DI")]) (define_insn "zero_extendqidi2" [(set (match_operand:DI 0 "register_operand" "=r,r") (zero_extend:DI (match_operand:QI 1 "nonimmediate_operand" "Q,m")))] "TARGET_64BIT" "@ movz{bl|x}\t{%1, %k0|%k0, %1} movz{bq|x}\t{%1, %0|%0, %1}" [(set_attr "type" "imovx") (set_attr "mode" "SI,DI")]) ;; Sign extension instructions (define_expand "extendsidi2" [(parallel [(set (match_operand:DI 0 "register_operand" "") (sign_extend:DI (match_operand:SI 1 "register_operand" ""))) (clobber (reg:CC 17)) (clobber (match_scratch:SI 2 ""))])] "" { if (TARGET_64BIT) { emit_insn (gen_extendsidi2_rex64 (operands[0], operands[1])); DONE; } }) (define_insn "*extendsidi2_1" [(set (match_operand:DI 0 "nonimmediate_operand" "=*A,r,?r,?*o") (sign_extend:DI (match_operand:SI 1 "register_operand" "0,0,r,r"))) (clobber (reg:CC 17)) (clobber (match_scratch:SI 2 "=X,X,X,&r"))] "!TARGET_64BIT" "#") (define_insn "extendsidi2_rex64" [(set (match_operand:DI 0 "register_operand" "=*a,r") (sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "*0,rm")))] "TARGET_64BIT" "@ {cltq|cdqe} movs{lq|x}\t{%1,%0|%0, %1}" [(set_attr "type" "imovx") (set_attr "mode" "DI") (set_attr "prefix_0f" "0") (set_attr "modrm" "0,1")]) (define_insn "extendhidi2" [(set (match_operand:DI 0 "register_operand" "=r") (sign_extend:DI (match_operand:HI 1 "nonimmediate_operand" "rm")))] "TARGET_64BIT" "movs{wq|x}\t{%1,%0|%0, %1}" [(set_attr "type" "imovx") (set_attr "mode" "DI")]) (define_insn "extendqidi2" [(set (match_operand:DI 0 "register_operand" "=r") (sign_extend:DI (match_operand:QI 1 "nonimmediate_operand" "qm")))] "TARGET_64BIT" "movs{bq|x}\t{%1,%0|%0, %1}" [(set_attr "type" "imovx") (set_attr "mode" "DI")]) ;; Extend to memory case when source register does die. (define_split [(set (match_operand:DI 0 "memory_operand" "") (sign_extend:DI (match_operand:SI 1 "register_operand" ""))) (clobber (reg:CC 17)) (clobber (match_operand:SI 2 "register_operand" ""))] "(reload_completed && dead_or_set_p (insn, operands[1]) && !reg_mentioned_p (operands[1], operands[0]))" [(set (match_dup 3) (match_dup 1)) (parallel [(set (match_dup 1) (ashiftrt:SI (match_dup 1) (const_int 31))) (clobber (reg:CC 17))]) (set (match_dup 4) (match_dup 1))] "split_di (&operands[0], 1, &operands[3], &operands[4]);") ;; Extend to memory case when source register does not die. (define_split [(set (match_operand:DI 0 "memory_operand" "") (sign_extend:DI (match_operand:SI 1 "register_operand" ""))) (clobber (reg:CC 17)) (clobber (match_operand:SI 2 "register_operand" ""))] "reload_completed" [(const_int 0)] { split_di (&operands[0], 1, &operands[3], &operands[4]); emit_move_insn (operands[3], operands[1]); /* Generate a cltd if possible and doing so it profitable. */ if (true_regnum (operands[1]) == 0 && true_regnum (operands[2]) == 1 && (optimize_size || TARGET_USE_CLTD)) { emit_insn (gen_ashrsi3_31 (operands[2], operands[1], GEN_INT (31))); } else { emit_move_insn (operands[2], operands[1]); emit_insn (gen_ashrsi3_31 (operands[2], operands[2], GEN_INT (31))); } emit_move_insn (operands[4], operands[2]); DONE; }) ;; Extend to register case. Optimize case where source and destination ;; registers match and cases where we can use cltd. (define_split [(set (match_operand:DI 0 "register_operand" "") (sign_extend:DI (match_operand:SI 1 "register_operand" ""))) (clobber (reg:CC 17)) (clobber (match_scratch:SI 2 ""))] "reload_completed" [(const_int 0)] { split_di (&operands[0], 1, &operands[3], &operands[4]); if (true_regnum (operands[3]) != true_regnum (operands[1])) emit_move_insn (operands[3], operands[1]); /* Generate a cltd if possible and doing so it profitable. */ if (true_regnum (operands[3]) == 0 && (optimize_size || TARGET_USE_CLTD)) { emit_insn (gen_ashrsi3_31 (operands[4], operands[3], GEN_INT (31))); DONE; } if (true_regnum (operands[4]) != true_regnum (operands[1])) emit_move_insn (operands[4], operands[1]); emit_insn (gen_ashrsi3_31 (operands[4], operands[4], GEN_INT (31))); DONE; }) (define_insn "extendhisi2" [(set (match_operand:SI 0 "register_operand" "=*a,r") (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "*0,rm")))] "" { switch (get_attr_prefix_0f (insn)) { case 0: return "{cwtl|cwde}"; default: return "movs{wl|x}\t{%1,%0|%0, %1}"; } } [(set_attr "type" "imovx") (set_attr "mode" "SI") (set (attr "prefix_0f") ;; movsx is short decodable while cwtl is vector decoded. (if_then_else (and (eq_attr "cpu" "!k6") (eq_attr "alternative" "0")) (const_string "0") (const_string "1"))) (set (attr "modrm") (if_then_else (eq_attr "prefix_0f" "0") (const_string "0") (const_string "1")))]) (define_insn "*extendhisi2_zext" [(set (match_operand:DI 0 "register_operand" "=*a,r") (zero_extend:DI (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "*0,rm"))))] "TARGET_64BIT" { switch (get_attr_prefix_0f (insn)) { case 0: return "{cwtl|cwde}"; default: return "movs{wl|x}\t{%1,%k0|%k0, %1}"; } } [(set_attr "type" "imovx") (set_attr "mode" "SI") (set (attr "prefix_0f") ;; movsx is short decodable while cwtl is vector decoded. (if_then_else (and (eq_attr "cpu" "!k6") (eq_attr "alternative" "0")) (const_string "0") (const_string "1"))) (set (attr "modrm") (if_then_else (eq_attr "prefix_0f" "0") (const_string "0") (const_string "1")))]) (define_insn "extendqihi2" [(set (match_operand:HI 0 "register_operand" "=*a,r") (sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "*0,qm")))] "" { switch (get_attr_prefix_0f (insn)) { case 0: return "{cbtw|cbw}"; default: return "movs{bw|x}\t{%1,%0|%0, %1}"; } } [(set_attr "type" "imovx") (set_attr "mode" "HI") (set (attr "prefix_0f") ;; movsx is short decodable while cwtl is vector decoded. (if_then_else (and (eq_attr "cpu" "!k6") (eq_attr "alternative" "0")) (const_string "0") (const_string "1"))) (set (attr "modrm") (if_then_else (eq_attr "prefix_0f" "0") (const_string "0") (const_string "1")))]) (define_insn "extendqisi2" [(set (match_operand:SI 0 "register_operand" "=r") (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "qm")))] "" "movs{bl|x}\t{%1,%0|%0, %1}" [(set_attr "type" "imovx") (set_attr "mode" "SI")]) (define_insn "*extendqisi2_zext" [(set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "qm"))))] "TARGET_64BIT" "movs{bl|x}\t{%1,%k0|%k0, %1}" [(set_attr "type" "imovx") (set_attr "mode" "SI")]) ;; Conversions between float and double. ;; These are all no-ops in the model used for the 80387. So just ;; emit moves. ;; %%% Kill these when call knows how to work out a DFmode push earlier. (define_insn "*dummy_extendsfdf2" [(set (match_operand:DF 0 "push_operand" "=<") (float_extend:DF (match_operand:SF 1 "nonimmediate_operand" "fY")))] "0" "#") (define_split [(set (match_operand:DF 0 "push_operand" "") (float_extend:DF (match_operand:SF 1 "fp_register_operand" "")))] "!TARGET_64BIT" [(set (reg:SI 7) (plus:SI (reg:SI 7) (const_int -8))) (set (mem:DF (reg:SI 7)) (float_extend:DF (match_dup 1)))]) (define_split [(set (match_operand:DF 0 "push_operand" "") (float_extend:DF (match_operand:SF 1 "fp_register_operand" "")))] "TARGET_64BIT" [(set (reg:DI 7) (plus:DI (reg:DI 7) (const_int -8))) (set (mem:DF (reg:DI 7)) (float_extend:DF (match_dup 1)))]) (define_insn "*dummy_extendsfxf2" [(set (match_operand:XF 0 "push_operand" "=<") (float_extend:XF (match_operand:SF 1 "nonimmediate_operand" "f")))] "0" "#") (define_split [(set (match_operand:XF 0 "push_operand" "") (float_extend:XF (match_operand:SF 1 "fp_register_operand" "")))] "" [(set (reg:SI 7) (plus:SI (reg:SI 7) (match_dup 2))) (set (mem:XF (reg:SI 7)) (float_extend:XF (match_dup 1)))] "operands[2] = GEN_INT (TARGET_128BIT_LONG_DOUBLE ? -16 : -12);") (define_split [(set (match_operand:XF 0 "push_operand" "") (float_extend:XF (match_operand:SF 1 "fp_register_operand" "")))] "TARGET_64BIT" [(set (reg:DI 7) (plus:DI (reg:DI 7) (match_dup 2))) (set (mem:DF (reg:DI 7)) (float_extend:XF (match_dup 1)))] "operands[2] = GEN_INT (TARGET_128BIT_LONG_DOUBLE ? -16 : -12);") (define_split [(set (match_operand:XF 0 "push_operand" "") (float_extend:XF (match_operand:DF 1 "fp_register_operand" "")))] "" [(set (reg:SI 7) (plus:SI (reg:SI 7) (match_dup 2))) (set (mem:DF (reg:SI 7)) (float_extend:XF (match_dup 1)))] "operands[2] = GEN_INT (TARGET_128BIT_LONG_DOUBLE ? -16 : -12);") (define_split [(set (match_operand:XF 0 "push_operand" "") (float_extend:XF (match_operand:DF 1 "fp_register_operand" "")))] "TARGET_64BIT" [(set (reg:DI 7) (plus:DI (reg:DI 7) (match_dup 2))) (set (mem:XF (reg:DI 7)) (float_extend:XF (match_dup 1)))] "operands[2] = GEN_INT (TARGET_128BIT_LONG_DOUBLE ? -16 : -12);") (define_expand "extendsfdf2" [(set (match_operand:DF 0 "nonimmediate_operand" "") (float_extend:DF (match_operand:SF 1 "general_operand" "")))] "TARGET_80387 || TARGET_SSE2" { /* ??? Needed for compress_float_constant since all fp constants are LEGITIMATE_CONSTANT_P. */ if (GET_CODE (operands[1]) == CONST_DOUBLE) operands[1] = validize_mem (force_const_mem (SFmode, operands[1])); if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) operands[1] = force_reg (SFmode, operands[1]); }) (define_insn "*extendsfdf2_1" [(set (match_operand:DF 0 "nonimmediate_operand" "=f#Y,mf#Y,Y#f") (float_extend:DF (match_operand:SF 1 "nonimmediate_operand" "fm#Y,f#Y,mY#f")))] "(TARGET_80387 || TARGET_SSE2) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" { switch (which_alternative) { case 0: if (REG_P (operands[1]) && find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp\t%y0"; else if (STACK_TOP_P (operands[0])) return "fld%z1\t%y1"; else return "fst\t%y0"; case 1: if (find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp%z0\t%y0"; else return "fst%z0\t%y0"; case 2: return "cvtss2sd\t{%1, %0|%0, %1}"; default: abort (); } } [(set_attr "type" "fmov,fmov,ssecvt") (set_attr "mode" "SF,XF,DF")]) (define_insn "*extendsfdf2_1_sse_only" [(set (match_operand:DF 0 "register_operand" "=Y") (float_extend:DF (match_operand:SF 1 "nonimmediate_operand" "mY")))] "!TARGET_80387 && TARGET_SSE2 && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "cvtss2sd\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "DF")]) (define_expand "extendsfxf2" [(set (match_operand:XF 0 "nonimmediate_operand" "") (float_extend:XF (match_operand:SF 1 "general_operand" "")))] "TARGET_80387" { /* ??? Needed for compress_float_constant since all fp constants are LEGITIMATE_CONSTANT_P. */ if (GET_CODE (operands[1]) == CONST_DOUBLE) operands[1] = validize_mem (force_const_mem (SFmode, operands[1])); if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) operands[1] = force_reg (SFmode, operands[1]); }) (define_insn "*extendsfxf2_1" [(set (match_operand:XF 0 "nonimmediate_operand" "=f,m") (float_extend:XF (match_operand:SF 1 "nonimmediate_operand" "fm,f")))] "TARGET_80387 && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" { switch (which_alternative) { case 0: if (REG_P (operands[1]) && find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp\t%y0"; else if (STACK_TOP_P (operands[0])) return "fld%z1\t%y1"; else return "fst\t%y0"; case 1: /* There is no non-popping store to memory for XFmode. So if we need one, follow the store with a load. */ if (! find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp%z0\t%y0\n\tfld%z0\t%y0"; else return "fstp%z0\t%y0"; default: abort (); } } [(set_attr "type" "fmov") (set_attr "mode" "SF,XF")]) (define_expand "extenddfxf2" [(set (match_operand:XF 0 "nonimmediate_operand" "") (float_extend:XF (match_operand:DF 1 "general_operand" "")))] "TARGET_80387" { /* ??? Needed for compress_float_constant since all fp constants are LEGITIMATE_CONSTANT_P. */ if (GET_CODE (operands[1]) == CONST_DOUBLE) operands[1] = validize_mem (force_const_mem (DFmode, operands[1])); if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) operands[1] = force_reg (DFmode, operands[1]); }) (define_insn "*extenddfxf2_1" [(set (match_operand:XF 0 "nonimmediate_operand" "=f,m") (float_extend:XF (match_operand:DF 1 "nonimmediate_operand" "fm,f")))] "TARGET_80387 && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" { switch (which_alternative) { case 0: if (REG_P (operands[1]) && find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp\t%y0"; else if (STACK_TOP_P (operands[0])) return "fld%z1\t%y1"; else return "fst\t%y0"; case 1: /* There is no non-popping store to memory for XFmode. So if we need one, follow the store with a load. */ if (! find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp%z0\t%y0\n\tfld%z0\t%y0"; else return "fstp%z0\t%y0"; default: abort (); } } [(set_attr "type" "fmov") (set_attr "mode" "DF,XF")]) ;; %%% This seems bad bad news. ;; This cannot output into an f-reg because there is no way to be sure ;; of truncating in that case. Otherwise this is just like a simple move ;; insn. So we pretend we can output to a reg in order to get better ;; register preferencing, but we really use a stack slot. (define_expand "truncdfsf2" [(parallel [(set (match_operand:SF 0 "nonimmediate_operand" "") (float_truncate:SF (match_operand:DF 1 "register_operand" ""))) (clobber (match_dup 2))])] "TARGET_80387 || TARGET_SSE2" " if (TARGET_80387) operands[2] = assign_386_stack_local (SFmode, 0); else { emit_insn (gen_truncdfsf2_sse_only (operands[0], operands[1])); DONE; } ") (define_insn "*truncdfsf2_1" [(set (match_operand:SF 0 "nonimmediate_operand" "=m,?f#rx,?r#fx,?x#rf") (float_truncate:SF (match_operand:DF 1 "register_operand" "f,f,f,f"))) (clobber (match_operand:SF 2 "memory_operand" "=X,m,m,m"))] "TARGET_80387 && !TARGET_SSE2" { switch (which_alternative) { case 0: if (find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp%z0\t%y0"; else return "fst%z0\t%y0"; default: abort (); } } [(set_attr "type" "fmov,multi,multi,multi") (set_attr "mode" "SF,SF,SF,SF")]) (define_insn "*truncdfsf2_1_sse" [(set (match_operand:SF 0 "nonimmediate_operand" "=*!m#fxr,?f#xr,?r#fx,?x#fr,Y#fr") (float_truncate:SF (match_operand:DF 1 "nonimmediate_operand" "f#Y,f#Y,f#Y,f#Y,mY#f"))) (clobber (match_operand:SF 2 "memory_operand" "=X,m,m,m,X"))] "TARGET_80387 && TARGET_SSE2 && !TARGET_SSE_PARTIAL_REGS_FOR_CVTSD2SS" { switch (which_alternative) { case 0: if (find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp%z0\t%y0"; else return "fst%z0\t%y0"; case 4: return "#"; default: abort (); } } [(set_attr "type" "fmov,multi,multi,multi,ssecvt") (set_attr "mode" "SF,SF,SF,SF,DF")]) (define_insn "*truncdfsf2_1_sse_nooverlap" [(set (match_operand:SF 0 "nonimmediate_operand" "=*!m,?f#rx,?r#fx,?x#rf,&Y") (float_truncate:SF (match_operand:DF 1 "nonimmediate_operand" "f#Y,f#Y,f#Y,f#Y,mY#f"))) (clobber (match_operand:SF 2 "memory_operand" "=X,m,m,m,X"))] "TARGET_80387 && TARGET_SSE2 && TARGET_SSE_PARTIAL_REGS_FOR_CVTSD2SS" { switch (which_alternative) { case 0: if (find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp%z0\t%y0"; else return "fst%z0\t%y0"; case 4: return "#"; default: abort (); } } [(set_attr "type" "fmov,multi,multi,multi,ssecvt") (set_attr "mode" "SF,SF,SF,SF,DF")]) (define_insn "*truncdfsf2_2" [(set (match_operand:SF 0 "nonimmediate_operand" "=Y,Y,!m") (float_truncate:SF (match_operand:DF 1 "nonimmediate_operand" "Y,mY,f#Y")))] "TARGET_80387 && TARGET_SSE2 && !TARGET_SSE_PARTIAL_REGS_FOR_CVTSD2SS && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" { switch (which_alternative) { case 0: case 1: return "cvtsd2ss\t{%1, %0|%0, %1}"; case 2: if (find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp%z0\t%y0"; else return "fst%z0\t%y0"; default: abort (); } } [(set_attr "type" "ssecvt,ssecvt,fmov") (set_attr "athlon_decode" "vector,double,*") (set_attr "mode" "SF,SF,SF")]) (define_insn "*truncdfsf2_2_nooverlap" [(set (match_operand:SF 0 "nonimmediate_operand" "=&Y,!m") (float_truncate:SF (match_operand:DF 1 "nonimmediate_operand" "mY,f")))] "TARGET_80387 && TARGET_SSE2 && TARGET_SSE_PARTIAL_REGS_FOR_CVTSD2SS && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" { switch (which_alternative) { case 0: return "#"; case 1: if (find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp%z0\t%y0"; else return "fst%z0\t%y0"; default: abort (); } } [(set_attr "type" "ssecvt,fmov") (set_attr "mode" "DF,SF")]) (define_insn "*truncdfsf2_3" [(set (match_operand:SF 0 "memory_operand" "=m") (float_truncate:SF (match_operand:DF 1 "register_operand" "f")))] "TARGET_80387" { if (find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp%z0\t%y0"; else return "fst%z0\t%y0"; } [(set_attr "type" "fmov") (set_attr "mode" "SF")]) (define_insn "truncdfsf2_sse_only" [(set (match_operand:SF 0 "register_operand" "=Y,Y") (float_truncate:SF (match_operand:DF 1 "nonimmediate_operand" "Y,mY")))] "!TARGET_80387 && TARGET_SSE2 && !TARGET_SSE_PARTIAL_REGS_FOR_CVTSD2SS" "cvtsd2ss\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "athlon_decode" "vector,double") (set_attr "mode" "SF")]) (define_insn "*truncdfsf2_sse_only_nooverlap" [(set (match_operand:SF 0 "register_operand" "=&Y") (float_truncate:SF (match_operand:DF 1 "nonimmediate_operand" "mY")))] "!TARGET_80387 && TARGET_SSE2 && TARGET_SSE_PARTIAL_REGS_FOR_CVTSD2SS" "#" [(set_attr "type" "ssecvt") (set_attr "mode" "DF")]) (define_split [(set (match_operand:SF 0 "memory_operand" "") (float_truncate:SF (match_operand:DF 1 "register_operand" ""))) (clobber (match_operand:SF 2 "memory_operand" ""))] "TARGET_80387" [(set (match_dup 0) (float_truncate:SF (match_dup 1)))] "") ; Avoid possible reformatting penalty on the destination by first ; zeroing it out (define_split [(set (match_operand:SF 0 "register_operand" "") (float_truncate:SF (match_operand:DF 1 "nonimmediate_operand" ""))) (clobber (match_operand 2 "" ""))] "TARGET_80387 && reload_completed && SSE_REG_P (operands[0]) && !STACK_REG_P (operands[1])" [(const_int 0)] { rtx src, dest; if (!TARGET_SSE_PARTIAL_REGS_FOR_CVTSD2SS) emit_insn (gen_truncdfsf2_sse_only (operands[0], operands[1])); else { dest = simplify_gen_subreg (V4SFmode, operands[0], SFmode, 0); src = simplify_gen_subreg (V2DFmode, operands[1], DFmode, 0); /* simplify_gen_subreg refuses to widen memory references. */ if (GET_CODE (src) == SUBREG) alter_subreg (&src); if (reg_overlap_mentioned_p (operands[0], operands[1])) abort (); emit_insn (gen_sse_clrv4sf (dest, CONST0_RTX (V4SFmode))); emit_insn (gen_cvtsd2ss (dest, dest, src)); } DONE; }) (define_split [(set (match_operand:SF 0 "register_operand" "") (float_truncate:SF (match_operand:DF 1 "nonimmediate_operand" "")))] "TARGET_80387 && reload_completed && SSE_REG_P (operands[0]) && TARGET_SSE_PARTIAL_REGS_FOR_CVTSD2SS" [(const_int 0)] { rtx src, dest; dest = simplify_gen_subreg (V4SFmode, operands[0], SFmode, 0); src = simplify_gen_subreg (V2DFmode, operands[1], DFmode, 0); /* simplify_gen_subreg refuses to widen memory references. */ if (GET_CODE (src) == SUBREG) alter_subreg (&src); if (reg_overlap_mentioned_p (operands[0], operands[1])) abort (); emit_insn (gen_sse_clrv4sf (dest, CONST0_RTX (V4SFmode))); emit_insn (gen_cvtsd2ss (dest, dest, src)); DONE; }) (define_split [(set (match_operand:SF 0 "register_operand" "") (float_truncate:SF (match_operand:DF 1 "fp_register_operand" ""))) (clobber (match_operand:SF 2 "memory_operand" ""))] "TARGET_80387 && reload_completed" [(set (match_dup 2) (float_truncate:SF (match_dup 1))) (set (match_dup 0) (match_dup 2))] "") (define_expand "truncxfsf2" [(parallel [(set (match_operand:SF 0 "nonimmediate_operand" "") (float_truncate:SF (match_operand:XF 1 "register_operand" ""))) (clobber (match_dup 2))])] "TARGET_80387" "operands[2] = assign_386_stack_local (SFmode, 0);") (define_insn "*truncxfsf2_1" [(set (match_operand:SF 0 "nonimmediate_operand" "=m,?f#rx,?r#fx,?x#rf") (float_truncate:SF (match_operand:XF 1 "register_operand" "f,f,f,f"))) (clobber (match_operand:SF 2 "memory_operand" "=X,m,m,m"))] "TARGET_80387" { switch (which_alternative) { case 0: if (find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp%z0\t%y0"; else return "fst%z0\t%y0"; default: abort(); } } [(set_attr "type" "fmov,multi,multi,multi") (set_attr "mode" "SF")]) (define_insn "*truncxfsf2_2" [(set (match_operand:SF 0 "memory_operand" "=m") (float_truncate:SF (match_operand:XF 1 "register_operand" "f")))] "TARGET_80387" { if (find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp%z0\t%y0"; else return "fst%z0\t%y0"; } [(set_attr "type" "fmov") (set_attr "mode" "SF")]) (define_split [(set (match_operand:SF 0 "memory_operand" "") (float_truncate:SF (match_operand:XF 1 "register_operand" ""))) (clobber (match_operand:SF 2 "memory_operand" ""))] "TARGET_80387" [(set (match_dup 0) (float_truncate:SF (match_dup 1)))] "") (define_split [(set (match_operand:SF 0 "register_operand" "") (float_truncate:SF (match_operand:XF 1 "register_operand" ""))) (clobber (match_operand:SF 2 "memory_operand" ""))] "TARGET_80387 && reload_completed" [(set (match_dup 2) (float_truncate:SF (match_dup 1))) (set (match_dup 0) (match_dup 2))] "") (define_expand "truncxfdf2" [(parallel [(set (match_operand:DF 0 "nonimmediate_operand" "") (float_truncate:DF (match_operand:XF 1 "register_operand" ""))) (clobber (match_dup 2))])] "TARGET_80387" "operands[2] = assign_386_stack_local (DFmode, 0);") (define_insn "*truncxfdf2_1" [(set (match_operand:DF 0 "nonimmediate_operand" "=m,?f#rY,?r#fY,?Y#rf") (float_truncate:DF (match_operand:XF 1 "register_operand" "f,f,f,f"))) (clobber (match_operand:DF 2 "memory_operand" "=X,m,m,m"))] "TARGET_80387" { switch (which_alternative) { case 0: if (find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp%z0\t%y0"; else return "fst%z0\t%y0"; default: abort(); } abort (); } [(set_attr "type" "fmov,multi,multi,multi") (set_attr "mode" "DF")]) (define_insn "*truncxfdf2_2" [(set (match_operand:DF 0 "memory_operand" "=m") (float_truncate:DF (match_operand:XF 1 "register_operand" "f")))] "TARGET_80387" { if (find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) return "fstp%z0\t%y0"; else return "fst%z0\t%y0"; } [(set_attr "type" "fmov") (set_attr "mode" "DF")]) (define_split [(set (match_operand:DF 0 "memory_operand" "") (float_truncate:DF (match_operand:XF 1 "register_operand" ""))) (clobber (match_operand:DF 2 "memory_operand" ""))] "TARGET_80387" [(set (match_dup 0) (float_truncate:DF (match_dup 1)))] "") (define_split [(set (match_operand:DF 0 "register_operand" "") (float_truncate:DF (match_operand:XF 1 "register_operand" ""))) (clobber (match_operand:DF 2 "memory_operand" ""))] "TARGET_80387 && reload_completed" [(set (match_dup 2) (float_truncate:DF (match_dup 1))) (set (match_dup 0) (match_dup 2))] "") ;; %%% Break up all these bad boys. ;; Signed conversion to DImode. (define_expand "fix_truncxfdi2" [(set (match_operand:DI 0 "nonimmediate_operand" "") (fix:DI (match_operand:XF 1 "register_operand" "")))] "TARGET_80387" "") (define_expand "fix_truncdfdi2" [(set (match_operand:DI 0 "nonimmediate_operand" "") (fix:DI (match_operand:DF 1 "register_operand" "")))] "TARGET_80387 || (TARGET_SSE2 && TARGET_64BIT)" { if (TARGET_64BIT && TARGET_SSE2) { rtx out = REG_P (operands[0]) ? operands[0] : gen_reg_rtx (DImode); emit_insn (gen_fix_truncdfdi_sse (out, operands[1])); if (out != operands[0]) emit_move_insn (operands[0], out); DONE; } }) (define_expand "fix_truncsfdi2" [(set (match_operand:DI 0 "nonimmediate_operand" "") (fix:DI (match_operand:SF 1 "register_operand" "")))] "TARGET_80387 || (TARGET_SSE && TARGET_64BIT)" { if (TARGET_SSE && TARGET_64BIT) { rtx out = REG_P (operands[0]) ? operands[0] : gen_reg_rtx (DImode); emit_insn (gen_fix_truncsfdi_sse (out, operands[1])); if (out != operands[0]) emit_move_insn (operands[0], out); DONE; } }) ;; See the comments in i386.h near OPTIMIZE_MODE_SWITCHING for the description ;; of the machinery. (define_insn_and_split "*fix_truncdi_1" [(set (match_operand:DI 0 "nonimmediate_operand" "=m,?r") (fix:DI (match_operand 1 "register_operand" "f,f")))] "TARGET_80387 && FLOAT_MODE_P (GET_MODE (operands[1])) && !reload_completed && !reload_in_progress && (!SSE_FLOAT_MODE_P (GET_MODE (operands[1])) || !TARGET_64BIT)" "#" "&& 1" [(const_int 0)] { ix86_optimize_mode_switching = 1; operands[2] = assign_386_stack_local (HImode, 1); operands[3] = assign_386_stack_local (HImode, 2); if (memory_operand (operands[0], VOIDmode)) emit_insn (gen_fix_truncdi_memory (operands[0], operands[1], operands[2], operands[3])); else { operands[4] = assign_386_stack_local (DImode, 0); emit_insn (gen_fix_truncdi_nomemory (operands[0], operands[1], operands[2], operands[3], operands[4])); } DONE; } [(set_attr "type" "fistp") (set_attr "mode" "DI")]) (define_insn "fix_truncdi_nomemory" [(set (match_operand:DI 0 "nonimmediate_operand" "=m,?r") (fix:DI (match_operand 1 "register_operand" "f,f"))) (use (match_operand:HI 2 "memory_operand" "m,m")) (use (match_operand:HI 3 "memory_operand" "m,m")) (clobber (match_operand:DI 4 "memory_operand" "=m,m")) (clobber (match_scratch:DF 5 "=&1f,&1f"))] "TARGET_80387 && FLOAT_MODE_P (GET_MODE (operands[1])) && (!SSE_FLOAT_MODE_P (GET_MODE (operands[1])) || !TARGET_64BIT)" "#" [(set_attr "type" "fistp") (set_attr "mode" "DI")]) (define_insn "fix_truncdi_memory" [(set (match_operand:DI 0 "memory_operand" "=m") (fix:DI (match_operand 1 "register_operand" "f"))) (use (match_operand:HI 2 "memory_operand" "m")) (use (match_operand:HI 3 "memory_operand" "m")) (clobber (match_scratch:DF 4 "=&1f"))] "TARGET_80387 && FLOAT_MODE_P (GET_MODE (operands[1])) && (!SSE_FLOAT_MODE_P (GET_MODE (operands[1])) || !TARGET_64BIT)" "* operands[5] = operands[4]; return output_fix_trunc (insn, operands);" [(set_attr "type" "fistp") (set_attr "mode" "DI")]) (define_split [(set (match_operand:DI 0 "register_operand" "") (fix:DI (match_operand 1 "register_operand" ""))) (use (match_operand:HI 2 "memory_operand" "")) (use (match_operand:HI 3 "memory_operand" "")) (clobber (match_operand:DI 4 "memory_operand" "")) (clobber (match_scratch 5 ""))] "reload_completed" [(parallel [(set (match_dup 4) (fix:DI (match_dup 1))) (use (match_dup 2)) (use (match_dup 3)) (clobber (match_dup 5))]) (set (match_dup 0) (match_dup 4))] "") (define_split [(set (match_operand:DI 0 "memory_operand" "") (fix:DI (match_operand 1 "register_operand" ""))) (use (match_operand:HI 2 "memory_operand" "")) (use (match_operand:HI 3 "memory_operand" "")) (clobber (match_operand:DI 4 "memory_operand" "")) (clobber (match_scratch 5 ""))] "reload_completed" [(parallel [(set (match_dup 0) (fix:DI (match_dup 1))) (use (match_dup 2)) (use (match_dup 3)) (clobber (match_dup 5))])] "") ;; When SSE available, it is always faster to use it! (define_insn "fix_truncsfdi_sse" [(set (match_operand:DI 0 "register_operand" "=r,r") (fix:DI (match_operand:SF 1 "nonimmediate_operand" "x,xm")))] "TARGET_64BIT && TARGET_SSE" "cvttss2si{q}\t{%1, %0|%0, %1}" [(set_attr "type" "sseicvt") (set_attr "mode" "SF") (set_attr "athlon_decode" "double,vector")]) ;; Avoid vector decoded form of the instruction. (define_peephole2 [(match_scratch:SF 2 "x") (set (match_operand:DI 0 "register_operand" "") (fix:DI (match_operand:SF 1 "memory_operand" "")))] "TARGET_K8 && !optimize_size" [(set (match_dup 2) (match_dup 1)) (set (match_dup 0) (fix:DI (match_dup 2)))] "") (define_insn "fix_truncdfdi_sse" [(set (match_operand:DI 0 "register_operand" "=r,r") (fix:DI (match_operand:DF 1 "nonimmediate_operand" "Y,Ym")))] "TARGET_64BIT && TARGET_SSE2" "cvttsd2si{q}\t{%1, %0|%0, %1}" [(set_attr "type" "sseicvt,sseicvt") (set_attr "mode" "DF") (set_attr "athlon_decode" "double,vector")]) ;; Avoid vector decoded form of the instruction. (define_peephole2 [(match_scratch:DF 2 "Y") (set (match_operand:DI 0 "register_operand" "") (fix:DI (match_operand:DF 1 "memory_operand" "")))] "TARGET_K8 && !optimize_size" [(set (match_dup 2) (match_dup 1)) (set (match_dup 0) (fix:DI (match_dup 2)))] "") ;; Signed conversion to SImode. (define_expand "fix_truncxfsi2" [(set (match_operand:SI 0 "nonimmediate_operand" "") (fix:SI (match_operand:XF 1 "register_operand" "")))] "TARGET_80387" "") (define_expand "fix_truncdfsi2" [(set (match_operand:SI 0 "nonimmediate_operand" "") (fix:SI (match_operand:DF 1 "register_operand" "")))] "TARGET_80387 || TARGET_SSE2" { if (TARGET_SSE2) { rtx out = REG_P (operands[0]) ? operands[0] : gen_reg_rtx (SImode); emit_insn (gen_fix_truncdfsi_sse (out, operands[1])); if (out != operands[0]) emit_move_insn (operands[0], out); DONE; } }) (define_expand "fix_truncsfsi2" [(set (match_operand:SI 0 "nonimmediate_operand" "") (fix:SI (match_operand:SF 1 "register_operand" "")))] "TARGET_80387 || TARGET_SSE" { if (TARGET_SSE) { rtx out = REG_P (operands[0]) ? operands[0] : gen_reg_rtx (SImode); emit_insn (gen_fix_truncsfsi_sse (out, operands[1])); if (out != operands[0]) emit_move_insn (operands[0], out); DONE; } }) ;; See the comments in i386.h near OPTIMIZE_MODE_SWITCHING for the description ;; of the machinery. (define_insn_and_split "*fix_truncsi_1" [(set (match_operand:SI 0 "nonimmediate_operand" "=m,?r") (fix:SI (match_operand 1 "register_operand" "f,f")))] "TARGET_80387 && FLOAT_MODE_P (GET_MODE (operands[1])) && !reload_completed && !reload_in_progress && !SSE_FLOAT_MODE_P (GET_MODE (operands[1]))" "#" "&& 1" [(const_int 0)] { ix86_optimize_mode_switching = 1; operands[2] = assign_386_stack_local (HImode, 1); operands[3] = assign_386_stack_local (HImode, 2); if (memory_operand (operands[0], VOIDmode)) emit_insn (gen_fix_truncsi_memory (operands[0], operands[1], operands[2], operands[3])); else { operands[4] = assign_386_stack_local (SImode, 0); emit_insn (gen_fix_truncsi_nomemory (operands[0], operands[1], operands[2], operands[3], operands[4])); } DONE; } [(set_attr "type" "fistp") (set_attr "mode" "SI")]) (define_insn "fix_truncsi_nomemory" [(set (match_operand:SI 0 "nonimmediate_operand" "=m,?r") (fix:SI (match_operand 1 "register_operand" "f,f"))) (use (match_operand:HI 2 "memory_operand" "m,m")) (use (match_operand:HI 3 "memory_operand" "m,m")) (clobber (match_operand:SI 4 "memory_operand" "=m,m"))] "TARGET_80387 && FLOAT_MODE_P (GET_MODE (operands[1])) && !SSE_FLOAT_MODE_P (GET_MODE (operands[1]))" "#" [(set_attr "type" "fistp") (set_attr "mode" "SI")]) (define_insn "fix_truncsi_memory" [(set (match_operand:SI 0 "memory_operand" "=m") (fix:SI (match_operand 1 "register_operand" "f"))) (use (match_operand:HI 2 "memory_operand" "m")) (use (match_operand:HI 3 "memory_operand" "m"))] "TARGET_80387 && FLOAT_MODE_P (GET_MODE (operands[1])) && !SSE_FLOAT_MODE_P (GET_MODE (operands[1]))" "* return output_fix_trunc (insn, operands);" [(set_attr "type" "fistp") (set_attr "mode" "SI")]) ;; When SSE available, it is always faster to use it! (define_insn "fix_truncsfsi_sse" [(set (match_operand:SI 0 "register_operand" "=r,r") (fix:SI (match_operand:SF 1 "nonimmediate_operand" "x,xm")))] "TARGET_SSE" "cvttss2si\t{%1, %0|%0, %1}" [(set_attr "type" "sseicvt") (set_attr "mode" "DF") (set_attr "athlon_decode" "double,vector")]) ;; Avoid vector decoded form of the instruction. (define_peephole2 [(match_scratch:SF 2 "x") (set (match_operand:SI 0 "register_operand" "") (fix:SI (match_operand:SF 1 "memory_operand" "")))] "TARGET_K8 && !optimize_size" [(set (match_dup 2) (match_dup 1)) (set (match_dup 0) (fix:SI (match_dup 2)))] "") (define_insn "fix_truncdfsi_sse" [(set (match_operand:SI 0 "register_operand" "=r,r") (fix:SI (match_operand:DF 1 "nonimmediate_operand" "Y,Ym")))] "TARGET_SSE2" "cvttsd2si\t{%1, %0|%0, %1}" [(set_attr "type" "sseicvt") (set_attr "mode" "DF") (set_attr "athlon_decode" "double,vector")]) ;; Avoid vector decoded form of the instruction. (define_peephole2 [(match_scratch:DF 2 "Y") (set (match_operand:SI 0 "register_operand" "") (fix:SI (match_operand:DF 1 "memory_operand" "")))] "TARGET_K8 && !optimize_size" [(set (match_dup 2) (match_dup 1)) (set (match_dup 0) (fix:SI (match_dup 2)))] "") (define_split [(set (match_operand:SI 0 "register_operand" "") (fix:SI (match_operand 1 "register_operand" ""))) (use (match_operand:HI 2 "memory_operand" "")) (use (match_operand:HI 3 "memory_operand" "")) (clobber (match_operand:SI 4 "memory_operand" ""))] "reload_completed" [(parallel [(set (match_dup 4) (fix:SI (match_dup 1))) (use (match_dup 2)) (use (match_dup 3))]) (set (match_dup 0) (match_dup 4))] "") (define_split [(set (match_operand:SI 0 "memory_operand" "") (fix:SI (match_operand 1 "register_operand" ""))) (use (match_operand:HI 2 "memory_operand" "")) (use (match_operand:HI 3 "memory_operand" "")) (clobber (match_operand:SI 4 "memory_operand" ""))] "reload_completed" [(parallel [(set (match_dup 0) (fix:SI (match_dup 1))) (use (match_dup 2)) (use (match_dup 3))])] "") ;; Signed conversion to HImode. (define_expand "fix_truncxfhi2" [(set (match_operand:HI 0 "nonimmediate_operand" "") (fix:HI (match_operand:XF 1 "register_operand" "")))] "TARGET_80387" "") (define_expand "fix_truncdfhi2" [(set (match_operand:HI 0 "nonimmediate_operand" "") (fix:HI (match_operand:DF 1 "register_operand" "")))] "TARGET_80387 && !TARGET_SSE2" "") (define_expand "fix_truncsfhi2" [(set (match_operand:HI 0 "nonimmediate_operand" "") (fix:HI (match_operand:SF 1 "register_operand" "")))] "TARGET_80387 && !TARGET_SSE" "") ;; See the comments in i386.h near OPTIMIZE_MODE_SWITCHING for the description ;; of the machinery. (define_insn_and_split "*fix_trunchi_1" [(set (match_operand:HI 0 "nonimmediate_operand" "=m,?r") (fix:HI (match_operand 1 "register_operand" "f,f")))] "TARGET_80387 && FLOAT_MODE_P (GET_MODE (operands[1])) && !reload_completed && !reload_in_progress && !SSE_FLOAT_MODE_P (GET_MODE (operands[1]))" "#" "" [(const_int 0)] { ix86_optimize_mode_switching = 1; operands[2] = assign_386_stack_local (HImode, 1); operands[3] = assign_386_stack_local (HImode, 2); if (memory_operand (operands[0], VOIDmode)) emit_insn (gen_fix_trunchi_memory (operands[0], operands[1], operands[2], operands[3])); else { operands[4] = assign_386_stack_local (HImode, 0); emit_insn (gen_fix_trunchi_nomemory (operands[0], operands[1], operands[2], operands[3], operands[4])); } DONE; } [(set_attr "type" "fistp") (set_attr "mode" "HI")]) (define_insn "fix_trunchi_nomemory" [(set (match_operand:HI 0 "nonimmediate_operand" "=m,?r") (fix:HI (match_operand 1 "register_operand" "f,f"))) (use (match_operand:HI 2 "memory_operand" "m,m")) (use (match_operand:HI 3 "memory_operand" "m,m")) (clobber (match_operand:HI 4 "memory_operand" "=m,m"))] "TARGET_80387 && FLOAT_MODE_P (GET_MODE (operands[1])) && !SSE_FLOAT_MODE_P (GET_MODE (operands[1]))" "#" [(set_attr "type" "fistp") (set_attr "mode" "HI")]) (define_insn "fix_trunchi_memory" [(set (match_operand:HI 0 "memory_operand" "=m") (fix:HI (match_operand 1 "register_operand" "f"))) (use (match_operand:HI 2 "memory_operand" "m")) (use (match_operand:HI 3 "memory_operand" "m"))] "TARGET_80387 && FLOAT_MODE_P (GET_MODE (operands[1])) && !SSE_FLOAT_MODE_P (GET_MODE (operands[1]))" "* return output_fix_trunc (insn, operands);" [(set_attr "type" "fistp") (set_attr "mode" "HI")]) (define_split [(set (match_operand:HI 0 "memory_operand" "") (fix:HI (match_operand 1 "register_operand" ""))) (use (match_operand:HI 2 "memory_operand" "")) (use (match_operand:HI 3 "memory_operand" "")) (clobber (match_operand:HI 4 "memory_operand" ""))] "reload_completed" [(parallel [(set (match_dup 0) (fix:HI (match_dup 1))) (use (match_dup 2)) (use (match_dup 3))])] "") (define_split [(set (match_operand:HI 0 "register_operand" "") (fix:HI (match_operand 1 "register_operand" ""))) (use (match_operand:HI 2 "memory_operand" "")) (use (match_operand:HI 3 "memory_operand" "")) (clobber (match_operand:HI 4 "memory_operand" ""))] "reload_completed" [(parallel [(set (match_dup 4) (fix:HI (match_dup 1))) (use (match_dup 2)) (use (match_dup 3)) (clobber (match_dup 4))]) (set (match_dup 0) (match_dup 4))] "") ;; %% Not used yet. (define_insn "x86_fnstcw_1" [(set (match_operand:HI 0 "memory_operand" "=m") (unspec:HI [(reg:HI 18)] UNSPEC_FSTCW))] "TARGET_80387" "fnstcw\t%0" [(set_attr "length" "2") (set_attr "mode" "HI") (set_attr "unit" "i387") (set_attr "ppro_uops" "few")]) (define_insn "x86_fldcw_1" [(set (reg:HI 18) (unspec:HI [(match_operand:HI 0 "memory_operand" "m")] UNSPEC_FLDCW))] "TARGET_80387" "fldcw\t%0" [(set_attr "length" "2") (set_attr "mode" "HI") (set_attr "unit" "i387") (set_attr "athlon_decode" "vector") (set_attr "ppro_uops" "few")]) ;; Conversion between fixed point and floating point. ;; Even though we only accept memory inputs, the backend _really_ ;; wants to be able to do this between registers. (define_expand "floathisf2" [(set (match_operand:SF 0 "register_operand" "") (float:SF (match_operand:HI 1 "nonimmediate_operand" "")))] "TARGET_SSE || TARGET_80387" { if (TARGET_SSE && TARGET_SSE_MATH) { emit_insn (gen_floatsisf2 (operands[0], convert_to_mode (SImode, operands[1], 0))); DONE; } }) (define_insn "*floathisf2_1" [(set (match_operand:SF 0 "register_operand" "=f,f") (float:SF (match_operand:HI 1 "nonimmediate_operand" "m,r")))] "TARGET_80387 && (!TARGET_SSE || !TARGET_SSE_MATH)" "@ fild%z1\t%1 #" [(set_attr "type" "fmov,multi") (set_attr "mode" "SF") (set_attr "fp_int_src" "true")]) (define_expand "floatsisf2" [(set (match_operand:SF 0 "register_operand" "") (float:SF (match_operand:SI 1 "nonimmediate_operand" "")))] "TARGET_SSE || TARGET_80387" "") (define_insn "*floatsisf2_i387" [(set (match_operand:SF 0 "register_operand" "=f#x,?f#x,x#f,x#f") (float:SF (match_operand:SI 1 "nonimmediate_operand" "m,r,r,mr")))] "TARGET_80387 && (!TARGET_SSE || TARGET_MIX_SSE_I387)" "@ fild%z1\t%1 # cvtsi2ss\t{%1, %0|%0, %1} cvtsi2ss\t{%1, %0|%0, %1}" [(set_attr "type" "fmov,multi,sseicvt,sseicvt") (set_attr "mode" "SF") (set_attr "athlon_decode" "*,*,vector,double") (set_attr "fp_int_src" "true")]) (define_insn "*floatsisf2_sse" [(set (match_operand:SF 0 "register_operand" "=x,x") (float:SF (match_operand:SI 1 "nonimmediate_operand" "r,mr")))] "TARGET_SSE" "cvtsi2ss\t{%1, %0|%0, %1}" [(set_attr "type" "sseicvt") (set_attr "mode" "SF") (set_attr "athlon_decode" "vector,double") (set_attr "fp_int_src" "true")]) ; Avoid possible reformatting penalty on the destination by first ; zeroing it out (define_split [(set (match_operand:SF 0 "register_operand" "") (float:SF (match_operand:SI 1 "nonimmediate_operand" "")))] "TARGET_80387 && reload_completed && TARGET_SSE_PARTIAL_REGS && SSE_REG_P (operands[0])" [(const_int 0)] { rtx dest; dest = simplify_gen_subreg (V4SFmode, operands[0], SFmode, 0); emit_insn (gen_sse_clrv4sf (dest, CONST0_RTX (V4SFmode))); emit_insn (gen_cvtsi2ss (dest, dest, operands[1])); DONE; }) (define_expand "floatdisf2" [(set (match_operand:SF 0 "register_operand" "") (float:SF (match_operand:DI 1 "nonimmediate_operand" "")))] "(TARGET_64BIT && TARGET_SSE) || TARGET_80387" "") (define_insn "*floatdisf2_i387_only" [(set (match_operand:SF 0 "register_operand" "=f,?f") (float:SF (match_operand:DI 1 "nonimmediate_operand" "m,r")))] "TARGET_80387 && (!TARGET_SSE || !TARGET_64BIT || TARGET_MIX_SSE_I387)" "@ fild%z1\t%1 #" [(set_attr "type" "fmov,multi") (set_attr "mode" "SF") (set_attr "fp_int_src" "true")]) (define_insn "*floatdisf2_i387" [(set (match_operand:SF 0 "register_operand" "=f#x,?f#x,x#f,x#f") (float:SF (match_operand:DI 1 "nonimmediate_operand" "m,r,r,mr")))] "TARGET_64BIT && TARGET_80387 && (!TARGET_SSE || TARGET_MIX_SSE_I387)" "@ fild%z1\t%1 # cvtsi2ss{q}\t{%1, %0|%0, %1} cvtsi2ss{q}\t{%1, %0|%0, %1}" [(set_attr "type" "fmov,multi,sseicvt,sseicvt") (set_attr "mode" "SF") (set_attr "athlon_decode" "*,*,vector,double") (set_attr "fp_int_src" "true")]) (define_insn "*floatdisf2_sse" [(set (match_operand:SF 0 "register_operand" "=x,x") (float:SF (match_operand:DI 1 "nonimmediate_operand" "r,mr")))] "TARGET_64BIT && TARGET_SSE" "cvtsi2ss{q}\t{%1, %0|%0, %1}" [(set_attr "type" "sseicvt") (set_attr "mode" "SF") (set_attr "athlon_decode" "vector,double") (set_attr "fp_int_src" "true")]) ; Avoid possible reformatting penalty on the destination by first ; zeroing it out (define_split [(set (match_operand:SF 0 "register_operand" "") (float:SF (match_operand:DI 1 "nonimmediate_operand" "")))] "TARGET_80387 && reload_completed && TARGET_SSE_PARTIAL_REGS && SSE_REG_P (operands[0])" [(const_int 0)] { rtx dest; dest = simplify_gen_subreg (V4SFmode, operands[0], SFmode, 0); emit_insn (gen_sse_clrv4sf (dest, CONST0_RTX (V4SFmode))); emit_insn (gen_cvtsi2ssq (dest, dest, operands[1])); DONE; }) (define_expand "floathidf2" [(set (match_operand:DF 0 "register_operand" "") (float:DF (match_operand:HI 1 "nonimmediate_operand" "")))] "TARGET_SSE2 || TARGET_80387" { if (TARGET_SSE && TARGET_SSE_MATH) { emit_insn (gen_floatsidf2 (operands[0], convert_to_mode (SImode, operands[1], 0))); DONE; } }) (define_insn "*floathidf2_1" [(set (match_operand:DF 0 "register_operand" "=f,f") (float:DF (match_operand:HI 1 "nonimmediate_operand" "m,r")))] "TARGET_80387 && (!TARGET_SSE2 || !TARGET_SSE_MATH)" "@ fild%z1\t%1 #" [(set_attr "type" "fmov,multi") (set_attr "mode" "DF") (set_attr "fp_int_src" "true")]) (define_expand "floatsidf2" [(set (match_operand:DF 0 "register_operand" "") (float:DF (match_operand:SI 1 "nonimmediate_operand" "")))] "TARGET_80387 || TARGET_SSE2" "") (define_insn "*floatsidf2_i387" [(set (match_operand:DF 0 "register_operand" "=f#Y,?f#Y,Y#f,Y#f") (float:DF (match_operand:SI 1 "nonimmediate_operand" "m,r,r,mr")))] "TARGET_80387 && (!TARGET_SSE2 || TARGET_MIX_SSE_I387)" "@ fild%z1\t%1 # cvtsi2sd\t{%1, %0|%0, %1} cvtsi2sd\t{%1, %0|%0, %1}" [(set_attr "type" "fmov,multi,sseicvt,sseicvt") (set_attr "mode" "DF") (set_attr "athlon_decode" "*,*,double,direct") (set_attr "fp_int_src" "true")]) (define_insn "*floatsidf2_sse" [(set (match_operand:DF 0 "register_operand" "=Y,Y") (float:DF (match_operand:SI 1 "nonimmediate_operand" "r,mr")))] "TARGET_SSE2" "cvtsi2sd\t{%1, %0|%0, %1}" [(set_attr "type" "sseicvt") (set_attr "mode" "DF") (set_attr "athlon_decode" "double,direct") (set_attr "fp_int_src" "true")]) (define_expand "floatdidf2" [(set (match_operand:DF 0 "register_operand" "") (float:DF (match_operand:DI 1 "nonimmediate_operand" "")))] "(TARGET_64BIT && TARGET_SSE2) || TARGET_80387" "") (define_insn "*floatdidf2_i387_only" [(set (match_operand:DF 0 "register_operand" "=f,?f") (float:DF (match_operand:DI 1 "nonimmediate_operand" "m,r")))] "TARGET_80387 && (!TARGET_SSE2 || !TARGET_64BIT)" "@ fild%z1\t%1 #" [(set_attr "type" "fmov,multi") (set_attr "mode" "DF") (set_attr "fp_int_src" "true")]) (define_insn "*floatdidf2_i387" [(set (match_operand:DF 0 "register_operand" "=f#Y,?f#Y,Y#f,Y#f") (float:DF (match_operand:DI 1 "nonimmediate_operand" "m,r,r,mr")))] "TARGET_64BIT && TARGET_80387 && (!TARGET_SSE2 || TARGET_MIX_SSE_I387)" "@ fild%z1\t%1 # cvtsi2sd{q}\t{%1, %0|%0, %1} cvtsi2sd{q}\t{%1, %0|%0, %1}" [(set_attr "type" "fmov,multi,sseicvt,sseicvt") (set_attr "mode" "DF") (set_attr "athlon_decode" "*,*,double,direct") (set_attr "fp_int_src" "true")]) (define_insn "*floatdidf2_sse" [(set (match_operand:DF 0 "register_operand" "=Y,Y") (float:DF (match_operand:DI 1 "nonimmediate_operand" "r,mr")))] "TARGET_SSE2" "cvtsi2sd{q}\t{%1, %0|%0, %1}" [(set_attr "type" "sseicvt") (set_attr "mode" "DF") (set_attr "athlon_decode" "double,direct") (set_attr "fp_int_src" "true")]) (define_insn "floathixf2" [(set (match_operand:XF 0 "register_operand" "=f,f") (float:XF (match_operand:HI 1 "nonimmediate_operand" "m,r")))] "TARGET_80387" "@ fild%z1\t%1 #" [(set_attr "type" "fmov,multi") (set_attr "mode" "XF") (set_attr "fp_int_src" "true")]) (define_insn "floatsixf2" [(set (match_operand:XF 0 "register_operand" "=f,f") (float:XF (match_operand:SI 1 "nonimmediate_operand" "m,r")))] "TARGET_80387" "@ fild%z1\t%1 #" [(set_attr "type" "fmov,multi") (set_attr "mode" "XF") (set_attr "fp_int_src" "true")]) (define_insn "floatdixf2" [(set (match_operand:XF 0 "register_operand" "=f,f") (float:XF (match_operand:DI 1 "nonimmediate_operand" "m,r")))] "TARGET_80387" "@ fild%z1\t%1 #" [(set_attr "type" "fmov,multi") (set_attr "mode" "XF") (set_attr "fp_int_src" "true")]) ;; %%% Kill these when reload knows how to do it. (define_split [(set (match_operand 0 "fp_register_operand" "") (float (match_operand 1 "register_operand" "")))] "reload_completed && FLOAT_MODE_P (GET_MODE (operands[0]))" [(const_int 0)] { operands[2] = ix86_force_to_memory (GET_MODE (operands[1]), operands[1]); operands[2] = gen_rtx_FLOAT (GET_MODE (operands[0]), operands[2]); emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[2])); ix86_free_from_memory (GET_MODE (operands[1])); DONE; }) (define_expand "floatunssisf2" [(use (match_operand:SF 0 "register_operand" "")) (use (match_operand:SI 1 "register_operand" ""))] "TARGET_SSE && TARGET_SSE_MATH && !TARGET_64BIT" "x86_emit_floatuns (operands); DONE;") (define_expand "floatunsdisf2" [(use (match_operand:SF 0 "register_operand" "")) (use (match_operand:DI 1 "register_operand" ""))] "TARGET_SSE && TARGET_SSE_MATH && TARGET_64BIT" "x86_emit_floatuns (operands); DONE;") (define_expand "floatunsdidf2" [(use (match_operand:DF 0 "register_operand" "")) (use (match_operand:DI 1 "register_operand" ""))] "TARGET_SSE2 && TARGET_SSE_MATH && TARGET_64BIT" "x86_emit_floatuns (operands); DONE;") ;; SSE extract/set expanders (define_expand "vec_setv2df" [(match_operand:V2DF 0 "register_operand" "") (match_operand:DF 1 "register_operand" "") (match_operand 2 "const_int_operand" "")] "TARGET_SSE2" { switch (INTVAL (operands[2])) { case 0: emit_insn (gen_sse2_movsd (operands[0], operands[0], simplify_gen_subreg (V2DFmode, operands[1], DFmode, 0))); break; case 1: { rtx op1 = simplify_gen_subreg (V2DFmode, operands[1], DFmode, 0); emit_insn (gen_sse2_unpcklpd (operands[0], operands[0], op1)); } break; default: abort (); } DONE; }) (define_expand "vec_extractv2df" [(match_operand:DF 0 "register_operand" "") (match_operand:V2DF 1 "register_operand" "") (match_operand 2 "const_int_operand" "")] "TARGET_SSE2" { switch (INTVAL (operands[2])) { case 0: emit_move_insn (operands[0], gen_lowpart (DFmode, operands[1])); break; case 1: { rtx dest = simplify_gen_subreg (V2DFmode, operands[0], DFmode, 0); emit_insn (gen_sse2_unpckhpd (dest, operands[1], operands[1])); } break; default: abort (); } DONE; }) (define_expand "vec_initv2df" [(match_operand:V2DF 0 "register_operand" "") (match_operand 1 "" "")] "TARGET_SSE2" { ix86_expand_vector_init (operands[0], operands[1]); DONE; }) (define_expand "vec_setv4sf" [(match_operand:V4SF 0 "register_operand" "") (match_operand:SF 1 "register_operand" "") (match_operand 2 "const_int_operand" "")] "TARGET_SSE" { switch (INTVAL (operands[2])) { case 0: emit_insn (gen_sse_movss (operands[0], operands[0], simplify_gen_subreg (V4SFmode, operands[1], SFmode, 0))); break; case 1: { rtx op1 = simplify_gen_subreg (V4SFmode, operands[1], SFmode, 0); rtx tmp = gen_reg_rtx (V4SFmode); emit_move_insn (tmp, operands[0]); emit_insn (gen_sse_unpcklps (operands[0], operands[0], operands[0])); emit_insn (gen_sse_movss (operands[0], operands[0], op1)); emit_insn (gen_sse_shufps (operands[0], operands[0], tmp, GEN_INT (1 + (0<<2) + (2<<4) + (3<<6)))); } case 2: { rtx op1 = simplify_gen_subreg (V4SFmode, operands[1], SFmode, 0); rtx tmp = gen_reg_rtx (V4SFmode); emit_move_insn (tmp, operands[0]); emit_insn (gen_sse_movss (tmp, tmp, op1)); emit_insn (gen_sse_shufps (operands[0], operands[0], tmp, GEN_INT (0 + (1<<2) + (0<<4) + (3<<6)))); } break; case 3: { rtx op1 = simplify_gen_subreg (V4SFmode, operands[1], SFmode, 0); rtx tmp = gen_reg_rtx (V4SFmode); emit_move_insn (tmp, operands[0]); emit_insn (gen_sse_movss (tmp, tmp, op1)); emit_insn (gen_sse_shufps (operands[0], operands[0], tmp, GEN_INT (0 + (1<<2) + (2<<4) + (0<<6)))); } break; default: abort (); } DONE; }) (define_expand "vec_extractv4sf" [(match_operand:SF 0 "register_operand" "") (match_operand:V4SF 1 "register_operand" "") (match_operand 2 "const_int_operand" "")] "TARGET_SSE" { switch (INTVAL (operands[2])) { case 0: emit_move_insn (operands[0], gen_lowpart (SFmode, operands[1])); break; case 1: { rtx op0 = simplify_gen_subreg (V4SFmode, operands[1], SFmode, 0); rtx tmp = gen_reg_rtx (V4SFmode); emit_move_insn (tmp, operands[1]); emit_insn (gen_sse_shufps (op0, tmp, tmp, GEN_INT (1))); } case 2: { rtx op0 = simplify_gen_subreg (V4SFmode, operands[1], SFmode, 0); rtx tmp = gen_reg_rtx (V4SFmode); emit_move_insn (tmp, operands[1]); emit_insn (gen_sse_unpckhps (op0, tmp, tmp)); } case 3: { rtx op0 = simplify_gen_subreg (V4SFmode, operands[1], SFmode, 0); rtx tmp = gen_reg_rtx (V4SFmode); emit_move_insn (tmp, operands[1]); emit_insn (gen_sse_shufps (op0, tmp, tmp, GEN_INT (3))); } default: abort (); } DONE; }) (define_expand "vec_initv4sf" [(match_operand:V4SF 0 "register_operand" "") (match_operand 1 "" "")] "TARGET_SSE" { ix86_expand_vector_init (operands[0], operands[1]); DONE; }) ;; Add instructions ;; %%% splits for addsidi3 ; [(set (match_operand:DI 0 "nonimmediate_operand" "") ; (plus:DI (match_operand:DI 1 "general_operand" "") ; (zero_extend:DI (match_operand:SI 2 "general_operand" ""))))] (define_expand "adddi3" [(set (match_operand:DI 0 "nonimmediate_operand" "") (plus:DI (match_operand:DI 1 "nonimmediate_operand" "") (match_operand:DI 2 "x86_64_general_operand" ""))) (clobber (reg:CC 17))] "" "ix86_expand_binary_operator (PLUS, DImode, operands); DONE;") (define_insn "*adddi3_1" [(set (match_operand:DI 0 "nonimmediate_operand" "=r,o") (plus:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") (match_operand:DI 2 "general_operand" "roiF,riF"))) (clobber (reg:CC 17))] "!TARGET_64BIT && ix86_binary_operator_ok (PLUS, DImode, operands)" "#") (define_split [(set (match_operand:DI 0 "nonimmediate_operand" "") (plus:DI (match_operand:DI 1 "nonimmediate_operand" "") (match_operand:DI 2 "general_operand" ""))) (clobber (reg:CC 17))] "!TARGET_64BIT && reload_completed" [(parallel [(set (reg:CC 17) (unspec:CC [(match_dup 1) (match_dup 2)] UNSPEC_ADD_CARRY)) (set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2)))]) (parallel [(set (match_dup 3) (plus:SI (plus:SI (ltu:SI (reg:CC 17) (const_int 0)) (match_dup 4)) (match_dup 5))) (clobber (reg:CC 17))])] "split_di (operands+0, 1, operands+0, operands+3); split_di (operands+1, 1, operands+1, operands+4); split_di (operands+2, 1, operands+2, operands+5);") (define_insn "adddi3_carry_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=rm,r") (plus:DI (plus:DI (match_operand:DI 3 "ix86_carry_flag_operator" "") (match_operand:DI 1 "nonimmediate_operand" "%0,0")) (match_operand:DI 2 "x86_64_general_operand" "re,rm"))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (PLUS, DImode, operands)" "adc{q}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "pent_pair" "pu") (set_attr "mode" "DI") (set_attr "ppro_uops" "few")]) (define_insn "*adddi3_cc_rex64" [(set (reg:CC 17) (unspec:CC [(match_operand:DI 1 "nonimmediate_operand" "%0,0") (match_operand:DI 2 "x86_64_general_operand" "re,rm")] UNSPEC_ADD_CARRY)) (set (match_operand:DI 0 "nonimmediate_operand" "=rm,r") (plus:DI (match_dup 1) (match_dup 2)))] "TARGET_64BIT && ix86_binary_operator_ok (PLUS, DImode, operands)" "add{q}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "DI")]) (define_insn "addqi3_carry" [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,q") (plus:QI (plus:QI (match_operand:QI 3 "ix86_carry_flag_operator" "") (match_operand:QI 1 "nonimmediate_operand" "%0,0")) (match_operand:QI 2 "general_operand" "qi,qm"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (PLUS, QImode, operands)" "adc{b}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "pent_pair" "pu") (set_attr "mode" "QI") (set_attr "ppro_uops" "few")]) (define_insn "addhi3_carry" [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,r") (plus:HI (plus:HI (match_operand:HI 3 "ix86_carry_flag_operator" "") (match_operand:HI 1 "nonimmediate_operand" "%0,0")) (match_operand:HI 2 "general_operand" "ri,rm"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (PLUS, HImode, operands)" "adc{w}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "pent_pair" "pu") (set_attr "mode" "HI") (set_attr "ppro_uops" "few")]) (define_insn "addsi3_carry" [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r") (plus:SI (plus:SI (match_operand:SI 3 "ix86_carry_flag_operator" "") (match_operand:SI 1 "nonimmediate_operand" "%0,0")) (match_operand:SI 2 "general_operand" "ri,rm"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (PLUS, SImode, operands)" "adc{l}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "pent_pair" "pu") (set_attr "mode" "SI") (set_attr "ppro_uops" "few")]) (define_insn "*addsi3_carry_zext" [(set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (plus:SI (plus:SI (match_operand:SI 3 "ix86_carry_flag_operator" "") (match_operand:SI 1 "nonimmediate_operand" "%0")) (match_operand:SI 2 "general_operand" "rim")))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (PLUS, SImode, operands)" "adc{l}\t{%2, %k0|%k0, %2}" [(set_attr "type" "alu") (set_attr "pent_pair" "pu") (set_attr "mode" "SI") (set_attr "ppro_uops" "few")]) (define_insn "*addsi3_cc" [(set (reg:CC 17) (unspec:CC [(match_operand:SI 1 "nonimmediate_operand" "%0,0") (match_operand:SI 2 "general_operand" "ri,rm")] UNSPEC_ADD_CARRY)) (set (match_operand:SI 0 "nonimmediate_operand" "=rm,r") (plus:SI (match_dup 1) (match_dup 2)))] "ix86_binary_operator_ok (PLUS, SImode, operands)" "add{l}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) (define_insn "addqi3_cc" [(set (reg:CC 17) (unspec:CC [(match_operand:QI 1 "nonimmediate_operand" "%0,0") (match_operand:QI 2 "general_operand" "qi,qm")] UNSPEC_ADD_CARRY)) (set (match_operand:QI 0 "nonimmediate_operand" "=qm,q") (plus:QI (match_dup 1) (match_dup 2)))] "ix86_binary_operator_ok (PLUS, QImode, operands)" "add{b}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "QI")]) (define_expand "addsi3" [(parallel [(set (match_operand:SI 0 "nonimmediate_operand" "") (plus:SI (match_operand:SI 1 "nonimmediate_operand" "") (match_operand:SI 2 "general_operand" ""))) (clobber (reg:CC 17))])] "" "ix86_expand_binary_operator (PLUS, SImode, operands); DONE;") (define_insn "*lea_1" [(set (match_operand:SI 0 "register_operand" "=r") (match_operand:SI 1 "no_seg_address_operand" "p"))] "!TARGET_64BIT" "lea{l}\t{%a1, %0|%0, %a1}" [(set_attr "type" "lea") (set_attr "mode" "SI")]) (define_insn "*lea_1_rex64" [(set (match_operand:SI 0 "register_operand" "=r") (subreg:SI (match_operand:DI 1 "no_seg_address_operand" "p") 0))] "TARGET_64BIT" "lea{l}\t{%a1, %0|%0, %a1}" [(set_attr "type" "lea") (set_attr "mode" "SI")]) (define_insn "*lea_1_zext" [(set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (subreg:SI (match_operand:DI 1 "no_seg_address_operand" "p") 0)))] "TARGET_64BIT" "lea{l}\t{%a1, %k0|%k0, %a1}" [(set_attr "type" "lea") (set_attr "mode" "SI")]) (define_insn "*lea_2_rex64" [(set (match_operand:DI 0 "register_operand" "=r") (match_operand:DI 1 "no_seg_address_operand" "p"))] "TARGET_64BIT" "lea{q}\t{%a1, %0|%0, %a1}" [(set_attr "type" "lea") (set_attr "mode" "DI")]) ;; The lea patterns for non-Pmodes needs to be matched by several ;; insns converted to real lea by splitters. (define_insn_and_split "*lea_general_1" [(set (match_operand 0 "register_operand" "=r") (plus (plus (match_operand 1 "index_register_operand" "r") (match_operand 2 "register_operand" "r")) (match_operand 3 "immediate_operand" "i")))] "(GET_MODE (operands[0]) == QImode || GET_MODE (operands[0]) == HImode || (TARGET_64BIT && GET_MODE (operands[0]) == SImode)) && (!TARGET_PARTIAL_REG_STALL || optimize_size) && GET_MODE (operands[0]) == GET_MODE (operands[1]) && GET_MODE (operands[0]) == GET_MODE (operands[2]) && (GET_MODE (operands[0]) == GET_MODE (operands[3]) || GET_MODE (operands[3]) == VOIDmode)" "#" "&& reload_completed" [(const_int 0)] { rtx pat; operands[0] = gen_lowpart (SImode, operands[0]); operands[1] = gen_lowpart (Pmode, operands[1]); operands[2] = gen_lowpart (Pmode, operands[2]); operands[3] = gen_lowpart (Pmode, operands[3]); pat = gen_rtx_PLUS (Pmode, gen_rtx_PLUS (Pmode, operands[1], operands[2]), operands[3]); if (Pmode != SImode) pat = gen_rtx_SUBREG (SImode, pat, 0); emit_insn (gen_rtx_SET (VOIDmode, operands[0], pat)); DONE; } [(set_attr "type" "lea") (set_attr "mode" "SI")]) (define_insn_and_split "*lea_general_1_zext" [(set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (plus:SI (plus:SI (match_operand:SI 1 "index_register_operand" "r") (match_operand:SI 2 "register_operand" "r")) (match_operand:SI 3 "immediate_operand" "i"))))] "TARGET_64BIT" "#" "&& reload_completed" [(set (match_dup 0) (zero_extend:DI (subreg:SI (plus:DI (plus:DI (match_dup 1) (match_dup 2)) (match_dup 3)) 0)))] { operands[1] = gen_lowpart (Pmode, operands[1]); operands[2] = gen_lowpart (Pmode, operands[2]); operands[3] = gen_lowpart (Pmode, operands[3]); } [(set_attr "type" "lea") (set_attr "mode" "SI")]) (define_insn_and_split "*lea_general_2" [(set (match_operand 0 "register_operand" "=r") (plus (mult (match_operand 1 "index_register_operand" "r") (match_operand 2 "const248_operand" "i")) (match_operand 3 "nonmemory_operand" "ri")))] "(GET_MODE (operands[0]) == QImode || GET_MODE (operands[0]) == HImode || (TARGET_64BIT && GET_MODE (operands[0]) == SImode)) && (!TARGET_PARTIAL_REG_STALL || optimize_size) && GET_MODE (operands[0]) == GET_MODE (operands[1]) && (GET_MODE (operands[0]) == GET_MODE (operands[3]) || GET_MODE (operands[3]) == VOIDmode)" "#" "&& reload_completed" [(const_int 0)] { rtx pat; operands[0] = gen_lowpart (SImode, operands[0]); operands[1] = gen_lowpart (Pmode, operands[1]); operands[3] = gen_lowpart (Pmode, operands[3]); pat = gen_rtx_PLUS (Pmode, gen_rtx_MULT (Pmode, operands[1], operands[2]), operands[3]); if (Pmode != SImode) pat = gen_rtx_SUBREG (SImode, pat, 0); emit_insn (gen_rtx_SET (VOIDmode, operands[0], pat)); DONE; } [(set_attr "type" "lea") (set_attr "mode" "SI")]) (define_insn_and_split "*lea_general_2_zext" [(set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (plus:SI (mult:SI (match_operand:SI 1 "index_register_operand" "r") (match_operand:SI 2 "const248_operand" "n")) (match_operand:SI 3 "nonmemory_operand" "ri"))))] "TARGET_64BIT" "#" "&& reload_completed" [(set (match_dup 0) (zero_extend:DI (subreg:SI (plus:DI (mult:DI (match_dup 1) (match_dup 2)) (match_dup 3)) 0)))] { operands[1] = gen_lowpart (Pmode, operands[1]); operands[3] = gen_lowpart (Pmode, operands[3]); } [(set_attr "type" "lea") (set_attr "mode" "SI")]) (define_insn_and_split "*lea_general_3" [(set (match_operand 0 "register_operand" "=r") (plus (plus (mult (match_operand 1 "index_register_operand" "r") (match_operand 2 "const248_operand" "i")) (match_operand 3 "register_operand" "r")) (match_operand 4 "immediate_operand" "i")))] "(GET_MODE (operands[0]) == QImode || GET_MODE (operands[0]) == HImode || (TARGET_64BIT && GET_MODE (operands[0]) == SImode)) && (!TARGET_PARTIAL_REG_STALL || optimize_size) && GET_MODE (operands[0]) == GET_MODE (operands[1]) && GET_MODE (operands[0]) == GET_MODE (operands[3])" "#" "&& reload_completed" [(const_int 0)] { rtx pat; operands[0] = gen_lowpart (SImode, operands[0]); operands[1] = gen_lowpart (Pmode, operands[1]); operands[3] = gen_lowpart (Pmode, operands[3]); operands[4] = gen_lowpart (Pmode, operands[4]); pat = gen_rtx_PLUS (Pmode, gen_rtx_PLUS (Pmode, gen_rtx_MULT (Pmode, operands[1], operands[2]), operands[3]), operands[4]); if (Pmode != SImode) pat = gen_rtx_SUBREG (SImode, pat, 0); emit_insn (gen_rtx_SET (VOIDmode, operands[0], pat)); DONE; } [(set_attr "type" "lea") (set_attr "mode" "SI")]) (define_insn_and_split "*lea_general_3_zext" [(set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (plus:SI (plus:SI (mult:SI (match_operand:SI 1 "index_register_operand" "r") (match_operand:SI 2 "const248_operand" "n")) (match_operand:SI 3 "register_operand" "r")) (match_operand:SI 4 "immediate_operand" "i"))))] "TARGET_64BIT" "#" "&& reload_completed" [(set (match_dup 0) (zero_extend:DI (subreg:SI (plus:DI (plus:DI (mult:DI (match_dup 1) (match_dup 2)) (match_dup 3)) (match_dup 4)) 0)))] { operands[1] = gen_lowpart (Pmode, operands[1]); operands[3] = gen_lowpart (Pmode, operands[3]); operands[4] = gen_lowpart (Pmode, operands[4]); } [(set_attr "type" "lea") (set_attr "mode" "SI")]) (define_insn "*adddi_1_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=r,rm,r") (plus:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0,r") (match_operand:DI 2 "x86_64_general_operand" "rme,re,re"))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (PLUS, DImode, operands)" { switch (get_attr_type (insn)) { case TYPE_LEA: operands[2] = SET_SRC (XVECEXP (PATTERN (insn), 0, 0)); return "lea{q}\t{%a2, %0|%0, %a2}"; case TYPE_INCDEC: if (! rtx_equal_p (operands[0], operands[1])) abort (); if (operands[2] == const1_rtx) return "inc{q}\t%0"; else if (operands[2] == constm1_rtx) return "dec{q}\t%0"; else abort (); default: if (! rtx_equal_p (operands[0], operands[1])) abort (); /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if (GET_CODE (operands[2]) == CONST_INT /* Avoid overflows. */ && ((INTVAL (operands[2]) & ((((unsigned int) 1) << 31) - 1))) && (INTVAL (operands[2]) == 128 || (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) != -128))) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{q}\t{%2, %0|%0, %2}"; } return "add{q}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (cond [(eq_attr "alternative" "2") (const_string "lea") ; Current assemblers are broken and do not allow @GOTOFF in ; ought but a memory context. (match_operand:DI 2 "pic_symbolic_operand" "") (const_string "lea") (match_operand:DI 2 "incdec_operand" "") (const_string "incdec") ] (const_string "alu"))) (set_attr "mode" "DI")]) ;; Convert lea to the lea pattern to avoid flags dependency. (define_split [(set (match_operand:DI 0 "register_operand" "") (plus:DI (match_operand:DI 1 "register_operand" "") (match_operand:DI 2 "x86_64_nonmemory_operand" ""))) (clobber (reg:CC 17))] "TARGET_64BIT && reload_completed && true_regnum (operands[0]) != true_regnum (operands[1])" [(set (match_dup 0) (plus:DI (match_dup 1) (match_dup 2)))] "") (define_insn "*adddi_2_rex64" [(set (reg 17) (compare (plus:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") (match_operand:DI 2 "x86_64_general_operand" "rme,re")) (const_int 0))) (set (match_operand:DI 0 "nonimmediate_operand" "=r,rm") (plus:DI (match_dup 1) (match_dup 2)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (PLUS, DImode, operands) /* Current assemblers are broken and do not allow @GOTOFF in ought but a memory context. */ && ! pic_symbolic_operand (operands[2], VOIDmode)" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (! rtx_equal_p (operands[0], operands[1])) abort (); if (operands[2] == const1_rtx) return "inc{q}\t%0"; else if (operands[2] == constm1_rtx) return "dec{q}\t%0"; else abort (); default: if (! rtx_equal_p (operands[0], operands[1])) abort (); /* ???? We ought to handle there the 32bit case too - do we need new constraint? */ /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if (GET_CODE (operands[2]) == CONST_INT /* Avoid overflows. */ && ((INTVAL (operands[2]) & ((((unsigned int) 1) << 31) - 1))) && (INTVAL (operands[2]) == 128 || (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) != -128))) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{q}\t{%2, %0|%0, %2}"; } return "add{q}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:DI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "DI")]) (define_insn "*adddi_3_rex64" [(set (reg 17) (compare (neg:DI (match_operand:DI 2 "x86_64_general_operand" "rme")) (match_operand:DI 1 "x86_64_general_operand" "%0"))) (clobber (match_scratch:DI 0 "=r"))] "TARGET_64BIT && ix86_match_ccmode (insn, CCZmode) && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM) /* Current assemblers are broken and do not allow @GOTOFF in ought but a memory context. */ && ! pic_symbolic_operand (operands[2], VOIDmode)" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (! rtx_equal_p (operands[0], operands[1])) abort (); if (operands[2] == const1_rtx) return "inc{q}\t%0"; else if (operands[2] == constm1_rtx) return "dec{q}\t%0"; else abort (); default: if (! rtx_equal_p (operands[0], operands[1])) abort (); /* ???? We ought to handle there the 32bit case too - do we need new constraint? */ /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if (GET_CODE (operands[2]) == CONST_INT /* Avoid overflows. */ && ((INTVAL (operands[2]) & ((((unsigned int) 1) << 31) - 1))) && (INTVAL (operands[2]) == 128 || (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) != -128))) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{q}\t{%2, %0|%0, %2}"; } return "add{q}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:DI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "DI")]) ; For comparisons against 1, -1 and 128, we may generate better code ; by converting cmp to add, inc or dec as done by peephole2. This pattern ; is matched then. We can't accept general immediate, because for ; case of overflows, the result is messed up. ; This pattern also don't hold of 0x8000000000000000, since the value overflows ; when negated. ; Also carry flag is reversed compared to cmp, so this conversion is valid ; only for comparisons not depending on it. (define_insn "*adddi_4_rex64" [(set (reg 17) (compare (match_operand:DI 1 "nonimmediate_operand" "0") (match_operand:DI 2 "x86_64_immediate_operand" "e"))) (clobber (match_scratch:DI 0 "=rm"))] "TARGET_64BIT && ix86_match_ccmode (insn, CCGCmode)" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (operands[2] == constm1_rtx) return "inc{q}\t%0"; else if (operands[2] == const1_rtx) return "dec{q}\t%0"; else abort(); default: if (! rtx_equal_p (operands[0], operands[1])) abort (); /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if ((INTVAL (operands[2]) == -128 || (INTVAL (operands[2]) > 0 && INTVAL (operands[2]) != 128)) /* Avoid overflows. */ && ((INTVAL (operands[2]) & ((((unsigned int) 1) << 31) - 1)))) return "sub{q}\t{%2, %0|%0, %2}"; operands[2] = GEN_INT (-INTVAL (operands[2])); return "add{q}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:DI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "DI")]) (define_insn "*adddi_5_rex64" [(set (reg 17) (compare (plus:DI (match_operand:DI 1 "nonimmediate_operand" "%0") (match_operand:DI 2 "x86_64_general_operand" "rme")) (const_int 0))) (clobber (match_scratch:DI 0 "=r"))] "TARGET_64BIT && ix86_match_ccmode (insn, CCGOCmode) && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM) /* Current assemblers are broken and do not allow @GOTOFF in ought but a memory context. */ && ! pic_symbolic_operand (operands[2], VOIDmode)" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (! rtx_equal_p (operands[0], operands[1])) abort (); if (operands[2] == const1_rtx) return "inc{q}\t%0"; else if (operands[2] == constm1_rtx) return "dec{q}\t%0"; else abort(); default: if (! rtx_equal_p (operands[0], operands[1])) abort (); /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if (GET_CODE (operands[2]) == CONST_INT /* Avoid overflows. */ && ((INTVAL (operands[2]) & ((((unsigned int) 1) << 31) - 1))) && (INTVAL (operands[2]) == 128 || (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) != -128))) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{q}\t{%2, %0|%0, %2}"; } return "add{q}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:DI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "DI")]) (define_insn "*addsi_1" [(set (match_operand:SI 0 "nonimmediate_operand" "=r,rm,r") (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,r") (match_operand:SI 2 "general_operand" "rmni,rni,rni"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (PLUS, SImode, operands)" { switch (get_attr_type (insn)) { case TYPE_LEA: operands[2] = SET_SRC (XVECEXP (PATTERN (insn), 0, 0)); return "lea{l}\t{%a2, %0|%0, %a2}"; case TYPE_INCDEC: if (! rtx_equal_p (operands[0], operands[1])) abort (); if (operands[2] == const1_rtx) return "inc{l}\t%0"; else if (operands[2] == constm1_rtx) return "dec{l}\t%0"; else abort(); default: if (! rtx_equal_p (operands[0], operands[1])) abort (); /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if (GET_CODE (operands[2]) == CONST_INT && (INTVAL (operands[2]) == 128 || (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) != -128))) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{l}\t{%2, %0|%0, %2}"; } return "add{l}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (cond [(eq_attr "alternative" "2") (const_string "lea") ; Current assemblers are broken and do not allow @GOTOFF in ; ought but a memory context. (match_operand:SI 2 "pic_symbolic_operand" "") (const_string "lea") (match_operand:SI 2 "incdec_operand" "") (const_string "incdec") ] (const_string "alu"))) (set_attr "mode" "SI")]) ;; Convert lea to the lea pattern to avoid flags dependency. (define_split [(set (match_operand 0 "register_operand" "") (plus (match_operand 1 "register_operand" "") (match_operand 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "reload_completed && true_regnum (operands[0]) != true_regnum (operands[1])" [(const_int 0)] { rtx pat; /* In -fPIC mode the constructs like (const (unspec [symbol_ref])) may confuse gen_lowpart. */ if (GET_MODE (operands[0]) != Pmode) { operands[1] = gen_lowpart (Pmode, operands[1]); operands[2] = gen_lowpart (Pmode, operands[2]); } operands[0] = gen_lowpart (SImode, operands[0]); pat = gen_rtx_PLUS (Pmode, operands[1], operands[2]); if (Pmode != SImode) pat = gen_rtx_SUBREG (SImode, pat, 0); emit_insn (gen_rtx_SET (VOIDmode, operands[0], pat)); DONE; }) ;; It may seem that nonimmediate operand is proper one for operand 1. ;; The addsi_1 pattern allows nonimmediate operand at that place and ;; we take care in ix86_binary_operator_ok to not allow two memory ;; operands so proper swapping will be done in reload. This allow ;; patterns constructed from addsi_1 to match. (define_insn "addsi_1_zext" [(set (match_operand:DI 0 "register_operand" "=r,r") (zero_extend:DI (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0,r") (match_operand:SI 2 "general_operand" "rmni,rni")))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (PLUS, SImode, operands)" { switch (get_attr_type (insn)) { case TYPE_LEA: operands[2] = SET_SRC (XVECEXP (PATTERN (insn), 0, 0)); return "lea{l}\t{%a2, %k0|%k0, %a2}"; case TYPE_INCDEC: if (operands[2] == const1_rtx) return "inc{l}\t%k0"; else if (operands[2] == constm1_rtx) return "dec{l}\t%k0"; else abort(); default: /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if (GET_CODE (operands[2]) == CONST_INT && (INTVAL (operands[2]) == 128 || (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) != -128))) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{l}\t{%2, %k0|%k0, %2}"; } return "add{l}\t{%2, %k0|%k0, %2}"; } } [(set (attr "type") (cond [(eq_attr "alternative" "1") (const_string "lea") ; Current assemblers are broken and do not allow @GOTOFF in ; ought but a memory context. (match_operand:SI 2 "pic_symbolic_operand" "") (const_string "lea") (match_operand:SI 2 "incdec_operand" "") (const_string "incdec") ] (const_string "alu"))) (set_attr "mode" "SI")]) ;; Convert lea to the lea pattern to avoid flags dependency. (define_split [(set (match_operand:DI 0 "register_operand" "") (zero_extend:DI (plus:SI (match_operand:SI 1 "register_operand" "") (match_operand:SI 2 "nonmemory_operand" "")))) (clobber (reg:CC 17))] "TARGET_64BIT && reload_completed && true_regnum (operands[0]) != true_regnum (operands[1])" [(set (match_dup 0) (zero_extend:DI (subreg:SI (plus:DI (match_dup 1) (match_dup 2)) 0)))] { operands[1] = gen_lowpart (Pmode, operands[1]); operands[2] = gen_lowpart (Pmode, operands[2]); }) (define_insn "*addsi_2" [(set (reg 17) (compare (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") (match_operand:SI 2 "general_operand" "rmni,rni")) (const_int 0))) (set (match_operand:SI 0 "nonimmediate_operand" "=r,rm") (plus:SI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (PLUS, SImode, operands) /* Current assemblers are broken and do not allow @GOTOFF in ought but a memory context. */ && ! pic_symbolic_operand (operands[2], VOIDmode)" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (! rtx_equal_p (operands[0], operands[1])) abort (); if (operands[2] == const1_rtx) return "inc{l}\t%0"; else if (operands[2] == constm1_rtx) return "dec{l}\t%0"; else abort(); default: if (! rtx_equal_p (operands[0], operands[1])) abort (); /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if (GET_CODE (operands[2]) == CONST_INT && (INTVAL (operands[2]) == 128 || (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) != -128))) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{l}\t{%2, %0|%0, %2}"; } return "add{l}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:SI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "SI")]) ;; See comment for addsi_1_zext why we do use nonimmediate_operand (define_insn "*addsi_2_zext" [(set (reg 17) (compare (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0") (match_operand:SI 2 "general_operand" "rmni")) (const_int 0))) (set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (plus:SI (match_dup 1) (match_dup 2))))] "TARGET_64BIT && ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (PLUS, SImode, operands) /* Current assemblers are broken and do not allow @GOTOFF in ought but a memory context. */ && ! pic_symbolic_operand (operands[2], VOIDmode)" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (operands[2] == const1_rtx) return "inc{l}\t%k0"; else if (operands[2] == constm1_rtx) return "dec{l}\t%k0"; else abort(); default: /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if (GET_CODE (operands[2]) == CONST_INT && (INTVAL (operands[2]) == 128 || (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) != -128))) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{l}\t{%2, %k0|%k0, %2}"; } return "add{l}\t{%2, %k0|%k0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:SI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "SI")]) (define_insn "*addsi_3" [(set (reg 17) (compare (neg:SI (match_operand:SI 2 "general_operand" "rmni")) (match_operand:SI 1 "nonimmediate_operand" "%0"))) (clobber (match_scratch:SI 0 "=r"))] "ix86_match_ccmode (insn, CCZmode) && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM) /* Current assemblers are broken and do not allow @GOTOFF in ought but a memory context. */ && ! pic_symbolic_operand (operands[2], VOIDmode)" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (! rtx_equal_p (operands[0], operands[1])) abort (); if (operands[2] == const1_rtx) return "inc{l}\t%0"; else if (operands[2] == constm1_rtx) return "dec{l}\t%0"; else abort(); default: if (! rtx_equal_p (operands[0], operands[1])) abort (); /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if (GET_CODE (operands[2]) == CONST_INT && (INTVAL (operands[2]) == 128 || (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) != -128))) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{l}\t{%2, %0|%0, %2}"; } return "add{l}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:SI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "SI")]) ;; See comment for addsi_1_zext why we do use nonimmediate_operand (define_insn "*addsi_3_zext" [(set (reg 17) (compare (neg:SI (match_operand:SI 2 "general_operand" "rmni")) (match_operand:SI 1 "nonimmediate_operand" "%0"))) (set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (plus:SI (match_dup 1) (match_dup 2))))] "TARGET_64BIT && ix86_match_ccmode (insn, CCZmode) && ix86_binary_operator_ok (PLUS, SImode, operands) /* Current assemblers are broken and do not allow @GOTOFF in ought but a memory context. */ && ! pic_symbolic_operand (operands[2], VOIDmode)" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (operands[2] == const1_rtx) return "inc{l}\t%k0"; else if (operands[2] == constm1_rtx) return "dec{l}\t%k0"; else abort(); default: /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if (GET_CODE (operands[2]) == CONST_INT && (INTVAL (operands[2]) == 128 || (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) != -128))) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{l}\t{%2, %k0|%k0, %2}"; } return "add{l}\t{%2, %k0|%k0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:SI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "SI")]) ; For comparisons against 1, -1 and 128, we may generate better code ; by converting cmp to add, inc or dec as done by peephole2. This pattern ; is matched then. We can't accept general immediate, because for ; case of overflows, the result is messed up. ; This pattern also don't hold of 0x80000000, since the value overflows ; when negated. ; Also carry flag is reversed compared to cmp, so this conversion is valid ; only for comparisons not depending on it. (define_insn "*addsi_4" [(set (reg 17) (compare (match_operand:SI 1 "nonimmediate_operand" "0") (match_operand:SI 2 "const_int_operand" "n"))) (clobber (match_scratch:SI 0 "=rm"))] "ix86_match_ccmode (insn, CCGCmode) && (INTVAL (operands[2]) & 0xffffffff) != 0x80000000" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (operands[2] == constm1_rtx) return "inc{l}\t%0"; else if (operands[2] == const1_rtx) return "dec{l}\t%0"; else abort(); default: if (! rtx_equal_p (operands[0], operands[1])) abort (); /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if ((INTVAL (operands[2]) == -128 || (INTVAL (operands[2]) > 0 && INTVAL (operands[2]) != 128))) return "sub{l}\t{%2, %0|%0, %2}"; operands[2] = GEN_INT (-INTVAL (operands[2])); return "add{l}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:SI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "SI")]) (define_insn "*addsi_5" [(set (reg 17) (compare (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0") (match_operand:SI 2 "general_operand" "rmni")) (const_int 0))) (clobber (match_scratch:SI 0 "=r"))] "ix86_match_ccmode (insn, CCGOCmode) && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM) /* Current assemblers are broken and do not allow @GOTOFF in ought but a memory context. */ && ! pic_symbolic_operand (operands[2], VOIDmode)" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (! rtx_equal_p (operands[0], operands[1])) abort (); if (operands[2] == const1_rtx) return "inc{l}\t%0"; else if (operands[2] == constm1_rtx) return "dec{l}\t%0"; else abort(); default: if (! rtx_equal_p (operands[0], operands[1])) abort (); /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if (GET_CODE (operands[2]) == CONST_INT && (INTVAL (operands[2]) == 128 || (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) != -128))) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{l}\t{%2, %0|%0, %2}"; } return "add{l}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:SI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "SI")]) (define_expand "addhi3" [(parallel [(set (match_operand:HI 0 "nonimmediate_operand" "") (plus:HI (match_operand:HI 1 "nonimmediate_operand" "") (match_operand:HI 2 "general_operand" ""))) (clobber (reg:CC 17))])] "TARGET_HIMODE_MATH" "ix86_expand_binary_operator (PLUS, HImode, operands); DONE;") ;; %%% After Dave's SUBREG_BYTE stuff goes in, re-enable incb %ah ;; type optimizations enabled by define-splits. This is not important ;; for PII, and in fact harmful because of partial register stalls. (define_insn "*addhi_1_lea" [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,r,r") (plus:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0,r") (match_operand:HI 2 "general_operand" "ri,rm,rni"))) (clobber (reg:CC 17))] "!TARGET_PARTIAL_REG_STALL && ix86_binary_operator_ok (PLUS, HImode, operands)" { switch (get_attr_type (insn)) { case TYPE_LEA: return "#"; case TYPE_INCDEC: if (operands[2] == const1_rtx) return "inc{w}\t%0"; else if (operands[2] == constm1_rtx) return "dec{w}\t%0"; abort(); default: /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if (GET_CODE (operands[2]) == CONST_INT && (INTVAL (operands[2]) == 128 || (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) != -128))) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{w}\t{%2, %0|%0, %2}"; } return "add{w}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (if_then_else (eq_attr "alternative" "2") (const_string "lea") (if_then_else (match_operand:HI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu")))) (set_attr "mode" "HI,HI,SI")]) (define_insn "*addhi_1" [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,r") (plus:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0") (match_operand:HI 2 "general_operand" "ri,rm"))) (clobber (reg:CC 17))] "TARGET_PARTIAL_REG_STALL && ix86_binary_operator_ok (PLUS, HImode, operands)" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (operands[2] == const1_rtx) return "inc{w}\t%0"; else if (operands[2] == constm1_rtx) return "dec{w}\t%0"; abort(); default: /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if (GET_CODE (operands[2]) == CONST_INT && (INTVAL (operands[2]) == 128 || (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) != -128))) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{w}\t{%2, %0|%0, %2}"; } return "add{w}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:HI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "HI")]) (define_insn "*addhi_2" [(set (reg 17) (compare (plus:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0") (match_operand:HI 2 "general_operand" "rmni,rni")) (const_int 0))) (set (match_operand:HI 0 "nonimmediate_operand" "=r,rm") (plus:HI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (PLUS, HImode, operands)" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (operands[2] == const1_rtx) return "inc{w}\t%0"; else if (operands[2] == constm1_rtx) return "dec{w}\t%0"; abort(); default: /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if (GET_CODE (operands[2]) == CONST_INT && (INTVAL (operands[2]) == 128 || (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) != -128))) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{w}\t{%2, %0|%0, %2}"; } return "add{w}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:HI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "HI")]) (define_insn "*addhi_3" [(set (reg 17) (compare (neg:HI (match_operand:HI 2 "general_operand" "rmni")) (match_operand:HI 1 "nonimmediate_operand" "%0"))) (clobber (match_scratch:HI 0 "=r"))] "ix86_match_ccmode (insn, CCZmode) && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (operands[2] == const1_rtx) return "inc{w}\t%0"; else if (operands[2] == constm1_rtx) return "dec{w}\t%0"; abort(); default: /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if (GET_CODE (operands[2]) == CONST_INT && (INTVAL (operands[2]) == 128 || (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) != -128))) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{w}\t{%2, %0|%0, %2}"; } return "add{w}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:HI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "HI")]) ; See comments above addsi_3_imm for details. (define_insn "*addhi_4" [(set (reg 17) (compare (match_operand:HI 1 "nonimmediate_operand" "0") (match_operand:HI 2 "const_int_operand" "n"))) (clobber (match_scratch:HI 0 "=rm"))] "ix86_match_ccmode (insn, CCGCmode) && (INTVAL (operands[2]) & 0xffff) != 0x8000" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (operands[2] == constm1_rtx) return "inc{w}\t%0"; else if (operands[2] == const1_rtx) return "dec{w}\t%0"; else abort(); default: if (! rtx_equal_p (operands[0], operands[1])) abort (); /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if ((INTVAL (operands[2]) == -128 || (INTVAL (operands[2]) > 0 && INTVAL (operands[2]) != 128))) return "sub{w}\t{%2, %0|%0, %2}"; operands[2] = GEN_INT (-INTVAL (operands[2])); return "add{w}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:HI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "SI")]) (define_insn "*addhi_5" [(set (reg 17) (compare (plus:HI (match_operand:HI 1 "nonimmediate_operand" "%0") (match_operand:HI 2 "general_operand" "rmni")) (const_int 0))) (clobber (match_scratch:HI 0 "=r"))] "ix86_match_ccmode (insn, CCGOCmode) && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (operands[2] == const1_rtx) return "inc{w}\t%0"; else if (operands[2] == constm1_rtx) return "dec{w}\t%0"; abort(); default: /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if (GET_CODE (operands[2]) == CONST_INT && (INTVAL (operands[2]) == 128 || (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) != -128))) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{w}\t{%2, %0|%0, %2}"; } return "add{w}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:HI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "HI")]) (define_expand "addqi3" [(parallel [(set (match_operand:QI 0 "nonimmediate_operand" "") (plus:QI (match_operand:QI 1 "nonimmediate_operand" "") (match_operand:QI 2 "general_operand" ""))) (clobber (reg:CC 17))])] "TARGET_QIMODE_MATH" "ix86_expand_binary_operator (PLUS, QImode, operands); DONE;") ;; %%% Potential partial reg stall on alternative 2. What to do? (define_insn "*addqi_1_lea" [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,q,r,r") (plus:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0,r") (match_operand:QI 2 "general_operand" "qn,qmn,rn,rn"))) (clobber (reg:CC 17))] "!TARGET_PARTIAL_REG_STALL && ix86_binary_operator_ok (PLUS, QImode, operands)" { int widen = (which_alternative == 2); switch (get_attr_type (insn)) { case TYPE_LEA: return "#"; case TYPE_INCDEC: if (operands[2] == const1_rtx) return widen ? "inc{l}\t%k0" : "inc{b}\t%0"; else if (operands[2] == constm1_rtx) return widen ? "dec{l}\t%k0" : "dec{b}\t%0"; abort(); default: /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if (GET_CODE (operands[2]) == CONST_INT && (INTVAL (operands[2]) == 128 || (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) != -128))) { operands[2] = GEN_INT (-INTVAL (operands[2])); if (widen) return "sub{l}\t{%2, %k0|%k0, %2}"; else return "sub{b}\t{%2, %0|%0, %2}"; } if (widen) return "add{l}\t{%k2, %k0|%k0, %k2}"; else return "add{b}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (if_then_else (eq_attr "alternative" "3") (const_string "lea") (if_then_else (match_operand:QI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu")))) (set_attr "mode" "QI,QI,SI,SI")]) (define_insn "*addqi_1" [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,q,r") (plus:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0") (match_operand:QI 2 "general_operand" "qn,qmn,rn"))) (clobber (reg:CC 17))] "TARGET_PARTIAL_REG_STALL && ix86_binary_operator_ok (PLUS, QImode, operands)" { int widen = (which_alternative == 2); switch (get_attr_type (insn)) { case TYPE_INCDEC: if (operands[2] == const1_rtx) return widen ? "inc{l}\t%k0" : "inc{b}\t%0"; else if (operands[2] == constm1_rtx) return widen ? "dec{l}\t%k0" : "dec{b}\t%0"; abort(); default: /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. Exceptions: -128 encodes smaller than 128, so swap sign and op. */ if (GET_CODE (operands[2]) == CONST_INT && (INTVAL (operands[2]) == 128 || (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) != -128))) { operands[2] = GEN_INT (-INTVAL (operands[2])); if (widen) return "sub{l}\t{%2, %k0|%k0, %2}"; else return "sub{b}\t{%2, %0|%0, %2}"; } if (widen) return "add{l}\t{%k2, %k0|%k0, %k2}"; else return "add{b}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:QI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "QI,QI,SI")]) (define_insn "*addqi_1_slp" [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "+qm,q")) (plus:QI (match_dup 0) (match_operand:QI 1 "general_operand" "qn,qnm"))) (clobber (reg:CC 17))] "(! TARGET_PARTIAL_REG_STALL || optimize_size) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (operands[1] == const1_rtx) return "inc{b}\t%0"; else if (operands[1] == constm1_rtx) return "dec{b}\t%0"; abort(); default: /* Make things pretty and `subl $4,%eax' rather than `addl $-4, %eax'. */ if (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) < 0) { operands[1] = GEN_INT (-INTVAL (operands[1])); return "sub{b}\t{%1, %0|%0, %1}"; } return "add{b}\t{%1, %0|%0, %1}"; } } [(set (attr "type") (if_then_else (match_operand:QI 1 "incdec_operand" "") (const_string "incdec") (const_string "alu1"))) (set (attr "memory") (if_then_else (match_operand 1 "memory_operand" "") (const_string "load") (const_string "none"))) (set_attr "mode" "QI")]) (define_insn "*addqi_2" [(set (reg 17) (compare (plus:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0") (match_operand:QI 2 "general_operand" "qmni,qni")) (const_int 0))) (set (match_operand:QI 0 "nonimmediate_operand" "=q,qm") (plus:QI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (PLUS, QImode, operands)" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (operands[2] == const1_rtx) return "inc{b}\t%0"; else if (operands[2] == constm1_rtx || (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 255)) return "dec{b}\t%0"; abort(); default: /* Make things pretty and `subb $4,%al' rather than `addb $-4, %al'. */ if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{b}\t{%2, %0|%0, %2}"; } return "add{b}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:QI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "QI")]) (define_insn "*addqi_3" [(set (reg 17) (compare (neg:QI (match_operand:QI 2 "general_operand" "qmni")) (match_operand:QI 1 "nonimmediate_operand" "%0"))) (clobber (match_scratch:QI 0 "=q"))] "ix86_match_ccmode (insn, CCZmode) && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (operands[2] == const1_rtx) return "inc{b}\t%0"; else if (operands[2] == constm1_rtx || (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 255)) return "dec{b}\t%0"; abort(); default: /* Make things pretty and `subb $4,%al' rather than `addb $-4, %al'. */ if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{b}\t{%2, %0|%0, %2}"; } return "add{b}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:QI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "QI")]) ; See comments above addsi_3_imm for details. (define_insn "*addqi_4" [(set (reg 17) (compare (match_operand:QI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const_int_operand" "n"))) (clobber (match_scratch:QI 0 "=qm"))] "ix86_match_ccmode (insn, CCGCmode) && (INTVAL (operands[2]) & 0xff) != 0x80" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (operands[2] == constm1_rtx || (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 255)) return "inc{b}\t%0"; else if (operands[2] == const1_rtx) return "dec{b}\t%0"; else abort(); default: if (! rtx_equal_p (operands[0], operands[1])) abort (); if (INTVAL (operands[2]) < 0) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "add{b}\t{%2, %0|%0, %2}"; } return "sub{b}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:HI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "QI")]) (define_insn "*addqi_5" [(set (reg 17) (compare (plus:QI (match_operand:QI 1 "nonimmediate_operand" "%0") (match_operand:QI 2 "general_operand" "qmni")) (const_int 0))) (clobber (match_scratch:QI 0 "=q"))] "ix86_match_ccmode (insn, CCGOCmode) && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (operands[2] == const1_rtx) return "inc{b}\t%0"; else if (operands[2] == constm1_rtx || (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 255)) return "dec{b}\t%0"; abort(); default: /* Make things pretty and `subb $4,%al' rather than `addb $-4, %al'. */ if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{b}\t{%2, %0|%0, %2}"; } return "add{b}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:QI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "QI")]) (define_insn "addqi_ext_1" [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "=Q") (const_int 8) (const_int 8)) (plus:SI (zero_extract:SI (match_operand 1 "ext_register_operand" "0") (const_int 8) (const_int 8)) (match_operand:QI 2 "general_operand" "Qmn"))) (clobber (reg:CC 17))] "!TARGET_64BIT" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (operands[2] == const1_rtx) return "inc{b}\t%h0"; else if (operands[2] == constm1_rtx || (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 255)) return "dec{b}\t%h0"; abort(); default: return "add{b}\t{%2, %h0|%h0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:QI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "QI")]) (define_insn "*addqi_ext_1_rex64" [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "=Q") (const_int 8) (const_int 8)) (plus:SI (zero_extract:SI (match_operand 1 "ext_register_operand" "0") (const_int 8) (const_int 8)) (match_operand:QI 2 "nonmemory_operand" "Qn"))) (clobber (reg:CC 17))] "TARGET_64BIT" { switch (get_attr_type (insn)) { case TYPE_INCDEC: if (operands[2] == const1_rtx) return "inc{b}\t%h0"; else if (operands[2] == constm1_rtx || (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 255)) return "dec{b}\t%h0"; abort(); default: return "add{b}\t{%2, %h0|%h0, %2}"; } } [(set (attr "type") (if_then_else (match_operand:QI 2 "incdec_operand" "") (const_string "incdec") (const_string "alu"))) (set_attr "mode" "QI")]) (define_insn "*addqi_ext_2" [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "=Q") (const_int 8) (const_int 8)) (plus:SI (zero_extract:SI (match_operand 1 "ext_register_operand" "%0") (const_int 8) (const_int 8)) (zero_extract:SI (match_operand 2 "ext_register_operand" "Q") (const_int 8) (const_int 8)))) (clobber (reg:CC 17))] "" "add{b}\t{%h2, %h0|%h0, %h2}" [(set_attr "type" "alu") (set_attr "mode" "QI")]) ;; The patterns that match these are at the end of this file. (define_expand "addxf3" [(set (match_operand:XF 0 "register_operand" "") (plus:XF (match_operand:XF 1 "register_operand" "") (match_operand:XF 2 "register_operand" "")))] "TARGET_80387" "") (define_expand "adddf3" [(set (match_operand:DF 0 "register_operand" "") (plus:DF (match_operand:DF 1 "register_operand" "") (match_operand:DF 2 "nonimmediate_operand" "")))] "TARGET_80387 || (TARGET_SSE2 && TARGET_SSE_MATH)" "") (define_expand "addsf3" [(set (match_operand:SF 0 "register_operand" "") (plus:SF (match_operand:SF 1 "register_operand" "") (match_operand:SF 2 "nonimmediate_operand" "")))] "TARGET_80387 || TARGET_SSE_MATH" "") ;; Subtract instructions ;; %%% splits for subsidi3 (define_expand "subdi3" [(parallel [(set (match_operand:DI 0 "nonimmediate_operand" "") (minus:DI (match_operand:DI 1 "nonimmediate_operand" "") (match_operand:DI 2 "x86_64_general_operand" ""))) (clobber (reg:CC 17))])] "" "ix86_expand_binary_operator (MINUS, DImode, operands); DONE;") (define_insn "*subdi3_1" [(set (match_operand:DI 0 "nonimmediate_operand" "=r,o") (minus:DI (match_operand:DI 1 "nonimmediate_operand" "0,0") (match_operand:DI 2 "general_operand" "roiF,riF"))) (clobber (reg:CC 17))] "!TARGET_64BIT && ix86_binary_operator_ok (MINUS, DImode, operands)" "#") (define_split [(set (match_operand:DI 0 "nonimmediate_operand" "") (minus:DI (match_operand:DI 1 "nonimmediate_operand" "") (match_operand:DI 2 "general_operand" ""))) (clobber (reg:CC 17))] "!TARGET_64BIT && reload_completed" [(parallel [(set (reg:CC 17) (compare:CC (match_dup 1) (match_dup 2))) (set (match_dup 0) (minus:SI (match_dup 1) (match_dup 2)))]) (parallel [(set (match_dup 3) (minus:SI (match_dup 4) (plus:SI (ltu:SI (reg:CC 17) (const_int 0)) (match_dup 5)))) (clobber (reg:CC 17))])] "split_di (operands+0, 1, operands+0, operands+3); split_di (operands+1, 1, operands+1, operands+4); split_di (operands+2, 1, operands+2, operands+5);") (define_insn "subdi3_carry_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=rm,r") (minus:DI (match_operand:DI 1 "nonimmediate_operand" "0,0") (plus:DI (match_operand:DI 3 "ix86_carry_flag_operator" "") (match_operand:DI 2 "x86_64_general_operand" "re,rm")))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (MINUS, DImode, operands)" "sbb{q}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "pent_pair" "pu") (set_attr "ppro_uops" "few") (set_attr "mode" "DI")]) (define_insn "*subdi_1_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=rm,r") (minus:DI (match_operand:DI 1 "nonimmediate_operand" "0,0") (match_operand:DI 2 "x86_64_general_operand" "re,rm"))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (MINUS, DImode, operands)" "sub{q}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "DI")]) (define_insn "*subdi_2_rex64" [(set (reg 17) (compare (minus:DI (match_operand:DI 1 "nonimmediate_operand" "0,0") (match_operand:DI 2 "x86_64_general_operand" "re,rm")) (const_int 0))) (set (match_operand:DI 0 "nonimmediate_operand" "=rm,r") (minus:DI (match_dup 1) (match_dup 2)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (MINUS, DImode, operands)" "sub{q}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "DI")]) (define_insn "*subdi_3_rex63" [(set (reg 17) (compare (match_operand:DI 1 "nonimmediate_operand" "0,0") (match_operand:DI 2 "x86_64_general_operand" "re,rm"))) (set (match_operand:DI 0 "nonimmediate_operand" "=rm,r") (minus:DI (match_dup 1) (match_dup 2)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCmode) && ix86_binary_operator_ok (MINUS, SImode, operands)" "sub{q}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "DI")]) (define_insn "subqi3_carry" [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,q") (minus:QI (match_operand:QI 1 "nonimmediate_operand" "0,0") (plus:QI (match_operand:QI 3 "ix86_carry_flag_operator" "") (match_operand:QI 2 "general_operand" "qi,qm")))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (MINUS, QImode, operands)" "sbb{b}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "pent_pair" "pu") (set_attr "ppro_uops" "few") (set_attr "mode" "QI")]) (define_insn "subhi3_carry" [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,r") (minus:HI (match_operand:HI 1 "nonimmediate_operand" "0,0") (plus:HI (match_operand:HI 3 "ix86_carry_flag_operator" "") (match_operand:HI 2 "general_operand" "ri,rm")))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (MINUS, HImode, operands)" "sbb{w}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "pent_pair" "pu") (set_attr "ppro_uops" "few") (set_attr "mode" "HI")]) (define_insn "subsi3_carry" [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r") (minus:SI (match_operand:SI 1 "nonimmediate_operand" "0,0") (plus:SI (match_operand:SI 3 "ix86_carry_flag_operator" "") (match_operand:SI 2 "general_operand" "ri,rm")))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (MINUS, SImode, operands)" "sbb{l}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "pent_pair" "pu") (set_attr "ppro_uops" "few") (set_attr "mode" "SI")]) (define_insn "subsi3_carry_zext" [(set (match_operand:DI 0 "register_operand" "=rm,r") (zero_extend:DI (minus:SI (match_operand:SI 1 "register_operand" "0,0") (plus:SI (match_operand:SI 3 "ix86_carry_flag_operator" "") (match_operand:SI 2 "general_operand" "ri,rm"))))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (MINUS, SImode, operands)" "sbb{l}\t{%2, %k0|%k0, %2}" [(set_attr "type" "alu") (set_attr "pent_pair" "pu") (set_attr "ppro_uops" "few") (set_attr "mode" "SI")]) (define_expand "subsi3" [(parallel [(set (match_operand:SI 0 "nonimmediate_operand" "") (minus:SI (match_operand:SI 1 "nonimmediate_operand" "") (match_operand:SI 2 "general_operand" ""))) (clobber (reg:CC 17))])] "" "ix86_expand_binary_operator (MINUS, SImode, operands); DONE;") (define_insn "*subsi_1" [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r") (minus:SI (match_operand:SI 1 "nonimmediate_operand" "0,0") (match_operand:SI 2 "general_operand" "ri,rm"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (MINUS, SImode, operands)" "sub{l}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) (define_insn "*subsi_1_zext" [(set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (minus:SI (match_operand:SI 1 "register_operand" "0") (match_operand:SI 2 "general_operand" "rim")))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (MINUS, SImode, operands)" "sub{l}\t{%2, %k0|%k0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) (define_insn "*subsi_2" [(set (reg 17) (compare (minus:SI (match_operand:SI 1 "nonimmediate_operand" "0,0") (match_operand:SI 2 "general_operand" "ri,rm")) (const_int 0))) (set (match_operand:SI 0 "nonimmediate_operand" "=rm,r") (minus:SI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (MINUS, SImode, operands)" "sub{l}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) (define_insn "*subsi_2_zext" [(set (reg 17) (compare (minus:SI (match_operand:SI 1 "register_operand" "0") (match_operand:SI 2 "general_operand" "rim")) (const_int 0))) (set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (minus:SI (match_dup 1) (match_dup 2))))] "TARGET_64BIT && ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (MINUS, SImode, operands)" "sub{l}\t{%2, %k0|%k0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) (define_insn "*subsi_3" [(set (reg 17) (compare (match_operand:SI 1 "nonimmediate_operand" "0,0") (match_operand:SI 2 "general_operand" "ri,rm"))) (set (match_operand:SI 0 "nonimmediate_operand" "=rm,r") (minus:SI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCmode) && ix86_binary_operator_ok (MINUS, SImode, operands)" "sub{l}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) (define_insn "*subsi_3_zext" [(set (reg 17) (compare (match_operand:SI 1 "register_operand" "0") (match_operand:SI 2 "general_operand" "rim"))) (set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (minus:SI (match_dup 1) (match_dup 2))))] "TARGET_64BIT && ix86_match_ccmode (insn, CCmode) && ix86_binary_operator_ok (MINUS, SImode, operands)" "sub{q}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "DI")]) (define_expand "subhi3" [(parallel [(set (match_operand:HI 0 "nonimmediate_operand" "") (minus:HI (match_operand:HI 1 "nonimmediate_operand" "") (match_operand:HI 2 "general_operand" ""))) (clobber (reg:CC 17))])] "TARGET_HIMODE_MATH" "ix86_expand_binary_operator (MINUS, HImode, operands); DONE;") (define_insn "*subhi_1" [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,r") (minus:HI (match_operand:HI 1 "nonimmediate_operand" "0,0") (match_operand:HI 2 "general_operand" "ri,rm"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (MINUS, HImode, operands)" "sub{w}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "HI")]) (define_insn "*subhi_2" [(set (reg 17) (compare (minus:HI (match_operand:HI 1 "nonimmediate_operand" "0,0") (match_operand:HI 2 "general_operand" "ri,rm")) (const_int 0))) (set (match_operand:HI 0 "nonimmediate_operand" "=rm,r") (minus:HI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (MINUS, HImode, operands)" "sub{w}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "HI")]) (define_insn "*subhi_3" [(set (reg 17) (compare (match_operand:HI 1 "nonimmediate_operand" "0,0") (match_operand:HI 2 "general_operand" "ri,rm"))) (set (match_operand:HI 0 "nonimmediate_operand" "=rm,r") (minus:HI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCmode) && ix86_binary_operator_ok (MINUS, HImode, operands)" "sub{w}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "HI")]) (define_expand "subqi3" [(parallel [(set (match_operand:QI 0 "nonimmediate_operand" "") (minus:QI (match_operand:QI 1 "nonimmediate_operand" "") (match_operand:QI 2 "general_operand" ""))) (clobber (reg:CC 17))])] "TARGET_QIMODE_MATH" "ix86_expand_binary_operator (MINUS, QImode, operands); DONE;") (define_insn "*subqi_1" [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,q") (minus:QI (match_operand:QI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "general_operand" "qn,qmn"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (MINUS, QImode, operands)" "sub{b}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "QI")]) (define_insn "*subqi_1_slp" [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "+qm,q")) (minus:QI (match_dup 0) (match_operand:QI 1 "general_operand" "qn,qmn"))) (clobber (reg:CC 17))] "(! TARGET_PARTIAL_REG_STALL || optimize_size) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "sub{b}\t{%1, %0|%0, %1}" [(set_attr "type" "alu1") (set_attr "mode" "QI")]) (define_insn "*subqi_2" [(set (reg 17) (compare (minus:QI (match_operand:QI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "general_operand" "qi,qm")) (const_int 0))) (set (match_operand:HI 0 "nonimmediate_operand" "=qm,q") (minus:HI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (MINUS, QImode, operands)" "sub{b}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "QI")]) (define_insn "*subqi_3" [(set (reg 17) (compare (match_operand:QI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "general_operand" "qi,qm"))) (set (match_operand:HI 0 "nonimmediate_operand" "=qm,q") (minus:HI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCmode) && ix86_binary_operator_ok (MINUS, QImode, operands)" "sub{b}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "QI")]) ;; The patterns that match these are at the end of this file. (define_expand "subxf3" [(set (match_operand:XF 0 "register_operand" "") (minus:XF (match_operand:XF 1 "register_operand" "") (match_operand:XF 2 "register_operand" "")))] "TARGET_80387" "") (define_expand "subdf3" [(set (match_operand:DF 0 "register_operand" "") (minus:DF (match_operand:DF 1 "register_operand" "") (match_operand:DF 2 "nonimmediate_operand" "")))] "TARGET_80387 || (TARGET_SSE2 && TARGET_SSE_MATH)" "") (define_expand "subsf3" [(set (match_operand:SF 0 "register_operand" "") (minus:SF (match_operand:SF 1 "register_operand" "") (match_operand:SF 2 "nonimmediate_operand" "")))] "TARGET_80387 || TARGET_SSE_MATH" "") ;; Multiply instructions (define_expand "muldi3" [(parallel [(set (match_operand:DI 0 "register_operand" "") (mult:DI (match_operand:DI 1 "register_operand" "") (match_operand:DI 2 "x86_64_general_operand" ""))) (clobber (reg:CC 17))])] "TARGET_64BIT" "") (define_insn "*muldi3_1_rex64" [(set (match_operand:DI 0 "register_operand" "=r,r,r") (mult:DI (match_operand:DI 1 "nonimmediate_operand" "%rm,rm,0") (match_operand:DI 2 "x86_64_general_operand" "K,e,mr"))) (clobber (reg:CC 17))] "TARGET_64BIT && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "@ imul{q}\t{%2, %1, %0|%0, %1, %2} imul{q}\t{%2, %1, %0|%0, %1, %2} imul{q}\t{%2, %0|%0, %2}" [(set_attr "type" "imul") (set_attr "prefix_0f" "0,0,1") (set (attr "athlon_decode") (cond [(eq_attr "cpu" "athlon") (const_string "vector") (eq_attr "alternative" "1") (const_string "vector") (and (eq_attr "alternative" "2") (match_operand 1 "memory_operand" "")) (const_string "vector")] (const_string "direct"))) (set_attr "mode" "DI")]) (define_expand "mulsi3" [(parallel [(set (match_operand:SI 0 "register_operand" "") (mult:SI (match_operand:SI 1 "register_operand" "") (match_operand:SI 2 "general_operand" ""))) (clobber (reg:CC 17))])] "" "") (define_insn "*mulsi3_1" [(set (match_operand:SI 0 "register_operand" "=r,r,r") (mult:SI (match_operand:SI 1 "nonimmediate_operand" "%rm,rm,0") (match_operand:SI 2 "general_operand" "K,i,mr"))) (clobber (reg:CC 17))] "GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM" "@ imul{l}\t{%2, %1, %0|%0, %1, %2} imul{l}\t{%2, %1, %0|%0, %1, %2} imul{l}\t{%2, %0|%0, %2}" [(set_attr "type" "imul") (set_attr "prefix_0f" "0,0,1") (set (attr "athlon_decode") (cond [(eq_attr "cpu" "athlon") (const_string "vector") (eq_attr "alternative" "1") (const_string "vector") (and (eq_attr "alternative" "2") (match_operand 1 "memory_operand" "")) (const_string "vector")] (const_string "direct"))) (set_attr "mode" "SI")]) (define_insn "*mulsi3_1_zext" [(set (match_operand:DI 0 "register_operand" "=r,r,r") (zero_extend:DI (mult:SI (match_operand:SI 1 "nonimmediate_operand" "%rm,rm,0") (match_operand:SI 2 "general_operand" "K,i,mr")))) (clobber (reg:CC 17))] "TARGET_64BIT && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "@ imul{l}\t{%2, %1, %k0|%k0, %1, %2} imul{l}\t{%2, %1, %k0|%k0, %1, %2} imul{l}\t{%2, %k0|%k0, %2}" [(set_attr "type" "imul") (set_attr "prefix_0f" "0,0,1") (set (attr "athlon_decode") (cond [(eq_attr "cpu" "athlon") (const_string "vector") (eq_attr "alternative" "1") (const_string "vector") (and (eq_attr "alternative" "2") (match_operand 1 "memory_operand" "")) (const_string "vector")] (const_string "direct"))) (set_attr "mode" "SI")]) (define_expand "mulhi3" [(parallel [(set (match_operand:HI 0 "register_operand" "") (mult:HI (match_operand:HI 1 "register_operand" "") (match_operand:HI 2 "general_operand" ""))) (clobber (reg:CC 17))])] "TARGET_HIMODE_MATH" "") (define_insn "*mulhi3_1" [(set (match_operand:HI 0 "register_operand" "=r,r,r") (mult:HI (match_operand:HI 1 "nonimmediate_operand" "%rm,rm,0") (match_operand:HI 2 "general_operand" "K,i,mr"))) (clobber (reg:CC 17))] "GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM" "@ imul{w}\t{%2, %1, %0|%0, %1, %2} imul{w}\t{%2, %1, %0|%0, %1, %2} imul{w}\t{%2, %0|%0, %2}" [(set_attr "type" "imul") (set_attr "prefix_0f" "0,0,1") (set (attr "athlon_decode") (cond [(eq_attr "cpu" "athlon") (const_string "vector") (eq_attr "alternative" "1,2") (const_string "vector")] (const_string "direct"))) (set_attr "mode" "HI")]) (define_expand "mulqi3" [(parallel [(set (match_operand:QI 0 "register_operand" "") (mult:QI (match_operand:QI 1 "nonimmediate_operand" "") (match_operand:QI 2 "register_operand" ""))) (clobber (reg:CC 17))])] "TARGET_QIMODE_MATH" "") (define_insn "*mulqi3_1" [(set (match_operand:QI 0 "register_operand" "=a") (mult:QI (match_operand:QI 1 "nonimmediate_operand" "%0") (match_operand:QI 2 "nonimmediate_operand" "qm"))) (clobber (reg:CC 17))] "TARGET_QIMODE_MATH && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "mul{b}\t%2" [(set_attr "type" "imul") (set_attr "length_immediate" "0") (set (attr "athlon_decode") (if_then_else (eq_attr "cpu" "athlon") (const_string "vector") (const_string "direct"))) (set_attr "mode" "QI")]) (define_expand "umulqihi3" [(parallel [(set (match_operand:HI 0 "register_operand" "") (mult:HI (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "")) (zero_extend:HI (match_operand:QI 2 "register_operand" "")))) (clobber (reg:CC 17))])] "TARGET_QIMODE_MATH" "") (define_insn "*umulqihi3_1" [(set (match_operand:HI 0 "register_operand" "=a") (mult:HI (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "%0")) (zero_extend:HI (match_operand:QI 2 "nonimmediate_operand" "qm")))) (clobber (reg:CC 17))] "TARGET_QIMODE_MATH && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "mul{b}\t%2" [(set_attr "type" "imul") (set_attr "length_immediate" "0") (set (attr "athlon_decode") (if_then_else (eq_attr "cpu" "athlon") (const_string "vector") (const_string "direct"))) (set_attr "mode" "QI")]) (define_expand "mulqihi3" [(parallel [(set (match_operand:HI 0 "register_operand" "") (mult:HI (sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "")) (sign_extend:HI (match_operand:QI 2 "register_operand" "")))) (clobber (reg:CC 17))])] "TARGET_QIMODE_MATH" "") (define_insn "*mulqihi3_insn" [(set (match_operand:HI 0 "register_operand" "=a") (mult:HI (sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "%0")) (sign_extend:HI (match_operand:QI 2 "nonimmediate_operand" "qm")))) (clobber (reg:CC 17))] "TARGET_QIMODE_MATH && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "imul{b}\t%2" [(set_attr "type" "imul") (set_attr "length_immediate" "0") (set (attr "athlon_decode") (if_then_else (eq_attr "cpu" "athlon") (const_string "vector") (const_string "direct"))) (set_attr "mode" "QI")]) (define_expand "umulditi3" [(parallel [(set (match_operand:TI 0 "register_operand" "") (mult:TI (zero_extend:TI (match_operand:DI 1 "nonimmediate_operand" "")) (zero_extend:TI (match_operand:DI 2 "register_operand" "")))) (clobber (reg:CC 17))])] "TARGET_64BIT" "") (define_insn "*umulditi3_insn" [(set (match_operand:TI 0 "register_operand" "=A") (mult:TI (zero_extend:TI (match_operand:DI 1 "nonimmediate_operand" "%0")) (zero_extend:TI (match_operand:DI 2 "nonimmediate_operand" "rm")))) (clobber (reg:CC 17))] "TARGET_64BIT && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "mul{q}\t%2" [(set_attr "type" "imul") (set_attr "ppro_uops" "few") (set_attr "length_immediate" "0") (set (attr "athlon_decode") (if_then_else (eq_attr "cpu" "athlon") (const_string "vector") (const_string "double"))) (set_attr "mode" "DI")]) ;; We can't use this pattern in 64bit mode, since it results in two separate 32bit registers (define_expand "umulsidi3" [(parallel [(set (match_operand:DI 0 "register_operand" "") (mult:DI (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "")) (zero_extend:DI (match_operand:SI 2 "register_operand" "")))) (clobber (reg:CC 17))])] "!TARGET_64BIT" "") (define_insn "*umulsidi3_insn" [(set (match_operand:DI 0 "register_operand" "=A") (mult:DI (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "%0")) (zero_extend:DI (match_operand:SI 2 "nonimmediate_operand" "rm")))) (clobber (reg:CC 17))] "!TARGET_64BIT && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "mul{l}\t%2" [(set_attr "type" "imul") (set_attr "ppro_uops" "few") (set_attr "length_immediate" "0") (set (attr "athlon_decode") (if_then_else (eq_attr "cpu" "athlon") (const_string "vector") (const_string "double"))) (set_attr "mode" "SI")]) (define_expand "mulditi3" [(parallel [(set (match_operand:TI 0 "register_operand" "") (mult:TI (sign_extend:TI (match_operand:DI 1 "nonimmediate_operand" "")) (sign_extend:TI (match_operand:DI 2 "register_operand" "")))) (clobber (reg:CC 17))])] "TARGET_64BIT" "") (define_insn "*mulditi3_insn" [(set (match_operand:TI 0 "register_operand" "=A") (mult:TI (sign_extend:TI (match_operand:DI 1 "nonimmediate_operand" "%0")) (sign_extend:TI (match_operand:DI 2 "nonimmediate_operand" "rm")))) (clobber (reg:CC 17))] "TARGET_64BIT && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "imul{q}\t%2" [(set_attr "type" "imul") (set_attr "length_immediate" "0") (set (attr "athlon_decode") (if_then_else (eq_attr "cpu" "athlon") (const_string "vector") (const_string "double"))) (set_attr "mode" "DI")]) (define_expand "mulsidi3" [(parallel [(set (match_operand:DI 0 "register_operand" "") (mult:DI (sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "")) (sign_extend:DI (match_operand:SI 2 "register_operand" "")))) (clobber (reg:CC 17))])] "!TARGET_64BIT" "") (define_insn "*mulsidi3_insn" [(set (match_operand:DI 0 "register_operand" "=A") (mult:DI (sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "%0")) (sign_extend:DI (match_operand:SI 2 "nonimmediate_operand" "rm")))) (clobber (reg:CC 17))] "!TARGET_64BIT && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "imul{l}\t%2" [(set_attr "type" "imul") (set_attr "length_immediate" "0") (set (attr "athlon_decode") (if_then_else (eq_attr "cpu" "athlon") (const_string "vector") (const_string "double"))) (set_attr "mode" "SI")]) (define_expand "umuldi3_highpart" [(parallel [(set (match_operand:DI 0 "register_operand" "") (truncate:DI (lshiftrt:TI (mult:TI (zero_extend:TI (match_operand:DI 1 "nonimmediate_operand" "")) (zero_extend:TI (match_operand:DI 2 "register_operand" ""))) (const_int 64)))) (clobber (match_scratch:DI 3 "")) (clobber (reg:CC 17))])] "TARGET_64BIT" "") (define_insn "*umuldi3_highpart_rex64" [(set (match_operand:DI 0 "register_operand" "=d") (truncate:DI (lshiftrt:TI (mult:TI (zero_extend:TI (match_operand:DI 1 "nonimmediate_operand" "%a")) (zero_extend:TI (match_operand:DI 2 "nonimmediate_operand" "rm"))) (const_int 64)))) (clobber (match_scratch:DI 3 "=1")) (clobber (reg:CC 17))] "TARGET_64BIT && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "mul{q}\t%2" [(set_attr "type" "imul") (set_attr "ppro_uops" "few") (set_attr "length_immediate" "0") (set (attr "athlon_decode") (if_then_else (eq_attr "cpu" "athlon") (const_string "vector") (const_string "double"))) (set_attr "mode" "DI")]) (define_expand "umulsi3_highpart" [(parallel [(set (match_operand:SI 0 "register_operand" "") (truncate:SI (lshiftrt:DI (mult:DI (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "")) (zero_extend:DI (match_operand:SI 2 "register_operand" ""))) (const_int 32)))) (clobber (match_scratch:SI 3 "")) (clobber (reg:CC 17))])] "" "") (define_insn "*umulsi3_highpart_insn" [(set (match_operand:SI 0 "register_operand" "=d") (truncate:SI (lshiftrt:DI (mult:DI (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "%a")) (zero_extend:DI (match_operand:SI 2 "nonimmediate_operand" "rm"))) (const_int 32)))) (clobber (match_scratch:SI 3 "=1")) (clobber (reg:CC 17))] "GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM" "mul{l}\t%2" [(set_attr "type" "imul") (set_attr "ppro_uops" "few") (set_attr "length_immediate" "0") (set (attr "athlon_decode") (if_then_else (eq_attr "cpu" "athlon") (const_string "vector") (const_string "double"))) (set_attr "mode" "SI")]) (define_insn "*umulsi3_highpart_zext" [(set (match_operand:DI 0 "register_operand" "=d") (zero_extend:DI (truncate:SI (lshiftrt:DI (mult:DI (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "%a")) (zero_extend:DI (match_operand:SI 2 "nonimmediate_operand" "rm"))) (const_int 32))))) (clobber (match_scratch:SI 3 "=1")) (clobber (reg:CC 17))] "TARGET_64BIT && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "mul{l}\t%2" [(set_attr "type" "imul") (set_attr "ppro_uops" "few") (set_attr "length_immediate" "0") (set (attr "athlon_decode") (if_then_else (eq_attr "cpu" "athlon") (const_string "vector") (const_string "double"))) (set_attr "mode" "SI")]) (define_expand "smuldi3_highpart" [(parallel [(set (match_operand:DI 0 "register_operand" "=d") (truncate:DI (lshiftrt:TI (mult:TI (sign_extend:TI (match_operand:DI 1 "nonimmediate_operand" "")) (sign_extend:TI (match_operand:DI 2 "register_operand" ""))) (const_int 64)))) (clobber (match_scratch:DI 3 "")) (clobber (reg:CC 17))])] "TARGET_64BIT" "") (define_insn "*smuldi3_highpart_rex64" [(set (match_operand:DI 0 "register_operand" "=d") (truncate:DI (lshiftrt:TI (mult:TI (sign_extend:TI (match_operand:DI 1 "nonimmediate_operand" "%a")) (sign_extend:TI (match_operand:DI 2 "nonimmediate_operand" "rm"))) (const_int 64)))) (clobber (match_scratch:DI 3 "=1")) (clobber (reg:CC 17))] "TARGET_64BIT && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "imul{q}\t%2" [(set_attr "type" "imul") (set_attr "ppro_uops" "few") (set (attr "athlon_decode") (if_then_else (eq_attr "cpu" "athlon") (const_string "vector") (const_string "double"))) (set_attr "mode" "DI")]) (define_expand "smulsi3_highpart" [(parallel [(set (match_operand:SI 0 "register_operand" "") (truncate:SI (lshiftrt:DI (mult:DI (sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "")) (sign_extend:DI (match_operand:SI 2 "register_operand" ""))) (const_int 32)))) (clobber (match_scratch:SI 3 "")) (clobber (reg:CC 17))])] "" "") (define_insn "*smulsi3_highpart_insn" [(set (match_operand:SI 0 "register_operand" "=d") (truncate:SI (lshiftrt:DI (mult:DI (sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "%a")) (sign_extend:DI (match_operand:SI 2 "nonimmediate_operand" "rm"))) (const_int 32)))) (clobber (match_scratch:SI 3 "=1")) (clobber (reg:CC 17))] "GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM" "imul{l}\t%2" [(set_attr "type" "imul") (set_attr "ppro_uops" "few") (set (attr "athlon_decode") (if_then_else (eq_attr "cpu" "athlon") (const_string "vector") (const_string "double"))) (set_attr "mode" "SI")]) (define_insn "*smulsi3_highpart_zext" [(set (match_operand:DI 0 "register_operand" "=d") (zero_extend:DI (truncate:SI (lshiftrt:DI (mult:DI (sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "%a")) (sign_extend:DI (match_operand:SI 2 "nonimmediate_operand" "rm"))) (const_int 32))))) (clobber (match_scratch:SI 3 "=1")) (clobber (reg:CC 17))] "TARGET_64BIT && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "imul{l}\t%2" [(set_attr "type" "imul") (set_attr "ppro_uops" "few") (set (attr "athlon_decode") (if_then_else (eq_attr "cpu" "athlon") (const_string "vector") (const_string "double"))) (set_attr "mode" "SI")]) ;; The patterns that match these are at the end of this file. (define_expand "mulxf3" [(set (match_operand:XF 0 "register_operand" "") (mult:XF (match_operand:XF 1 "register_operand" "") (match_operand:XF 2 "register_operand" "")))] "TARGET_80387" "") (define_expand "muldf3" [(set (match_operand:DF 0 "register_operand" "") (mult:DF (match_operand:DF 1 "register_operand" "") (match_operand:DF 2 "nonimmediate_operand" "")))] "TARGET_80387 || (TARGET_SSE2 && TARGET_SSE_MATH)" "") (define_expand "mulsf3" [(set (match_operand:SF 0 "register_operand" "") (mult:SF (match_operand:SF 1 "register_operand" "") (match_operand:SF 2 "nonimmediate_operand" "")))] "TARGET_80387 || TARGET_SSE_MATH" "") ;; Divide instructions (define_insn "divqi3" [(set (match_operand:QI 0 "register_operand" "=a") (div:QI (match_operand:HI 1 "register_operand" "0") (match_operand:QI 2 "nonimmediate_operand" "qm"))) (clobber (reg:CC 17))] "TARGET_QIMODE_MATH" "idiv{b}\t%2" [(set_attr "type" "idiv") (set_attr "mode" "QI") (set_attr "ppro_uops" "few")]) (define_insn "udivqi3" [(set (match_operand:QI 0 "register_operand" "=a") (udiv:QI (match_operand:HI 1 "register_operand" "0") (match_operand:QI 2 "nonimmediate_operand" "qm"))) (clobber (reg:CC 17))] "TARGET_QIMODE_MATH" "div{b}\t%2" [(set_attr "type" "idiv") (set_attr "mode" "QI") (set_attr "ppro_uops" "few")]) ;; The patterns that match these are at the end of this file. (define_expand "divxf3" [(set (match_operand:XF 0 "register_operand" "") (div:XF (match_operand:XF 1 "register_operand" "") (match_operand:XF 2 "register_operand" "")))] "TARGET_80387" "") (define_expand "divdf3" [(set (match_operand:DF 0 "register_operand" "") (div:DF (match_operand:DF 1 "register_operand" "") (match_operand:DF 2 "nonimmediate_operand" "")))] "TARGET_80387 || (TARGET_SSE2 && TARGET_SSE_MATH)" "") (define_expand "divsf3" [(set (match_operand:SF 0 "register_operand" "") (div:SF (match_operand:SF 1 "register_operand" "") (match_operand:SF 2 "nonimmediate_operand" "")))] "TARGET_80387 || TARGET_SSE_MATH" "") ;; Remainder instructions. (define_expand "divmoddi4" [(parallel [(set (match_operand:DI 0 "register_operand" "") (div:DI (match_operand:DI 1 "register_operand" "") (match_operand:DI 2 "nonimmediate_operand" ""))) (set (match_operand:DI 3 "register_operand" "") (mod:DI (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))])] "TARGET_64BIT" "") ;; Allow to come the parameter in eax or edx to avoid extra moves. ;; Penalize eax case slightly because it results in worse scheduling ;; of code. (define_insn "*divmoddi4_nocltd_rex64" [(set (match_operand:DI 0 "register_operand" "=&a,?a") (div:DI (match_operand:DI 2 "register_operand" "1,0") (match_operand:DI 3 "nonimmediate_operand" "rm,rm"))) (set (match_operand:DI 1 "register_operand" "=&d,&d") (mod:DI (match_dup 2) (match_dup 3))) (clobber (reg:CC 17))] "TARGET_64BIT && !optimize_size && !TARGET_USE_CLTD" "#" [(set_attr "type" "multi")]) (define_insn "*divmoddi4_cltd_rex64" [(set (match_operand:DI 0 "register_operand" "=a") (div:DI (match_operand:DI 2 "register_operand" "a") (match_operand:DI 3 "nonimmediate_operand" "rm"))) (set (match_operand:DI 1 "register_operand" "=&d") (mod:DI (match_dup 2) (match_dup 3))) (clobber (reg:CC 17))] "TARGET_64BIT && (optimize_size || TARGET_USE_CLTD)" "#" [(set_attr "type" "multi")]) (define_insn "*divmoddi_noext_rex64" [(set (match_operand:DI 0 "register_operand" "=a") (div:DI (match_operand:DI 1 "register_operand" "0") (match_operand:DI 2 "nonimmediate_operand" "rm"))) (set (match_operand:DI 3 "register_operand" "=d") (mod:DI (match_dup 1) (match_dup 2))) (use (match_operand:DI 4 "register_operand" "3")) (clobber (reg:CC 17))] "TARGET_64BIT" "idiv{q}\t%2" [(set_attr "type" "idiv") (set_attr "mode" "DI") (set_attr "ppro_uops" "few")]) (define_split [(set (match_operand:DI 0 "register_operand" "") (div:DI (match_operand:DI 1 "register_operand" "") (match_operand:DI 2 "nonimmediate_operand" ""))) (set (match_operand:DI 3 "register_operand" "") (mod:DI (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))] "TARGET_64BIT && reload_completed" [(parallel [(set (match_dup 3) (ashiftrt:DI (match_dup 4) (const_int 63))) (clobber (reg:CC 17))]) (parallel [(set (match_dup 0) (div:DI (reg:DI 0) (match_dup 2))) (set (match_dup 3) (mod:DI (reg:DI 0) (match_dup 2))) (use (match_dup 3)) (clobber (reg:CC 17))])] { /* Avoid use of cltd in favor of a mov+shift. */ if (!TARGET_USE_CLTD && !optimize_size) { if (true_regnum (operands[1])) emit_move_insn (operands[0], operands[1]); else emit_move_insn (operands[3], operands[1]); operands[4] = operands[3]; } else { if (true_regnum (operands[1])) abort(); operands[4] = operands[1]; } }) (define_expand "divmodsi4" [(parallel [(set (match_operand:SI 0 "register_operand" "") (div:SI (match_operand:SI 1 "register_operand" "") (match_operand:SI 2 "nonimmediate_operand" ""))) (set (match_operand:SI 3 "register_operand" "") (mod:SI (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))])] "" "") ;; Allow to come the parameter in eax or edx to avoid extra moves. ;; Penalize eax case slightly because it results in worse scheduling ;; of code. (define_insn "*divmodsi4_nocltd" [(set (match_operand:SI 0 "register_operand" "=&a,?a") (div:SI (match_operand:SI 2 "register_operand" "1,0") (match_operand:SI 3 "nonimmediate_operand" "rm,rm"))) (set (match_operand:SI 1 "register_operand" "=&d,&d") (mod:SI (match_dup 2) (match_dup 3))) (clobber (reg:CC 17))] "!optimize_size && !TARGET_USE_CLTD" "#" [(set_attr "type" "multi")]) (define_insn "*divmodsi4_cltd" [(set (match_operand:SI 0 "register_operand" "=a") (div:SI (match_operand:SI 2 "register_operand" "a") (match_operand:SI 3 "nonimmediate_operand" "rm"))) (set (match_operand:SI 1 "register_operand" "=&d") (mod:SI (match_dup 2) (match_dup 3))) (clobber (reg:CC 17))] "optimize_size || TARGET_USE_CLTD" "#" [(set_attr "type" "multi")]) (define_insn "*divmodsi_noext" [(set (match_operand:SI 0 "register_operand" "=a") (div:SI (match_operand:SI 1 "register_operand" "0") (match_operand:SI 2 "nonimmediate_operand" "rm"))) (set (match_operand:SI 3 "register_operand" "=d") (mod:SI (match_dup 1) (match_dup 2))) (use (match_operand:SI 4 "register_operand" "3")) (clobber (reg:CC 17))] "" "idiv{l}\t%2" [(set_attr "type" "idiv") (set_attr "mode" "SI") (set_attr "ppro_uops" "few")]) (define_split [(set (match_operand:SI 0 "register_operand" "") (div:SI (match_operand:SI 1 "register_operand" "") (match_operand:SI 2 "nonimmediate_operand" ""))) (set (match_operand:SI 3 "register_operand" "") (mod:SI (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))] "reload_completed" [(parallel [(set (match_dup 3) (ashiftrt:SI (match_dup 4) (const_int 31))) (clobber (reg:CC 17))]) (parallel [(set (match_dup 0) (div:SI (reg:SI 0) (match_dup 2))) (set (match_dup 3) (mod:SI (reg:SI 0) (match_dup 2))) (use (match_dup 3)) (clobber (reg:CC 17))])] { /* Avoid use of cltd in favor of a mov+shift. */ if (!TARGET_USE_CLTD && !optimize_size) { if (true_regnum (operands[1])) emit_move_insn (operands[0], operands[1]); else emit_move_insn (operands[3], operands[1]); operands[4] = operands[3]; } else { if (true_regnum (operands[1])) abort(); operands[4] = operands[1]; } }) ;; %%% Split me. (define_insn "divmodhi4" [(set (match_operand:HI 0 "register_operand" "=a") (div:HI (match_operand:HI 1 "register_operand" "0") (match_operand:HI 2 "nonimmediate_operand" "rm"))) (set (match_operand:HI 3 "register_operand" "=&d") (mod:HI (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))] "TARGET_HIMODE_MATH" "cwtd\;idiv{w}\t%2" [(set_attr "type" "multi") (set_attr "length_immediate" "0") (set_attr "mode" "SI")]) (define_insn "udivmoddi4" [(set (match_operand:DI 0 "register_operand" "=a") (udiv:DI (match_operand:DI 1 "register_operand" "0") (match_operand:DI 2 "nonimmediate_operand" "rm"))) (set (match_operand:DI 3 "register_operand" "=&d") (umod:DI (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))] "TARGET_64BIT" "xor{q}\t%3, %3\;div{q}\t%2" [(set_attr "type" "multi") (set_attr "length_immediate" "0") (set_attr "mode" "DI")]) (define_insn "*udivmoddi4_noext" [(set (match_operand:DI 0 "register_operand" "=a") (udiv:DI (match_operand:DI 1 "register_operand" "0") (match_operand:DI 2 "nonimmediate_operand" "rm"))) (set (match_operand:DI 3 "register_operand" "=d") (umod:DI (match_dup 1) (match_dup 2))) (use (match_dup 3)) (clobber (reg:CC 17))] "TARGET_64BIT" "div{q}\t%2" [(set_attr "type" "idiv") (set_attr "ppro_uops" "few") (set_attr "mode" "DI")]) (define_split [(set (match_operand:DI 0 "register_operand" "") (udiv:DI (match_operand:DI 1 "register_operand" "") (match_operand:DI 2 "nonimmediate_operand" ""))) (set (match_operand:DI 3 "register_operand" "") (umod:DI (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))] "TARGET_64BIT && reload_completed" [(set (match_dup 3) (const_int 0)) (parallel [(set (match_dup 0) (udiv:DI (match_dup 1) (match_dup 2))) (set (match_dup 3) (umod:DI (match_dup 1) (match_dup 2))) (use (match_dup 3)) (clobber (reg:CC 17))])] "") (define_insn "udivmodsi4" [(set (match_operand:SI 0 "register_operand" "=a") (udiv:SI (match_operand:SI 1 "register_operand" "0") (match_operand:SI 2 "nonimmediate_operand" "rm"))) (set (match_operand:SI 3 "register_operand" "=&d") (umod:SI (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))] "" "xor{l}\t%3, %3\;div{l}\t%2" [(set_attr "type" "multi") (set_attr "length_immediate" "0") (set_attr "mode" "SI")]) (define_insn "*udivmodsi4_noext" [(set (match_operand:SI 0 "register_operand" "=a") (udiv:SI (match_operand:SI 1 "register_operand" "0") (match_operand:SI 2 "nonimmediate_operand" "rm"))) (set (match_operand:SI 3 "register_operand" "=d") (umod:SI (match_dup 1) (match_dup 2))) (use (match_dup 3)) (clobber (reg:CC 17))] "" "div{l}\t%2" [(set_attr "type" "idiv") (set_attr "ppro_uops" "few") (set_attr "mode" "SI")]) (define_split [(set (match_operand:SI 0 "register_operand" "") (udiv:SI (match_operand:SI 1 "register_operand" "") (match_operand:SI 2 "nonimmediate_operand" ""))) (set (match_operand:SI 3 "register_operand" "") (umod:SI (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))] "reload_completed" [(set (match_dup 3) (const_int 0)) (parallel [(set (match_dup 0) (udiv:SI (match_dup 1) (match_dup 2))) (set (match_dup 3) (umod:SI (match_dup 1) (match_dup 2))) (use (match_dup 3)) (clobber (reg:CC 17))])] "") (define_expand "udivmodhi4" [(set (match_dup 4) (const_int 0)) (parallel [(set (match_operand:HI 0 "register_operand" "") (udiv:HI (match_operand:HI 1 "register_operand" "") (match_operand:HI 2 "nonimmediate_operand" ""))) (set (match_operand:HI 3 "register_operand" "") (umod:HI (match_dup 1) (match_dup 2))) (use (match_dup 4)) (clobber (reg:CC 17))])] "TARGET_HIMODE_MATH" "operands[4] = gen_reg_rtx (HImode);") (define_insn "*udivmodhi_noext" [(set (match_operand:HI 0 "register_operand" "=a") (udiv:HI (match_operand:HI 1 "register_operand" "0") (match_operand:HI 2 "nonimmediate_operand" "rm"))) (set (match_operand:HI 3 "register_operand" "=d") (umod:HI (match_dup 1) (match_dup 2))) (use (match_operand:HI 4 "register_operand" "3")) (clobber (reg:CC 17))] "" "div{w}\t%2" [(set_attr "type" "idiv") (set_attr "mode" "HI") (set_attr "ppro_uops" "few")]) ;; We can not use div/idiv for double division, because it causes ;; "division by zero" on the overflow and that's not what we expect ;; from truncate. Because true (non truncating) double division is ;; never generated, we can't create this insn anyway. ; ;(define_insn "" ; [(set (match_operand:SI 0 "register_operand" "=a") ; (truncate:SI ; (udiv:DI (match_operand:DI 1 "register_operand" "A") ; (zero_extend:DI ; (match_operand:SI 2 "nonimmediate_operand" "rm"))))) ; (set (match_operand:SI 3 "register_operand" "=d") ; (truncate:SI ; (umod:DI (match_dup 1) (zero_extend:DI (match_dup 2))))) ; (clobber (reg:CC 17))] ; "" ; "div{l}\t{%2, %0|%0, %2}" ; [(set_attr "type" "idiv") ; (set_attr "ppro_uops" "few")]) ;;- Logical AND instructions ;; On Pentium, "test imm, reg" is pairable only with eax, ax, and al. ;; Note that this excludes ah. (define_insn "*testdi_1_rex64" [(set (reg 17) (compare (and:DI (match_operand:DI 0 "nonimmediate_operand" "%!*a,r,!*a,r,rm") (match_operand:DI 1 "x86_64_szext_general_operand" "Z,Z,e,e,re")) (const_int 0)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "@ test{l}\t{%k1, %k0|%k0, %k1} test{l}\t{%k1, %k0|%k0, %k1} test{q}\t{%1, %0|%0, %1} test{q}\t{%1, %0|%0, %1} test{q}\t{%1, %0|%0, %1}" [(set_attr "type" "test") (set_attr "modrm" "0,1,0,1,1") (set_attr "mode" "SI,SI,DI,DI,DI") (set_attr "pent_pair" "uv,np,uv,np,uv")]) (define_insn "testsi_1" [(set (reg 17) (compare (and:SI (match_operand:SI 0 "nonimmediate_operand" "%!*a,r,rm") (match_operand:SI 1 "general_operand" "in,in,rin")) (const_int 0)))] "ix86_match_ccmode (insn, CCNOmode) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "test{l}\t{%1, %0|%0, %1}" [(set_attr "type" "test") (set_attr "modrm" "0,1,1") (set_attr "mode" "SI") (set_attr "pent_pair" "uv,np,uv")]) (define_expand "testsi_ccno_1" [(set (reg:CCNO 17) (compare:CCNO (and:SI (match_operand:SI 0 "nonimmediate_operand" "") (match_operand:SI 1 "nonmemory_operand" "")) (const_int 0)))] "" "") (define_insn "*testhi_1" [(set (reg 17) (compare (and:HI (match_operand:HI 0 "nonimmediate_operand" "%!*a,r,rm") (match_operand:HI 1 "general_operand" "n,n,rn")) (const_int 0)))] "ix86_match_ccmode (insn, CCNOmode) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "test{w}\t{%1, %0|%0, %1}" [(set_attr "type" "test") (set_attr "modrm" "0,1,1") (set_attr "mode" "HI") (set_attr "pent_pair" "uv,np,uv")]) (define_expand "testqi_ccz_1" [(set (reg:CCZ 17) (compare:CCZ (and:QI (match_operand:QI 0 "nonimmediate_operand" "") (match_operand:QI 1 "nonmemory_operand" "")) (const_int 0)))] "" "") (define_insn "*testqi_1_maybe_si" [(set (reg 17) (compare (and:QI (match_operand:QI 0 "nonimmediate_operand" "%!*a,q,qm,r") (match_operand:QI 1 "general_operand" "n,n,qn,n")) (const_int 0)))] "(GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) && ix86_match_ccmode (insn, GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) >= 0 ? CCNOmode : CCZmode)" { if (which_alternative == 3) { if (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) < 0) operands[1] = GEN_INT (INTVAL (operands[1]) & 0xff); return "test{l}\t{%1, %k0|%k0, %1}"; } return "test{b}\t{%1, %0|%0, %1}"; } [(set_attr "type" "test") (set_attr "modrm" "0,1,1,1") (set_attr "mode" "QI,QI,QI,SI") (set_attr "pent_pair" "uv,np,uv,np")]) (define_insn "*testqi_1" [(set (reg 17) (compare (and:QI (match_operand:QI 0 "nonimmediate_operand" "%!*a,q,qm") (match_operand:QI 1 "general_operand" "n,n,qn")) (const_int 0)))] "(GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) && ix86_match_ccmode (insn, CCNOmode)" "test{b}\t{%1, %0|%0, %1}" [(set_attr "type" "test") (set_attr "modrm" "0,1,1") (set_attr "mode" "QI") (set_attr "pent_pair" "uv,np,uv")]) (define_expand "testqi_ext_ccno_0" [(set (reg:CCNO 17) (compare:CCNO (and:SI (zero_extract:SI (match_operand 0 "ext_register_operand" "") (const_int 8) (const_int 8)) (match_operand 1 "const_int_operand" "")) (const_int 0)))] "" "") (define_insn "*testqi_ext_0" [(set (reg 17) (compare (and:SI (zero_extract:SI (match_operand 0 "ext_register_operand" "Q") (const_int 8) (const_int 8)) (match_operand 1 "const_int_operand" "n")) (const_int 0)))] "ix86_match_ccmode (insn, CCNOmode)" "test{b}\t{%1, %h0|%h0, %1}" [(set_attr "type" "test") (set_attr "mode" "QI") (set_attr "length_immediate" "1") (set_attr "pent_pair" "np")]) (define_insn "*testqi_ext_1" [(set (reg 17) (compare (and:SI (zero_extract:SI (match_operand 0 "ext_register_operand" "Q") (const_int 8) (const_int 8)) (zero_extend:SI (match_operand:QI 1 "general_operand" "Qm"))) (const_int 0)))] "!TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "test{b}\t{%1, %h0|%h0, %1}" [(set_attr "type" "test") (set_attr "mode" "QI")]) (define_insn "*testqi_ext_1_rex64" [(set (reg 17) (compare (and:SI (zero_extract:SI (match_operand 0 "ext_register_operand" "Q") (const_int 8) (const_int 8)) (zero_extend:SI (match_operand:QI 1 "register_operand" "Q"))) (const_int 0)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode)" "test{b}\t{%1, %h0|%h0, %1}" [(set_attr "type" "test") (set_attr "mode" "QI")]) (define_insn "*testqi_ext_2" [(set (reg 17) (compare (and:SI (zero_extract:SI (match_operand 0 "ext_register_operand" "Q") (const_int 8) (const_int 8)) (zero_extract:SI (match_operand 1 "ext_register_operand" "Q") (const_int 8) (const_int 8))) (const_int 0)))] "ix86_match_ccmode (insn, CCNOmode)" "test{b}\t{%h1, %h0|%h0, %h1}" [(set_attr "type" "test") (set_attr "mode" "QI")]) ;; Combine likes to form bit extractions for some tests. Humor it. (define_insn "*testqi_ext_3" [(set (reg 17) (compare (zero_extract:SI (match_operand 0 "nonimmediate_operand" "rm") (match_operand:SI 1 "const_int_operand" "") (match_operand:SI 2 "const_int_operand" "")) (const_int 0)))] "ix86_match_ccmode (insn, CCNOmode) && (GET_MODE (operands[0]) == SImode || (TARGET_64BIT && GET_MODE (operands[0]) == DImode) || GET_MODE (operands[0]) == HImode || GET_MODE (operands[0]) == QImode)" "#") (define_insn "*testqi_ext_3_rex64" [(set (reg 17) (compare (zero_extract:DI (match_operand 0 "nonimmediate_operand" "rm") (match_operand:DI 1 "const_int_operand" "") (match_operand:DI 2 "const_int_operand" "")) (const_int 0)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode) /* The code below cannot deal with constants outside HOST_WIDE_INT. */ && INTVAL (operands[1]) + INTVAL (operands[2]) < HOST_BITS_PER_WIDE_INT /* Ensure that resulting mask is zero or sign extended operand. */ && (INTVAL (operands[1]) + INTVAL (operands[2]) <= 32 || (INTVAL (operands[1]) + INTVAL (operands[2]) == 64 && INTVAL (operands[1]) > 32)) && (GET_MODE (operands[0]) == SImode || GET_MODE (operands[0]) == DImode || GET_MODE (operands[0]) == HImode || GET_MODE (operands[0]) == QImode)" "#") (define_split [(set (match_operand 0 "flags_reg_operand" "") (match_operator 1 "compare_operator" [(zero_extract (match_operand 2 "nonimmediate_operand" "") (match_operand 3 "const_int_operand" "") (match_operand 4 "const_int_operand" "")) (const_int 0)]))] "ix86_match_ccmode (insn, CCNOmode)" [(set (match_dup 0) (match_op_dup 1 [(match_dup 2) (const_int 0)]))] { rtx val = operands[2]; HOST_WIDE_INT len = INTVAL (operands[3]); HOST_WIDE_INT pos = INTVAL (operands[4]); HOST_WIDE_INT mask; enum machine_mode mode, submode; mode = GET_MODE (val); if (GET_CODE (val) == MEM) { /* ??? Combine likes to put non-volatile mem extractions in QImode no matter the size of the test. So find a mode that works. */ if (! MEM_VOLATILE_P (val)) { mode = smallest_mode_for_size (pos + len, MODE_INT); val = adjust_address (val, mode, 0); } } else if (GET_CODE (val) == SUBREG && (submode = GET_MODE (SUBREG_REG (val)), GET_MODE_BITSIZE (mode) > GET_MODE_BITSIZE (submode)) && pos + len <= GET_MODE_BITSIZE (submode)) { /* Narrow a paradoxical subreg to prevent partial register stalls. */ mode = submode; val = SUBREG_REG (val); } else if (mode == HImode && pos + len <= 8) { /* Small HImode tests can be converted to QImode. */ mode = QImode; val = gen_lowpart (QImode, val); } mask = ((HOST_WIDE_INT)1 << (pos + len)) - 1; mask &= ~(((HOST_WIDE_INT)1 << pos) - 1); operands[2] = gen_rtx_AND (mode, val, gen_int_mode (mask, mode)); }) ;; Convert HImode/SImode test instructions with immediate to QImode ones. ;; i386 does not allow to encode test with 8bit sign extended immediate, so ;; this is relatively important trick. ;; Do the conversion only post-reload to avoid limiting of the register class ;; to QI regs. (define_split [(set (match_operand 0 "flags_reg_operand" "") (match_operator 1 "compare_operator" [(and (match_operand 2 "register_operand" "") (match_operand 3 "const_int_operand" "")) (const_int 0)]))] "reload_completed && QI_REG_P (operands[2]) && GET_MODE (operands[2]) != QImode && ((ix86_match_ccmode (insn, CCZmode) && !(INTVAL (operands[3]) & ~(255 << 8))) || (ix86_match_ccmode (insn, CCNOmode) && !(INTVAL (operands[3]) & ~(127 << 8))))" [(set (match_dup 0) (match_op_dup 1 [(and:SI (zero_extract:SI (match_dup 2) (const_int 8) (const_int 8)) (match_dup 3)) (const_int 0)]))] "operands[2] = gen_lowpart (SImode, operands[2]); operands[3] = gen_int_mode (INTVAL (operands[3]) >> 8, SImode);") (define_split [(set (match_operand 0 "flags_reg_operand" "") (match_operator 1 "compare_operator" [(and (match_operand 2 "nonimmediate_operand" "") (match_operand 3 "const_int_operand" "")) (const_int 0)]))] "reload_completed && GET_MODE (operands[2]) != QImode && (!REG_P (operands[2]) || ANY_QI_REG_P (operands[2])) && ((ix86_match_ccmode (insn, CCZmode) && !(INTVAL (operands[3]) & ~255)) || (ix86_match_ccmode (insn, CCNOmode) && !(INTVAL (operands[3]) & ~127)))" [(set (match_dup 0) (match_op_dup 1 [(and:QI (match_dup 2) (match_dup 3)) (const_int 0)]))] "operands[2] = gen_lowpart (QImode, operands[2]); operands[3] = gen_lowpart (QImode, operands[3]);") ;; %%% This used to optimize known byte-wide and operations to memory, ;; and sometimes to QImode registers. If this is considered useful, ;; it should be done with splitters. (define_expand "anddi3" [(set (match_operand:DI 0 "nonimmediate_operand" "") (and:DI (match_operand:DI 1 "nonimmediate_operand" "") (match_operand:DI 2 "x86_64_szext_general_operand" ""))) (clobber (reg:CC 17))] "TARGET_64BIT" "ix86_expand_binary_operator (AND, DImode, operands); DONE;") (define_insn "*anddi_1_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=r,rm,r,r") (and:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0,0,qm") (match_operand:DI 2 "x86_64_szext_general_operand" "Z,re,rm,L"))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (AND, DImode, operands)" { switch (get_attr_type (insn)) { case TYPE_IMOVX: { enum machine_mode mode; if (GET_CODE (operands[2]) != CONST_INT) abort (); if (INTVAL (operands[2]) == 0xff) mode = QImode; else if (INTVAL (operands[2]) == 0xffff) mode = HImode; else abort (); operands[1] = gen_lowpart (mode, operands[1]); if (mode == QImode) return "movz{bq|x}\t{%1,%0|%0, %1}"; else return "movz{wq|x}\t{%1,%0|%0, %1}"; } default: if (! rtx_equal_p (operands[0], operands[1])) abort (); if (get_attr_mode (insn) == MODE_SI) return "and{l}\t{%k2, %k0|%k0, %k2}"; else return "and{q}\t{%2, %0|%0, %2}"; } } [(set_attr "type" "alu,alu,alu,imovx") (set_attr "length_immediate" "*,*,*,0") (set_attr "mode" "SI,DI,DI,DI")]) (define_insn "*anddi_2" [(set (reg 17) (compare (and:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0,0") (match_operand:DI 2 "x86_64_szext_general_operand" "Z,rem,re")) (const_int 0))) (set (match_operand:DI 0 "nonimmediate_operand" "=r,r,rm") (and:DI (match_dup 1) (match_dup 2)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode) && ix86_binary_operator_ok (AND, DImode, operands)" "@ and{l}\t{%k2, %k0|%k0, %k2} and{q}\t{%2, %0|%0, %2} and{q}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI,DI,DI")]) (define_expand "andsi3" [(set (match_operand:SI 0 "nonimmediate_operand" "") (and:SI (match_operand:SI 1 "nonimmediate_operand" "") (match_operand:SI 2 "general_operand" ""))) (clobber (reg:CC 17))] "" "ix86_expand_binary_operator (AND, SImode, operands); DONE;") (define_insn "*andsi_1" [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r,r") (and:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,qm") (match_operand:SI 2 "general_operand" "ri,rm,L"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (AND, SImode, operands)" { switch (get_attr_type (insn)) { case TYPE_IMOVX: { enum machine_mode mode; if (GET_CODE (operands[2]) != CONST_INT) abort (); if (INTVAL (operands[2]) == 0xff) mode = QImode; else if (INTVAL (operands[2]) == 0xffff) mode = HImode; else abort (); operands[1] = gen_lowpart (mode, operands[1]); if (mode == QImode) return "movz{bl|x}\t{%1,%0|%0, %1}"; else return "movz{wl|x}\t{%1,%0|%0, %1}"; } default: if (! rtx_equal_p (operands[0], operands[1])) abort (); return "and{l}\t{%2, %0|%0, %2}"; } } [(set_attr "type" "alu,alu,imovx") (set_attr "length_immediate" "*,*,0") (set_attr "mode" "SI")]) (define_split [(set (match_operand 0 "register_operand" "") (and (match_dup 0) (const_int -65536))) (clobber (reg:CC 17))] "optimize_size || (TARGET_FAST_PREFIX && !TARGET_PARTIAL_REG_STALL)" [(set (strict_low_part (match_dup 1)) (const_int 0))] "operands[1] = gen_lowpart (HImode, operands[0]);") (define_split [(set (match_operand 0 "ext_register_operand" "") (and (match_dup 0) (const_int -256))) (clobber (reg:CC 17))] "(optimize_size || !TARGET_PARTIAL_REG_STALL) && reload_completed" [(set (strict_low_part (match_dup 1)) (const_int 0))] "operands[1] = gen_lowpart (QImode, operands[0]);") (define_split [(set (match_operand 0 "ext_register_operand" "") (and (match_dup 0) (const_int -65281))) (clobber (reg:CC 17))] "(optimize_size || !TARGET_PARTIAL_REG_STALL) && reload_completed" [(parallel [(set (zero_extract:SI (match_dup 0) (const_int 8) (const_int 8)) (xor:SI (zero_extract:SI (match_dup 0) (const_int 8) (const_int 8)) (zero_extract:SI (match_dup 0) (const_int 8) (const_int 8)))) (clobber (reg:CC 17))])] "operands[0] = gen_lowpart (SImode, operands[0]);") ;; See comment for addsi_1_zext why we do use nonimmediate_operand (define_insn "*andsi_1_zext" [(set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (and:SI (match_operand:SI 1 "nonimmediate_operand" "%0") (match_operand:SI 2 "general_operand" "rim")))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (AND, SImode, operands)" "and{l}\t{%2, %k0|%k0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) (define_insn "*andsi_2" [(set (reg 17) (compare (and:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") (match_operand:SI 2 "general_operand" "rim,ri")) (const_int 0))) (set (match_operand:SI 0 "nonimmediate_operand" "=r,rm") (and:SI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCNOmode) && ix86_binary_operator_ok (AND, SImode, operands)" "and{l}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) ;; See comment for addsi_1_zext why we do use nonimmediate_operand (define_insn "*andsi_2_zext" [(set (reg 17) (compare (and:SI (match_operand:SI 1 "nonimmediate_operand" "%0") (match_operand:SI 2 "general_operand" "rim")) (const_int 0))) (set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (and:SI (match_dup 1) (match_dup 2))))] "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode) && ix86_binary_operator_ok (AND, SImode, operands)" "and{l}\t{%2, %k0|%k0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) (define_expand "andhi3" [(set (match_operand:HI 0 "nonimmediate_operand" "") (and:HI (match_operand:HI 1 "nonimmediate_operand" "") (match_operand:HI 2 "general_operand" ""))) (clobber (reg:CC 17))] "TARGET_HIMODE_MATH" "ix86_expand_binary_operator (AND, HImode, operands); DONE;") (define_insn "*andhi_1" [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,r,r") (and:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0,qm") (match_operand:HI 2 "general_operand" "ri,rm,L"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (AND, HImode, operands)" { switch (get_attr_type (insn)) { case TYPE_IMOVX: if (GET_CODE (operands[2]) != CONST_INT) abort (); if (INTVAL (operands[2]) == 0xff) return "movz{bl|x}\t{%b1, %k0|%k0, %b1}"; abort (); default: if (! rtx_equal_p (operands[0], operands[1])) abort (); return "and{w}\t{%2, %0|%0, %2}"; } } [(set_attr "type" "alu,alu,imovx") (set_attr "length_immediate" "*,*,0") (set_attr "mode" "HI,HI,SI")]) (define_insn "*andhi_2" [(set (reg 17) (compare (and:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0") (match_operand:HI 2 "general_operand" "rim,ri")) (const_int 0))) (set (match_operand:HI 0 "nonimmediate_operand" "=r,rm") (and:HI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCNOmode) && ix86_binary_operator_ok (AND, HImode, operands)" "and{w}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "HI")]) (define_expand "andqi3" [(set (match_operand:QI 0 "nonimmediate_operand" "") (and:QI (match_operand:QI 1 "nonimmediate_operand" "") (match_operand:QI 2 "general_operand" ""))) (clobber (reg:CC 17))] "TARGET_QIMODE_MATH" "ix86_expand_binary_operator (AND, QImode, operands); DONE;") ;; %%% Potential partial reg stall on alternative 2. What to do? (define_insn "*andqi_1" [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,q,r") (and:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0") (match_operand:QI 2 "general_operand" "qi,qmi,ri"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (AND, QImode, operands)" "@ and{b}\t{%2, %0|%0, %2} and{b}\t{%2, %0|%0, %2} and{l}\t{%k2, %k0|%k0, %k2}" [(set_attr "type" "alu") (set_attr "mode" "QI,QI,SI")]) (define_insn "*andqi_1_slp" [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "+qm,q")) (and:QI (match_dup 0) (match_operand:QI 1 "general_operand" "qi,qmi"))) (clobber (reg:CC 17))] "(! TARGET_PARTIAL_REG_STALL || optimize_size) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "and{b}\t{%1, %0|%0, %1}" [(set_attr "type" "alu1") (set_attr "mode" "QI")]) (define_insn "*andqi_2_maybe_si" [(set (reg 17) (compare (and:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0") (match_operand:QI 2 "general_operand" "qim,qi,i")) (const_int 0))) (set (match_operand:QI 0 "nonimmediate_operand" "=q,qm,*r") (and:QI (match_dup 1) (match_dup 2)))] "ix86_binary_operator_ok (AND, QImode, operands) && ix86_match_ccmode (insn, GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) >= 0 ? CCNOmode : CCZmode)" { if (which_alternative == 2) { if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0) operands[2] = GEN_INT (INTVAL (operands[2]) & 0xff); return "and{l}\t{%2, %k0|%k0, %2}"; } return "and{b}\t{%2, %0|%0, %2}"; } [(set_attr "type" "alu") (set_attr "mode" "QI,QI,SI")]) (define_insn "*andqi_2" [(set (reg 17) (compare (and:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0") (match_operand:QI 2 "general_operand" "qim,qi")) (const_int 0))) (set (match_operand:QI 0 "nonimmediate_operand" "=q,qm") (and:QI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCNOmode) && ix86_binary_operator_ok (AND, QImode, operands)" "and{b}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "QI")]) (define_insn "*andqi_2_slp" [(set (reg 17) (compare (and:QI (match_operand:QI 0 "nonimmediate_operand" "+q,qm") (match_operand:QI 1 "nonimmediate_operand" "qmi,qi")) (const_int 0))) (set (strict_low_part (match_dup 0)) (and:QI (match_dup 0) (match_dup 1)))] "(! TARGET_PARTIAL_REG_STALL || optimize_size) && ix86_match_ccmode (insn, CCNOmode) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "and{b}\t{%1, %0|%0, %1}" [(set_attr "type" "alu1") (set_attr "mode" "QI")]) ;; ??? A bug in recog prevents it from recognizing a const_int as an ;; operand to zero_extend in andqi_ext_1. It was checking explicitly ;; for a QImode operand, which of course failed. (define_insn "andqi_ext_0" [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "=Q") (const_int 8) (const_int 8)) (and:SI (zero_extract:SI (match_operand 1 "ext_register_operand" "0") (const_int 8) (const_int 8)) (match_operand 2 "const_int_operand" "n"))) (clobber (reg:CC 17))] "" "and{b}\t{%2, %h0|%h0, %2}" [(set_attr "type" "alu") (set_attr "length_immediate" "1") (set_attr "mode" "QI")]) ;; Generated by peephole translating test to and. This shows up ;; often in fp comparisons. (define_insn "*andqi_ext_0_cc" [(set (reg 17) (compare (and:SI (zero_extract:SI (match_operand 1 "ext_register_operand" "0") (const_int 8) (const_int 8)) (match_operand 2 "const_int_operand" "n")) (const_int 0))) (set (zero_extract:SI (match_operand 0 "ext_register_operand" "=Q") (const_int 8) (const_int 8)) (and:SI (zero_extract:SI (match_dup 1) (const_int 8) (const_int 8)) (match_dup 2)))] "ix86_match_ccmode (insn, CCNOmode)" "and{b}\t{%2, %h0|%h0, %2}" [(set_attr "type" "alu") (set_attr "length_immediate" "1") (set_attr "mode" "QI")]) (define_insn "*andqi_ext_1" [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "=Q") (const_int 8) (const_int 8)) (and:SI (zero_extract:SI (match_operand 1 "ext_register_operand" "0") (const_int 8) (const_int 8)) (zero_extend:SI (match_operand:QI 2 "general_operand" "Qm")))) (clobber (reg:CC 17))] "!TARGET_64BIT" "and{b}\t{%2, %h0|%h0, %2}" [(set_attr "type" "alu") (set_attr "length_immediate" "0") (set_attr "mode" "QI")]) (define_insn "*andqi_ext_1_rex64" [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "=Q") (const_int 8) (const_int 8)) (and:SI (zero_extract:SI (match_operand 1 "ext_register_operand" "0") (const_int 8) (const_int 8)) (zero_extend:SI (match_operand 2 "ext_register_operand" "Q")))) (clobber (reg:CC 17))] "TARGET_64BIT" "and{b}\t{%2, %h0|%h0, %2}" [(set_attr "type" "alu") (set_attr "length_immediate" "0") (set_attr "mode" "QI")]) (define_insn "*andqi_ext_2" [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "=Q") (const_int 8) (const_int 8)) (and:SI (zero_extract:SI (match_operand 1 "ext_register_operand" "%0") (const_int 8) (const_int 8)) (zero_extract:SI (match_operand 2 "ext_register_operand" "Q") (const_int 8) (const_int 8)))) (clobber (reg:CC 17))] "" "and{b}\t{%h2, %h0|%h0, %h2}" [(set_attr "type" "alu") (set_attr "length_immediate" "0") (set_attr "mode" "QI")]) ;; Convert wide AND instructions with immediate operand to shorter QImode ;; equivalents when possible. ;; Don't do the splitting with memory operands, since it introduces risk ;; of memory mismatch stalls. We may want to do the splitting for optimizing ;; for size, but that can (should?) be handled by generic code instead. (define_split [(set (match_operand 0 "register_operand" "") (and (match_operand 1 "register_operand" "") (match_operand 2 "const_int_operand" ""))) (clobber (reg:CC 17))] "reload_completed && QI_REG_P (operands[0]) && (!TARGET_PARTIAL_REG_STALL || optimize_size) && !(~INTVAL (operands[2]) & ~(255 << 8)) && GET_MODE (operands[0]) != QImode" [(parallel [(set (zero_extract:SI (match_dup 0) (const_int 8) (const_int 8)) (and:SI (zero_extract:SI (match_dup 1) (const_int 8) (const_int 8)) (match_dup 2))) (clobber (reg:CC 17))])] "operands[0] = gen_lowpart (SImode, operands[0]); operands[1] = gen_lowpart (SImode, operands[1]); operands[2] = gen_int_mode ((INTVAL (operands[2]) >> 8) & 0xff, SImode);") ;; Since AND can be encoded with sign extended immediate, this is only ;; profitable when 7th bit is not set. (define_split [(set (match_operand 0 "register_operand" "") (and (match_operand 1 "general_operand" "") (match_operand 2 "const_int_operand" ""))) (clobber (reg:CC 17))] "reload_completed && ANY_QI_REG_P (operands[0]) && (!TARGET_PARTIAL_REG_STALL || optimize_size) && !(~INTVAL (operands[2]) & ~255) && !(INTVAL (operands[2]) & 128) && GET_MODE (operands[0]) != QImode" [(parallel [(set (strict_low_part (match_dup 0)) (and:QI (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))])] "operands[0] = gen_lowpart (QImode, operands[0]); operands[1] = gen_lowpart (QImode, operands[1]); operands[2] = gen_lowpart (QImode, operands[2]);") ;; Logical inclusive OR instructions ;; %%% This used to optimize known byte-wide and operations to memory. ;; If this is considered useful, it should be done with splitters. (define_expand "iordi3" [(set (match_operand:DI 0 "nonimmediate_operand" "") (ior:DI (match_operand:DI 1 "nonimmediate_operand" "") (match_operand:DI 2 "x86_64_general_operand" ""))) (clobber (reg:CC 17))] "TARGET_64BIT" "ix86_expand_binary_operator (IOR, DImode, operands); DONE;") (define_insn "*iordi_1_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=rm,r") (ior:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") (match_operand:DI 2 "x86_64_general_operand" "re,rme"))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (IOR, DImode, operands)" "or{q}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "DI")]) (define_insn "*iordi_2_rex64" [(set (reg 17) (compare (ior:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") (match_operand:DI 2 "x86_64_general_operand" "rem,re")) (const_int 0))) (set (match_operand:DI 0 "nonimmediate_operand" "=r,rm") (ior:DI (match_dup 1) (match_dup 2)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode) && ix86_binary_operator_ok (IOR, DImode, operands)" "or{q}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "DI")]) (define_insn "*iordi_3_rex64" [(set (reg 17) (compare (ior:DI (match_operand:DI 1 "nonimmediate_operand" "%0") (match_operand:DI 2 "x86_64_general_operand" "rem")) (const_int 0))) (clobber (match_scratch:DI 0 "=r"))] "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode) && ix86_binary_operator_ok (IOR, DImode, operands)" "or{q}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "DI")]) (define_expand "iorsi3" [(set (match_operand:SI 0 "nonimmediate_operand" "") (ior:SI (match_operand:SI 1 "nonimmediate_operand" "") (match_operand:SI 2 "general_operand" ""))) (clobber (reg:CC 17))] "" "ix86_expand_binary_operator (IOR, SImode, operands); DONE;") (define_insn "*iorsi_1" [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r") (ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") (match_operand:SI 2 "general_operand" "ri,rmi"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (IOR, SImode, operands)" "or{l}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) ;; See comment for addsi_1_zext why we do use nonimmediate_operand (define_insn "*iorsi_1_zext" [(set (match_operand:DI 0 "register_operand" "=rm") (zero_extend:DI (ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0") (match_operand:SI 2 "general_operand" "rim")))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (IOR, SImode, operands)" "or{l}\t{%2, %k0|%k0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) (define_insn "*iorsi_1_zext_imm" [(set (match_operand:DI 0 "register_operand" "=rm") (ior:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "%0")) (match_operand:DI 2 "x86_64_zext_immediate_operand" "Z"))) (clobber (reg:CC 17))] "TARGET_64BIT" "or{l}\t{%2, %k0|%k0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) (define_insn "*iorsi_2" [(set (reg 17) (compare (ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") (match_operand:SI 2 "general_operand" "rim,ri")) (const_int 0))) (set (match_operand:SI 0 "nonimmediate_operand" "=r,rm") (ior:SI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCNOmode) && ix86_binary_operator_ok (IOR, SImode, operands)" "or{l}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) ;; See comment for addsi_1_zext why we do use nonimmediate_operand ;; ??? Special case for immediate operand is missing - it is tricky. (define_insn "*iorsi_2_zext" [(set (reg 17) (compare (ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0") (match_operand:SI 2 "general_operand" "rim")) (const_int 0))) (set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (ior:SI (match_dup 1) (match_dup 2))))] "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode) && ix86_binary_operator_ok (IOR, SImode, operands)" "or{l}\t{%2, %k0|%k0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) (define_insn "*iorsi_2_zext_imm" [(set (reg 17) (compare (ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0") (match_operand 2 "x86_64_zext_immediate_operand" "Z")) (const_int 0))) (set (match_operand:DI 0 "register_operand" "=r") (ior:DI (zero_extend:DI (match_dup 1)) (match_dup 2)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode) && ix86_binary_operator_ok (IOR, SImode, operands)" "or{l}\t{%2, %k0|%k0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) (define_insn "*iorsi_3" [(set (reg 17) (compare (ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0") (match_operand:SI 2 "general_operand" "rim")) (const_int 0))) (clobber (match_scratch:SI 0 "=r"))] "ix86_match_ccmode (insn, CCNOmode) && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "or{l}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) (define_expand "iorhi3" [(set (match_operand:HI 0 "nonimmediate_operand" "") (ior:HI (match_operand:HI 1 "nonimmediate_operand" "") (match_operand:HI 2 "general_operand" ""))) (clobber (reg:CC 17))] "TARGET_HIMODE_MATH" "ix86_expand_binary_operator (IOR, HImode, operands); DONE;") (define_insn "*iorhi_1" [(set (match_operand:HI 0 "nonimmediate_operand" "=r,m") (ior:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0") (match_operand:HI 2 "general_operand" "rmi,ri"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (IOR, HImode, operands)" "or{w}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "HI")]) (define_insn "*iorhi_2" [(set (reg 17) (compare (ior:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0") (match_operand:HI 2 "general_operand" "rim,ri")) (const_int 0))) (set (match_operand:HI 0 "nonimmediate_operand" "=r,rm") (ior:HI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCNOmode) && ix86_binary_operator_ok (IOR, HImode, operands)" "or{w}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "HI")]) (define_insn "*iorhi_3" [(set (reg 17) (compare (ior:HI (match_operand:HI 1 "nonimmediate_operand" "%0") (match_operand:HI 2 "general_operand" "rim")) (const_int 0))) (clobber (match_scratch:HI 0 "=r"))] "ix86_match_ccmode (insn, CCNOmode) && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "or{w}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "HI")]) (define_expand "iorqi3" [(set (match_operand:QI 0 "nonimmediate_operand" "") (ior:QI (match_operand:QI 1 "nonimmediate_operand" "") (match_operand:QI 2 "general_operand" ""))) (clobber (reg:CC 17))] "TARGET_QIMODE_MATH" "ix86_expand_binary_operator (IOR, QImode, operands); DONE;") ;; %%% Potential partial reg stall on alternative 2. What to do? (define_insn "*iorqi_1" [(set (match_operand:QI 0 "nonimmediate_operand" "=q,m,r") (ior:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0") (match_operand:QI 2 "general_operand" "qmi,qi,ri"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (IOR, QImode, operands)" "@ or{b}\t{%2, %0|%0, %2} or{b}\t{%2, %0|%0, %2} or{l}\t{%k2, %k0|%k0, %k2}" [(set_attr "type" "alu") (set_attr "mode" "QI,QI,SI")]) (define_insn "*iorqi_1_slp" [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "+q,m")) (ior:QI (match_dup 0) (match_operand:QI 1 "general_operand" "qmi,qi"))) (clobber (reg:CC 17))] "(! TARGET_PARTIAL_REG_STALL || optimize_size) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "or{b}\t{%1, %0|%0, %1}" [(set_attr "type" "alu1") (set_attr "mode" "QI")]) (define_insn "*iorqi_2" [(set (reg 17) (compare (ior:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0") (match_operand:QI 2 "general_operand" "qim,qi")) (const_int 0))) (set (match_operand:QI 0 "nonimmediate_operand" "=q,qm") (ior:QI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCNOmode) && ix86_binary_operator_ok (IOR, QImode, operands)" "or{b}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "QI")]) (define_insn "*iorqi_2_slp" [(set (reg 17) (compare (ior:QI (match_operand:QI 0 "nonimmediate_operand" "+q,qm") (match_operand:QI 1 "general_operand" "qim,qi")) (const_int 0))) (set (strict_low_part (match_dup 0)) (ior:QI (match_dup 0) (match_dup 1)))] "(! TARGET_PARTIAL_REG_STALL || optimize_size) && ix86_match_ccmode (insn, CCNOmode) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "or{b}\t{%1, %0|%0, %1}" [(set_attr "type" "alu1") (set_attr "mode" "QI")]) (define_insn "*iorqi_3" [(set (reg 17) (compare (ior:QI (match_operand:QI 1 "nonimmediate_operand" "%0") (match_operand:QI 2 "general_operand" "qim")) (const_int 0))) (clobber (match_scratch:QI 0 "=q"))] "ix86_match_ccmode (insn, CCNOmode) && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "or{b}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "QI")]) (define_insn "iorqi_ext_0" [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "=Q") (const_int 8) (const_int 8)) (ior:SI (zero_extract:SI (match_operand 1 "ext_register_operand" "0") (const_int 8) (const_int 8)) (match_operand 2 "const_int_operand" "n"))) (clobber (reg:CC 17))] "(!TARGET_PARTIAL_REG_STALL || optimize_size)" "or{b}\t{%2, %h0|%h0, %2}" [(set_attr "type" "alu") (set_attr "length_immediate" "1") (set_attr "mode" "QI")]) (define_insn "*iorqi_ext_1" [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "=Q") (const_int 8) (const_int 8)) (ior:SI (zero_extract:SI (match_operand 1 "ext_register_operand" "0") (const_int 8) (const_int 8)) (zero_extend:SI (match_operand:QI 2 "general_operand" "Qm")))) (clobber (reg:CC 17))] "!TARGET_64BIT && (!TARGET_PARTIAL_REG_STALL || optimize_size)" "or{b}\t{%2, %h0|%h0, %2}" [(set_attr "type" "alu") (set_attr "length_immediate" "0") (set_attr "mode" "QI")]) (define_insn "*iorqi_ext_1_rex64" [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "=Q") (const_int 8) (const_int 8)) (ior:SI (zero_extract:SI (match_operand 1 "ext_register_operand" "0") (const_int 8) (const_int 8)) (zero_extend:SI (match_operand 2 "ext_register_operand" "Q")))) (clobber (reg:CC 17))] "TARGET_64BIT && (!TARGET_PARTIAL_REG_STALL || optimize_size)" "or{b}\t{%2, %h0|%h0, %2}" [(set_attr "type" "alu") (set_attr "length_immediate" "0") (set_attr "mode" "QI")]) (define_insn "*iorqi_ext_2" [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "=Q") (const_int 8) (const_int 8)) (ior:SI (zero_extract:SI (match_operand 1 "ext_register_operand" "0") (const_int 8) (const_int 8)) (zero_extract:SI (match_operand 2 "ext_register_operand" "Q") (const_int 8) (const_int 8)))) (clobber (reg:CC 17))] "(!TARGET_PARTIAL_REG_STALL || optimize_size)" "ior{b}\t{%h2, %h0|%h0, %h2}" [(set_attr "type" "alu") (set_attr "length_immediate" "0") (set_attr "mode" "QI")]) (define_split [(set (match_operand 0 "register_operand" "") (ior (match_operand 1 "register_operand" "") (match_operand 2 "const_int_operand" ""))) (clobber (reg:CC 17))] "reload_completed && QI_REG_P (operands[0]) && (!TARGET_PARTIAL_REG_STALL || optimize_size) && !(INTVAL (operands[2]) & ~(255 << 8)) && GET_MODE (operands[0]) != QImode" [(parallel [(set (zero_extract:SI (match_dup 0) (const_int 8) (const_int 8)) (ior:SI (zero_extract:SI (match_dup 1) (const_int 8) (const_int 8)) (match_dup 2))) (clobber (reg:CC 17))])] "operands[0] = gen_lowpart (SImode, operands[0]); operands[1] = gen_lowpart (SImode, operands[1]); operands[2] = gen_int_mode ((INTVAL (operands[2]) >> 8) & 0xff, SImode);") ;; Since OR can be encoded with sign extended immediate, this is only ;; profitable when 7th bit is set. (define_split [(set (match_operand 0 "register_operand" "") (ior (match_operand 1 "general_operand" "") (match_operand 2 "const_int_operand" ""))) (clobber (reg:CC 17))] "reload_completed && ANY_QI_REG_P (operands[0]) && (!TARGET_PARTIAL_REG_STALL || optimize_size) && !(INTVAL (operands[2]) & ~255) && (INTVAL (operands[2]) & 128) && GET_MODE (operands[0]) != QImode" [(parallel [(set (strict_low_part (match_dup 0)) (ior:QI (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))])] "operands[0] = gen_lowpart (QImode, operands[0]); operands[1] = gen_lowpart (QImode, operands[1]); operands[2] = gen_lowpart (QImode, operands[2]);") ;; Logical XOR instructions ;; %%% This used to optimize known byte-wide and operations to memory. ;; If this is considered useful, it should be done with splitters. (define_expand "xordi3" [(set (match_operand:DI 0 "nonimmediate_operand" "") (xor:DI (match_operand:DI 1 "nonimmediate_operand" "") (match_operand:DI 2 "x86_64_general_operand" ""))) (clobber (reg:CC 17))] "TARGET_64BIT" "ix86_expand_binary_operator (XOR, DImode, operands); DONE;") (define_insn "*xordi_1_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=rm,r") (xor:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") (match_operand:DI 2 "x86_64_general_operand" "re,rm"))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (XOR, DImode, operands)" "@ xor{q}\t{%2, %0|%0, %2} xor{q}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "DI,DI")]) (define_insn "*xordi_2_rex64" [(set (reg 17) (compare (xor:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") (match_operand:DI 2 "x86_64_general_operand" "rem,re")) (const_int 0))) (set (match_operand:DI 0 "nonimmediate_operand" "=r,rm") (xor:DI (match_dup 1) (match_dup 2)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode) && ix86_binary_operator_ok (XOR, DImode, operands)" "@ xor{q}\t{%2, %0|%0, %2} xor{q}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "DI,DI")]) (define_insn "*xordi_3_rex64" [(set (reg 17) (compare (xor:DI (match_operand:DI 1 "nonimmediate_operand" "%0") (match_operand:DI 2 "x86_64_general_operand" "rem")) (const_int 0))) (clobber (match_scratch:DI 0 "=r"))] "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode) && ix86_binary_operator_ok (XOR, DImode, operands)" "xor{q}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "DI")]) (define_expand "xorsi3" [(set (match_operand:SI 0 "nonimmediate_operand" "") (xor:SI (match_operand:SI 1 "nonimmediate_operand" "") (match_operand:SI 2 "general_operand" ""))) (clobber (reg:CC 17))] "" "ix86_expand_binary_operator (XOR, SImode, operands); DONE;") (define_insn "*xorsi_1" [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r") (xor:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") (match_operand:SI 2 "general_operand" "ri,rm"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (XOR, SImode, operands)" "xor{l}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) ;; See comment for addsi_1_zext why we do use nonimmediate_operand ;; Add speccase for immediates (define_insn "*xorsi_1_zext" [(set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (xor:SI (match_operand:SI 1 "nonimmediate_operand" "%0") (match_operand:SI 2 "general_operand" "rim")))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (XOR, SImode, operands)" "xor{l}\t{%2, %k0|%k0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) (define_insn "*xorsi_1_zext_imm" [(set (match_operand:DI 0 "register_operand" "=r") (xor:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "%0")) (match_operand:DI 2 "x86_64_zext_immediate_operand" "Z"))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (XOR, SImode, operands)" "xor{l}\t{%2, %k0|%k0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) (define_insn "*xorsi_2" [(set (reg 17) (compare (xor:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") (match_operand:SI 2 "general_operand" "rim,ri")) (const_int 0))) (set (match_operand:SI 0 "nonimmediate_operand" "=r,rm") (xor:SI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCNOmode) && ix86_binary_operator_ok (XOR, SImode, operands)" "xor{l}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) ;; See comment for addsi_1_zext why we do use nonimmediate_operand ;; ??? Special case for immediate operand is missing - it is tricky. (define_insn "*xorsi_2_zext" [(set (reg 17) (compare (xor:SI (match_operand:SI 1 "nonimmediate_operand" "%0") (match_operand:SI 2 "general_operand" "rim")) (const_int 0))) (set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (xor:SI (match_dup 1) (match_dup 2))))] "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode) && ix86_binary_operator_ok (XOR, SImode, operands)" "xor{l}\t{%2, %k0|%k0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) (define_insn "*xorsi_2_zext_imm" [(set (reg 17) (compare (xor:SI (match_operand:SI 1 "nonimmediate_operand" "%0") (match_operand 2 "x86_64_zext_immediate_operand" "Z")) (const_int 0))) (set (match_operand:DI 0 "register_operand" "=r") (xor:DI (zero_extend:DI (match_dup 1)) (match_dup 2)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode) && ix86_binary_operator_ok (XOR, SImode, operands)" "xor{l}\t{%2, %k0|%k0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) (define_insn "*xorsi_3" [(set (reg 17) (compare (xor:SI (match_operand:SI 1 "nonimmediate_operand" "%0") (match_operand:SI 2 "general_operand" "rim")) (const_int 0))) (clobber (match_scratch:SI 0 "=r"))] "ix86_match_ccmode (insn, CCNOmode) && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "xor{l}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "SI")]) (define_expand "xorhi3" [(set (match_operand:HI 0 "nonimmediate_operand" "") (xor:HI (match_operand:HI 1 "nonimmediate_operand" "") (match_operand:HI 2 "general_operand" ""))) (clobber (reg:CC 17))] "TARGET_HIMODE_MATH" "ix86_expand_binary_operator (XOR, HImode, operands); DONE;") (define_insn "*xorhi_1" [(set (match_operand:HI 0 "nonimmediate_operand" "=r,m") (xor:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0") (match_operand:HI 2 "general_operand" "rmi,ri"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (XOR, HImode, operands)" "xor{w}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "HI")]) (define_insn "*xorhi_2" [(set (reg 17) (compare (xor:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0") (match_operand:HI 2 "general_operand" "rim,ri")) (const_int 0))) (set (match_operand:HI 0 "nonimmediate_operand" "=r,rm") (xor:HI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCNOmode) && ix86_binary_operator_ok (XOR, HImode, operands)" "xor{w}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "HI")]) (define_insn "*xorhi_3" [(set (reg 17) (compare (xor:HI (match_operand:HI 1 "nonimmediate_operand" "%0") (match_operand:HI 2 "general_operand" "rim")) (const_int 0))) (clobber (match_scratch:HI 0 "=r"))] "ix86_match_ccmode (insn, CCNOmode) && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "xor{w}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "HI")]) (define_expand "xorqi3" [(set (match_operand:QI 0 "nonimmediate_operand" "") (xor:QI (match_operand:QI 1 "nonimmediate_operand" "") (match_operand:QI 2 "general_operand" ""))) (clobber (reg:CC 17))] "TARGET_QIMODE_MATH" "ix86_expand_binary_operator (XOR, QImode, operands); DONE;") ;; %%% Potential partial reg stall on alternative 2. What to do? (define_insn "*xorqi_1" [(set (match_operand:QI 0 "nonimmediate_operand" "=q,m,r") (xor:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0") (match_operand:QI 2 "general_operand" "qmi,qi,ri"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (XOR, QImode, operands)" "@ xor{b}\t{%2, %0|%0, %2} xor{b}\t{%2, %0|%0, %2} xor{l}\t{%k2, %k0|%k0, %k2}" [(set_attr "type" "alu") (set_attr "mode" "QI,QI,SI")]) (define_insn "*xorqi_1_slp" [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "+qm,q")) (xor:QI (match_dup 0) (match_operand:QI 1 "general_operand" "qi,qmi"))) (clobber (reg:CC 17))] "(! TARGET_PARTIAL_REG_STALL || optimize_size) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "xor{b}\t{%1, %0|%0, %1}" [(set_attr "type" "alu1") (set_attr "mode" "QI")]) (define_insn "xorqi_ext_0" [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "=Q") (const_int 8) (const_int 8)) (xor:SI (zero_extract:SI (match_operand 1 "ext_register_operand" "0") (const_int 8) (const_int 8)) (match_operand 2 "const_int_operand" "n"))) (clobber (reg:CC 17))] "(!TARGET_PARTIAL_REG_STALL || optimize_size)" "xor{b}\t{%2, %h0|%h0, %2}" [(set_attr "type" "alu") (set_attr "length_immediate" "1") (set_attr "mode" "QI")]) (define_insn "*xorqi_ext_1" [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "=Q") (const_int 8) (const_int 8)) (xor:SI (zero_extract:SI (match_operand 1 "ext_register_operand" "0") (const_int 8) (const_int 8)) (zero_extend:SI (match_operand:QI 2 "general_operand" "Qm")))) (clobber (reg:CC 17))] "!TARGET_64BIT && (!TARGET_PARTIAL_REG_STALL || optimize_size)" "xor{b}\t{%2, %h0|%h0, %2}" [(set_attr "type" "alu") (set_attr "length_immediate" "0") (set_attr "mode" "QI")]) (define_insn "*xorqi_ext_1_rex64" [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "=Q") (const_int 8) (const_int 8)) (xor:SI (zero_extract:SI (match_operand 1 "ext_register_operand" "0") (const_int 8) (const_int 8)) (zero_extend:SI (match_operand 2 "ext_register_operand" "Q")))) (clobber (reg:CC 17))] "TARGET_64BIT && (!TARGET_PARTIAL_REG_STALL || optimize_size)" "xor{b}\t{%2, %h0|%h0, %2}" [(set_attr "type" "alu") (set_attr "length_immediate" "0") (set_attr "mode" "QI")]) (define_insn "*xorqi_ext_2" [(set (zero_extract:SI (match_operand 0 "ext_register_operand" "=Q") (const_int 8) (const_int 8)) (xor:SI (zero_extract:SI (match_operand 1 "ext_register_operand" "0") (const_int 8) (const_int 8)) (zero_extract:SI (match_operand 2 "ext_register_operand" "Q") (const_int 8) (const_int 8)))) (clobber (reg:CC 17))] "(!TARGET_PARTIAL_REG_STALL || optimize_size)" "xor{b}\t{%h2, %h0|%h0, %h2}" [(set_attr "type" "alu") (set_attr "length_immediate" "0") (set_attr "mode" "QI")]) (define_insn "*xorqi_cc_1" [(set (reg 17) (compare (xor:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0") (match_operand:QI 2 "general_operand" "qim,qi")) (const_int 0))) (set (match_operand:QI 0 "nonimmediate_operand" "=q,qm") (xor:QI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCNOmode) && ix86_binary_operator_ok (XOR, QImode, operands)" "xor{b}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "QI")]) (define_insn "*xorqi_2_slp" [(set (reg 17) (compare (xor:QI (match_operand:QI 0 "nonimmediate_operand" "+q,qm") (match_operand:QI 1 "general_operand" "qim,qi")) (const_int 0))) (set (strict_low_part (match_dup 0)) (xor:QI (match_dup 0) (match_dup 1)))] "(! TARGET_PARTIAL_REG_STALL || optimize_size) && ix86_match_ccmode (insn, CCNOmode) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "xor{b}\t{%1, %0|%0, %1}" [(set_attr "type" "alu1") (set_attr "mode" "QI")]) (define_insn "*xorqi_cc_2" [(set (reg 17) (compare (xor:QI (match_operand:QI 1 "nonimmediate_operand" "%0") (match_operand:QI 2 "general_operand" "qim")) (const_int 0))) (clobber (match_scratch:QI 0 "=q"))] "ix86_match_ccmode (insn, CCNOmode) && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "xor{b}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "QI")]) (define_insn "*xorqi_cc_ext_1" [(set (reg 17) (compare (xor:SI (zero_extract:SI (match_operand 1 "ext_register_operand" "0") (const_int 8) (const_int 8)) (match_operand:QI 2 "general_operand" "qmn")) (const_int 0))) (set (zero_extract:SI (match_operand 0 "ext_register_operand" "=q") (const_int 8) (const_int 8)) (xor:SI (zero_extract:SI (match_dup 1) (const_int 8) (const_int 8)) (match_dup 2)))] "!TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode)" "xor{b}\t{%2, %h0|%h0, %2}" [(set_attr "type" "alu") (set_attr "mode" "QI")]) (define_insn "*xorqi_cc_ext_1_rex64" [(set (reg 17) (compare (xor:SI (zero_extract:SI (match_operand 1 "ext_register_operand" "0") (const_int 8) (const_int 8)) (match_operand:QI 2 "nonmemory_operand" "Qn")) (const_int 0))) (set (zero_extract:SI (match_operand 0 "ext_register_operand" "=Q") (const_int 8) (const_int 8)) (xor:SI (zero_extract:SI (match_dup 1) (const_int 8) (const_int 8)) (match_dup 2)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode)" "xor{b}\t{%2, %h0|%h0, %2}" [(set_attr "type" "alu") (set_attr "mode" "QI")]) (define_expand "xorqi_cc_ext_1" [(parallel [ (set (reg:CCNO 17) (compare:CCNO (xor:SI (zero_extract:SI (match_operand 1 "ext_register_operand" "") (const_int 8) (const_int 8)) (match_operand:QI 2 "general_operand" "")) (const_int 0))) (set (zero_extract:SI (match_operand 0 "ext_register_operand" "") (const_int 8) (const_int 8)) (xor:SI (zero_extract:SI (match_dup 1) (const_int 8) (const_int 8)) (match_dup 2)))])] "" "") (define_split [(set (match_operand 0 "register_operand" "") (xor (match_operand 1 "register_operand" "") (match_operand 2 "const_int_operand" ""))) (clobber (reg:CC 17))] "reload_completed && QI_REG_P (operands[0]) && (!TARGET_PARTIAL_REG_STALL || optimize_size) && !(INTVAL (operands[2]) & ~(255 << 8)) && GET_MODE (operands[0]) != QImode" [(parallel [(set (zero_extract:SI (match_dup 0) (const_int 8) (const_int 8)) (xor:SI (zero_extract:SI (match_dup 1) (const_int 8) (const_int 8)) (match_dup 2))) (clobber (reg:CC 17))])] "operands[0] = gen_lowpart (SImode, operands[0]); operands[1] = gen_lowpart (SImode, operands[1]); operands[2] = gen_int_mode ((INTVAL (operands[2]) >> 8) & 0xff, SImode);") ;; Since XOR can be encoded with sign extended immediate, this is only ;; profitable when 7th bit is set. (define_split [(set (match_operand 0 "register_operand" "") (xor (match_operand 1 "general_operand" "") (match_operand 2 "const_int_operand" ""))) (clobber (reg:CC 17))] "reload_completed && ANY_QI_REG_P (operands[0]) && (!TARGET_PARTIAL_REG_STALL || optimize_size) && !(INTVAL (operands[2]) & ~255) && (INTVAL (operands[2]) & 128) && GET_MODE (operands[0]) != QImode" [(parallel [(set (strict_low_part (match_dup 0)) (xor:QI (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))])] "operands[0] = gen_lowpart (QImode, operands[0]); operands[1] = gen_lowpart (QImode, operands[1]); operands[2] = gen_lowpart (QImode, operands[2]);") ;; Negation instructions (define_expand "negdi2" [(parallel [(set (match_operand:DI 0 "nonimmediate_operand" "") (neg:DI (match_operand:DI 1 "nonimmediate_operand" ""))) (clobber (reg:CC 17))])] "" "ix86_expand_unary_operator (NEG, DImode, operands); DONE;") (define_insn "*negdi2_1" [(set (match_operand:DI 0 "nonimmediate_operand" "=ro") (neg:DI (match_operand:DI 1 "general_operand" "0"))) (clobber (reg:CC 17))] "!TARGET_64BIT && ix86_unary_operator_ok (NEG, DImode, operands)" "#") (define_split [(set (match_operand:DI 0 "nonimmediate_operand" "") (neg:DI (match_operand:DI 1 "general_operand" ""))) (clobber (reg:CC 17))] "!TARGET_64BIT && reload_completed" [(parallel [(set (reg:CCZ 17) (compare:CCZ (neg:SI (match_dup 2)) (const_int 0))) (set (match_dup 0) (neg:SI (match_dup 2)))]) (parallel [(set (match_dup 1) (plus:SI (plus:SI (ltu:SI (reg:CC 17) (const_int 0)) (match_dup 3)) (const_int 0))) (clobber (reg:CC 17))]) (parallel [(set (match_dup 1) (neg:SI (match_dup 1))) (clobber (reg:CC 17))])] "split_di (operands+1, 1, operands+2, operands+3); split_di (operands+0, 1, operands+0, operands+1);") (define_insn "*negdi2_1_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=rm") (neg:DI (match_operand:DI 1 "nonimmediate_operand" "0"))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_unary_operator_ok (NEG, DImode, operands)" "neg{q}\t%0" [(set_attr "type" "negnot") (set_attr "mode" "DI")]) ;; The problem with neg is that it does not perform (compare x 0), ;; it really performs (compare 0 x), which leaves us with the zero ;; flag being the only useful item. (define_insn "*negdi2_cmpz_rex64" [(set (reg:CCZ 17) (compare:CCZ (neg:DI (match_operand:DI 1 "nonimmediate_operand" "0")) (const_int 0))) (set (match_operand:DI 0 "nonimmediate_operand" "=rm") (neg:DI (match_dup 1)))] "TARGET_64BIT && ix86_unary_operator_ok (NEG, DImode, operands)" "neg{q}\t%0" [(set_attr "type" "negnot") (set_attr "mode" "DI")]) (define_expand "negsi2" [(parallel [(set (match_operand:SI 0 "nonimmediate_operand" "") (neg:SI (match_operand:SI 1 "nonimmediate_operand" ""))) (clobber (reg:CC 17))])] "" "ix86_expand_unary_operator (NEG, SImode, operands); DONE;") (define_insn "*negsi2_1" [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") (neg:SI (match_operand:SI 1 "nonimmediate_operand" "0"))) (clobber (reg:CC 17))] "ix86_unary_operator_ok (NEG, SImode, operands)" "neg{l}\t%0" [(set_attr "type" "negnot") (set_attr "mode" "SI")]) ;; Combine is quite creative about this pattern. (define_insn "*negsi2_1_zext" [(set (match_operand:DI 0 "register_operand" "=r") (lshiftrt:DI (neg:DI (ashift:DI (match_operand:DI 1 "register_operand" "0") (const_int 32))) (const_int 32))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_unary_operator_ok (NEG, SImode, operands)" "neg{l}\t%k0" [(set_attr "type" "negnot") (set_attr "mode" "SI")]) ;; The problem with neg is that it does not perform (compare x 0), ;; it really performs (compare 0 x), which leaves us with the zero ;; flag being the only useful item. (define_insn "*negsi2_cmpz" [(set (reg:CCZ 17) (compare:CCZ (neg:SI (match_operand:SI 1 "nonimmediate_operand" "0")) (const_int 0))) (set (match_operand:SI 0 "nonimmediate_operand" "=rm") (neg:SI (match_dup 1)))] "ix86_unary_operator_ok (NEG, SImode, operands)" "neg{l}\t%0" [(set_attr "type" "negnot") (set_attr "mode" "SI")]) (define_insn "*negsi2_cmpz_zext" [(set (reg:CCZ 17) (compare:CCZ (lshiftrt:DI (neg:DI (ashift:DI (match_operand:DI 1 "register_operand" "0") (const_int 32))) (const_int 32)) (const_int 0))) (set (match_operand:DI 0 "register_operand" "=r") (lshiftrt:DI (neg:DI (ashift:DI (match_dup 1) (const_int 32))) (const_int 32)))] "TARGET_64BIT && ix86_unary_operator_ok (NEG, SImode, operands)" "neg{l}\t%k0" [(set_attr "type" "negnot") (set_attr "mode" "SI")]) (define_expand "neghi2" [(parallel [(set (match_operand:HI 0 "nonimmediate_operand" "") (neg:HI (match_operand:HI 1 "nonimmediate_operand" ""))) (clobber (reg:CC 17))])] "TARGET_HIMODE_MATH" "ix86_expand_unary_operator (NEG, HImode, operands); DONE;") (define_insn "*neghi2_1" [(set (match_operand:HI 0 "nonimmediate_operand" "=rm") (neg:HI (match_operand:HI 1 "nonimmediate_operand" "0"))) (clobber (reg:CC 17))] "ix86_unary_operator_ok (NEG, HImode, operands)" "neg{w}\t%0" [(set_attr "type" "negnot") (set_attr "mode" "HI")]) (define_insn "*neghi2_cmpz" [(set (reg:CCZ 17) (compare:CCZ (neg:HI (match_operand:HI 1 "nonimmediate_operand" "0")) (const_int 0))) (set (match_operand:HI 0 "nonimmediate_operand" "=rm") (neg:HI (match_dup 1)))] "ix86_unary_operator_ok (NEG, HImode, operands)" "neg{w}\t%0" [(set_attr "type" "negnot") (set_attr "mode" "HI")]) (define_expand "negqi2" [(parallel [(set (match_operand:QI 0 "nonimmediate_operand" "") (neg:QI (match_operand:QI 1 "nonimmediate_operand" ""))) (clobber (reg:CC 17))])] "TARGET_QIMODE_MATH" "ix86_expand_unary_operator (NEG, QImode, operands); DONE;") (define_insn "*negqi2_1" [(set (match_operand:QI 0 "nonimmediate_operand" "=qm") (neg:QI (match_operand:QI 1 "nonimmediate_operand" "0"))) (clobber (reg:CC 17))] "ix86_unary_operator_ok (NEG, QImode, operands)" "neg{b}\t%0" [(set_attr "type" "negnot") (set_attr "mode" "QI")]) (define_insn "*negqi2_cmpz" [(set (reg:CCZ 17) (compare:CCZ (neg:QI (match_operand:QI 1 "nonimmediate_operand" "0")) (const_int 0))) (set (match_operand:QI 0 "nonimmediate_operand" "=qm") (neg:QI (match_dup 1)))] "ix86_unary_operator_ok (NEG, QImode, operands)" "neg{b}\t%0" [(set_attr "type" "negnot") (set_attr "mode" "QI")]) ;; Changing of sign for FP values is doable using integer unit too. (define_expand "negsf2" [(parallel [(set (match_operand:SF 0 "nonimmediate_operand" "") (neg:SF (match_operand:SF 1 "nonimmediate_operand" ""))) (clobber (reg:CC 17))])] "TARGET_80387 || TARGET_SSE_MATH" "if (TARGET_SSE_MATH) { /* In case operand is in memory, we will not use SSE. */ if (memory_operand (operands[0], VOIDmode) && rtx_equal_p (operands[0], operands[1])) emit_insn (gen_negsf2_memory (operands[0], operands[1])); else { /* Using SSE is tricky, since we need bitwise negation of -0 in register. */ rtx reg = gen_reg_rtx (SFmode); rtx dest = operands[0]; rtx imm = gen_lowpart (SFmode, gen_int_mode (0x80000000, SImode)); operands[1] = force_reg (SFmode, operands[1]); operands[0] = force_reg (SFmode, operands[0]); reg = force_reg (V4SFmode, gen_rtx_CONST_VECTOR (V4SFmode, gen_rtvec (4, imm, CONST0_RTX (SFmode), CONST0_RTX (SFmode), CONST0_RTX (SFmode)))); emit_insn (gen_negsf2_ifs (operands[0], operands[1], reg)); if (dest != operands[0]) emit_move_insn (dest, operands[0]); } DONE; } ix86_expand_unary_operator (NEG, SFmode, operands); DONE;") (define_insn "negsf2_memory" [(set (match_operand:SF 0 "memory_operand" "=m") (neg:SF (match_operand:SF 1 "memory_operand" "0"))) (clobber (reg:CC 17))] "ix86_unary_operator_ok (NEG, SFmode, operands)" "#") (define_insn "negsf2_ifs" [(set (match_operand:SF 0 "nonimmediate_operand" "=x#fr,x#fr,f#xr,rm#xf") (neg:SF (match_operand:SF 1 "nonimmediate_operand" "0,x#fr,0,0"))) (use (match_operand:V4SF 2 "nonimmediate_operand" "xm,0,xm*r,xm*r")) (clobber (reg:CC 17))] "TARGET_SSE && (reload_in_progress || reload_completed || (register_operand (operands[0], VOIDmode) && register_operand (operands[1], VOIDmode)))" "#") (define_split [(set (match_operand:SF 0 "memory_operand" "") (neg:SF (match_operand:SF 1 "memory_operand" ""))) (use (match_operand:SF 2 "" "")) (clobber (reg:CC 17))] "" [(parallel [(set (match_dup 0) (neg:SF (match_dup 1))) (clobber (reg:CC 17))])]) (define_split [(set (match_operand:SF 0 "register_operand" "") (neg:SF (match_operand:SF 1 "register_operand" ""))) (use (match_operand:V4SF 2 "" "")) (clobber (reg:CC 17))] "reload_completed && !SSE_REG_P (operands[0])" [(parallel [(set (match_dup 0) (neg:SF (match_dup 1))) (clobber (reg:CC 17))])]) (define_split [(set (match_operand:SF 0 "register_operand" "") (neg:SF (match_operand:SF 1 "register_operand" ""))) (use (match_operand:V4SF 2 "nonimmediate_operand" "")) (clobber (reg:CC 17))] "reload_completed && SSE_REG_P (operands[0])" [(set (match_dup 0) (xor:V4SF (match_dup 1) (match_dup 2)))] { operands[0] = simplify_gen_subreg (V4SFmode, operands[0], SFmode, 0); operands[1] = simplify_gen_subreg (V4SFmode, operands[1], SFmode, 0); if (operands_match_p (operands[0], operands[2])) { rtx tmp; tmp = operands[1]; operands[1] = operands[2]; operands[2] = tmp; } }) ;; Keep 'f' and 'r' in separate alternatives to avoid reload problems ;; because of secondary memory needed to reload from class FLOAT_INT_REGS ;; to itself. (define_insn "*negsf2_if" [(set (match_operand:SF 0 "nonimmediate_operand" "=f#r,rm#f") (neg:SF (match_operand:SF 1 "nonimmediate_operand" "0,0"))) (clobber (reg:CC 17))] "TARGET_80387 && ix86_unary_operator_ok (NEG, SFmode, operands)" "#") (define_split [(set (match_operand:SF 0 "fp_register_operand" "") (neg:SF (match_operand:SF 1 "register_operand" ""))) (clobber (reg:CC 17))] "TARGET_80387 && reload_completed" [(set (match_dup 0) (neg:SF (match_dup 1)))] "") (define_split [(set (match_operand:SF 0 "register_and_not_fp_reg_operand" "") (neg:SF (match_operand:SF 1 "register_operand" ""))) (clobber (reg:CC 17))] "TARGET_80387 && reload_completed" [(parallel [(set (match_dup 0) (xor:SI (match_dup 0) (match_dup 1))) (clobber (reg:CC 17))])] "operands[1] = gen_int_mode (0x80000000, SImode); operands[0] = gen_lowpart (SImode, operands[0]);") (define_split [(set (match_operand 0 "memory_operand" "") (neg (match_operand 1 "memory_operand" ""))) (clobber (reg:CC 17))] "TARGET_80387 && reload_completed && FLOAT_MODE_P (GET_MODE (operands[0]))" [(parallel [(set (match_dup 0) (xor:QI (match_dup 0) (match_dup 1))) (clobber (reg:CC 17))])] { int size = GET_MODE_SIZE (GET_MODE (operands[1])); if (GET_MODE (operands[1]) == XFmode) size = 10; operands[0] = adjust_address (operands[0], QImode, size - 1); operands[1] = gen_int_mode (0x80, QImode); }) (define_expand "negdf2" [(parallel [(set (match_operand:DF 0 "nonimmediate_operand" "") (neg:DF (match_operand:DF 1 "nonimmediate_operand" ""))) (clobber (reg:CC 17))])] "TARGET_80387 || (TARGET_SSE2 && TARGET_SSE_MATH)" "if (TARGET_SSE2 && TARGET_SSE_MATH) { /* In case operand is in memory, we will not use SSE. */ if (memory_operand (operands[0], VOIDmode) && rtx_equal_p (operands[0], operands[1])) emit_insn (gen_negdf2_memory (operands[0], operands[1])); else { /* Using SSE is tricky, since we need bitwise negation of -0 in register. */ rtx reg; #if HOST_BITS_PER_WIDE_INT >= 64 rtx imm = gen_int_mode (((HOST_WIDE_INT)1) << 63, DImode); #else rtx imm = immed_double_const (0, 0x80000000, DImode); #endif rtx dest = operands[0]; operands[1] = force_reg (DFmode, operands[1]); operands[0] = force_reg (DFmode, operands[0]); imm = gen_lowpart (DFmode, imm); reg = force_reg (V2DFmode, gen_rtx_CONST_VECTOR (V2DFmode, gen_rtvec (2, imm, CONST0_RTX (DFmode)))); emit_insn (gen_negdf2_ifs (operands[0], operands[1], reg)); if (dest != operands[0]) emit_move_insn (dest, operands[0]); } DONE; } ix86_expand_unary_operator (NEG, DFmode, operands); DONE;") (define_insn "negdf2_memory" [(set (match_operand:DF 0 "memory_operand" "=m") (neg:DF (match_operand:DF 1 "memory_operand" "0"))) (clobber (reg:CC 17))] "ix86_unary_operator_ok (NEG, DFmode, operands)" "#") (define_insn "negdf2_ifs" [(set (match_operand:DF 0 "nonimmediate_operand" "=Y#fr,Y#fr,f#Yr,rm#Yf") (neg:DF (match_operand:DF 1 "nonimmediate_operand" "0,Y#fr,0,0"))) (use (match_operand:V2DF 2 "nonimmediate_operand" "Ym,0,Ym*r,Ym*r")) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_SSE2 && (reload_in_progress || reload_completed || (register_operand (operands[0], VOIDmode) && register_operand (operands[1], VOIDmode)))" "#") (define_insn "*negdf2_ifs_rex64" [(set (match_operand:DF 0 "nonimmediate_operand" "=Y#f,Y#f,fm#Y") (neg:DF (match_operand:DF 1 "nonimmediate_operand" "0,Y#fr,0"))) (use (match_operand:V2DF 2 "nonimmediate_operand" "Ym,0,Ym*r")) (clobber (reg:CC 17))] "TARGET_64BIT && TARGET_SSE2 && (reload_in_progress || reload_completed || (register_operand (operands[0], VOIDmode) && register_operand (operands[1], VOIDmode)))" "#") (define_split [(set (match_operand:DF 0 "memory_operand" "") (neg:DF (match_operand:DF 1 "memory_operand" ""))) (use (match_operand:V2DF 2 "" "")) (clobber (reg:CC 17))] "" [(parallel [(set (match_dup 0) (neg:DF (match_dup 1))) (clobber (reg:CC 17))])]) (define_split [(set (match_operand:DF 0 "register_operand" "") (neg:DF (match_operand:DF 1 "register_operand" ""))) (use (match_operand:V2DF 2 "" "")) (clobber (reg:CC 17))] "reload_completed && !SSE_REG_P (operands[0]) && (!TARGET_64BIT || FP_REG_P (operands[0]))" [(parallel [(set (match_dup 0) (neg:DF (match_dup 1))) (clobber (reg:CC 17))])]) (define_split [(set (match_operand:DF 0 "register_operand" "") (neg:DF (match_operand:DF 1 "register_operand" ""))) (use (match_operand:V2DF 2 "" "")) (clobber (reg:CC 17))] "TARGET_64BIT && reload_completed && GENERAL_REG_P (operands[0])" [(parallel [(set (match_dup 0) (xor:DI (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))])] "operands[0] = gen_lowpart (DImode, operands[0]); operands[1] = gen_lowpart (DImode, operands[1]); operands[2] = gen_lowpart (DImode, operands[2]);") (define_split [(set (match_operand:DF 0 "register_operand" "") (neg:DF (match_operand:DF 1 "register_operand" ""))) (use (match_operand:V2DF 2 "nonimmediate_operand" "")) (clobber (reg:CC 17))] "reload_completed && SSE_REG_P (operands[0])" [(set (match_dup 0) (xor:V2DF (match_dup 1) (match_dup 2)))] { operands[0] = simplify_gen_subreg (V2DFmode, operands[0], DFmode, 0); operands[1] = simplify_gen_subreg (V2DFmode, operands[1], DFmode, 0); /* Avoid possible reformatting on the operands. */ if (TARGET_SSE_PARTIAL_REGS && !optimize_size) emit_insn (gen_sse2_unpcklpd (operands[0], operands[0], operands[0])); if (operands_match_p (operands[0], operands[2])) { rtx tmp; tmp = operands[1]; operands[1] = operands[2]; operands[2] = tmp; } }) ;; Keep 'f' and 'r' in separate alternatives to avoid reload problems ;; because of secondary memory needed to reload from class FLOAT_INT_REGS ;; to itself. (define_insn "*negdf2_if" [(set (match_operand:DF 0 "nonimmediate_operand" "=f#r,rm#f") (neg:DF (match_operand:DF 1 "nonimmediate_operand" "0,0"))) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_80387 && ix86_unary_operator_ok (NEG, DFmode, operands)" "#") ;; FIXME: We should to allow integer registers here. Problem is that ;; we need another scratch register to get constant from. ;; Forcing constant to mem if no register available in peep2 should be ;; safe even for PIC mode, because of RIP relative addressing. (define_insn "*negdf2_if_rex64" [(set (match_operand:DF 0 "nonimmediate_operand" "=f,mf") (neg:DF (match_operand:DF 1 "nonimmediate_operand" "0,0"))) (clobber (reg:CC 17))] "TARGET_64BIT && TARGET_80387 && ix86_unary_operator_ok (NEG, DFmode, operands)" "#") (define_split [(set (match_operand:DF 0 "fp_register_operand" "") (neg:DF (match_operand:DF 1 "register_operand" ""))) (clobber (reg:CC 17))] "TARGET_80387 && reload_completed" [(set (match_dup 0) (neg:DF (match_dup 1)))] "") (define_split [(set (match_operand:DF 0 "register_and_not_fp_reg_operand" "") (neg:DF (match_operand:DF 1 "register_operand" ""))) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_80387 && reload_completed" [(parallel [(set (match_dup 3) (xor:SI (match_dup 3) (match_dup 4))) (clobber (reg:CC 17))])] "operands[4] = gen_int_mode (0x80000000, SImode); split_di (operands+0, 1, operands+2, operands+3);") (define_expand "negxf2" [(parallel [(set (match_operand:XF 0 "nonimmediate_operand" "") (neg:XF (match_operand:XF 1 "nonimmediate_operand" ""))) (clobber (reg:CC 17))])] "TARGET_80387" "ix86_expand_unary_operator (NEG, XFmode, operands); DONE;") ;; Keep 'f' and 'r' in separate alternatives to avoid reload problems ;; because of secondary memory needed to reload from class FLOAT_INT_REGS ;; to itself. (define_insn "*negxf2_if" [(set (match_operand:XF 0 "nonimmediate_operand" "=f#r,rm#f") (neg:XF (match_operand:XF 1 "nonimmediate_operand" "0,0"))) (clobber (reg:CC 17))] "TARGET_80387 && ix86_unary_operator_ok (NEG, XFmode, operands)" "#") (define_split [(set (match_operand:XF 0 "fp_register_operand" "") (neg:XF (match_operand:XF 1 "register_operand" ""))) (clobber (reg:CC 17))] "TARGET_80387 && reload_completed" [(set (match_dup 0) (neg:XF (match_dup 1)))] "") (define_split [(set (match_operand:XF 0 "register_and_not_fp_reg_operand" "") (neg:XF (match_operand:XF 1 "register_operand" ""))) (clobber (reg:CC 17))] "TARGET_80387 && reload_completed" [(parallel [(set (match_dup 0) (xor:SI (match_dup 0) (match_dup 1))) (clobber (reg:CC 17))])] "operands[1] = GEN_INT (0x8000); operands[0] = gen_rtx_REG (SImode, true_regnum (operands[0]) + (TARGET_64BIT ? 1 : 2));") ;; Conditionalize these after reload. If they matches before reload, we ;; lose the clobber and ability to use integer instructions. (define_insn "*negsf2_1" [(set (match_operand:SF 0 "register_operand" "=f") (neg:SF (match_operand:SF 1 "register_operand" "0")))] "TARGET_80387 && reload_completed" "fchs" [(set_attr "type" "fsgn") (set_attr "mode" "SF") (set_attr "ppro_uops" "few")]) (define_insn "*negdf2_1" [(set (match_operand:DF 0 "register_operand" "=f") (neg:DF (match_operand:DF 1 "register_operand" "0")))] "TARGET_80387 && reload_completed" "fchs" [(set_attr "type" "fsgn") (set_attr "mode" "DF") (set_attr "ppro_uops" "few")]) (define_insn "*negextendsfdf2" [(set (match_operand:DF 0 "register_operand" "=f") (neg:DF (float_extend:DF (match_operand:SF 1 "register_operand" "0"))))] "TARGET_80387" "fchs" [(set_attr "type" "fsgn") (set_attr "mode" "DF") (set_attr "ppro_uops" "few")]) (define_insn "*negxf2_1" [(set (match_operand:XF 0 "register_operand" "=f") (neg:XF (match_operand:XF 1 "register_operand" "0")))] "TARGET_80387 && reload_completed" "fchs" [(set_attr "type" "fsgn") (set_attr "mode" "XF") (set_attr "ppro_uops" "few")]) (define_insn "*negextenddfxf2" [(set (match_operand:XF 0 "register_operand" "=f") (neg:XF (float_extend:XF (match_operand:DF 1 "register_operand" "0"))))] "TARGET_80387" "fchs" [(set_attr "type" "fsgn") (set_attr "mode" "XF") (set_attr "ppro_uops" "few")]) (define_insn "*negextendsfxf2" [(set (match_operand:XF 0 "register_operand" "=f") (neg:XF (float_extend:XF (match_operand:SF 1 "register_operand" "0"))))] "TARGET_80387" "fchs" [(set_attr "type" "fsgn") (set_attr "mode" "XF") (set_attr "ppro_uops" "few")]) ;; Absolute value instructions (define_expand "abssf2" [(parallel [(set (match_operand:SF 0 "nonimmediate_operand" "") (neg:SF (match_operand:SF 1 "nonimmediate_operand" ""))) (clobber (reg:CC 17))])] "TARGET_80387 || TARGET_SSE_MATH" "if (TARGET_SSE_MATH) { /* In case operand is in memory, we will not use SSE. */ if (memory_operand (operands[0], VOIDmode) && rtx_equal_p (operands[0], operands[1])) emit_insn (gen_abssf2_memory (operands[0], operands[1])); else { /* Using SSE is tricky, since we need bitwise negation of -0 in register. */ rtx reg = gen_reg_rtx (V4SFmode); rtx dest = operands[0]; rtx imm; operands[1] = force_reg (SFmode, operands[1]); operands[0] = force_reg (SFmode, operands[0]); imm = gen_lowpart (SFmode, gen_int_mode(~0x80000000, SImode)); reg = force_reg (V4SFmode, gen_rtx_CONST_VECTOR (V4SFmode, gen_rtvec (4, imm, CONST0_RTX (SFmode), CONST0_RTX (SFmode), CONST0_RTX (SFmode)))); emit_insn (gen_abssf2_ifs (operands[0], operands[1], reg)); if (dest != operands[0]) emit_move_insn (dest, operands[0]); } DONE; } ix86_expand_unary_operator (ABS, SFmode, operands); DONE;") (define_insn "abssf2_memory" [(set (match_operand:SF 0 "memory_operand" "=m") (abs:SF (match_operand:SF 1 "memory_operand" "0"))) (clobber (reg:CC 17))] "ix86_unary_operator_ok (ABS, SFmode, operands)" "#") (define_insn "abssf2_ifs" [(set (match_operand:SF 0 "nonimmediate_operand" "=x#fr,x#fr,f#xr,rm#xf") (abs:SF (match_operand:SF 1 "nonimmediate_operand" "0,x#fr,0,0"))) (use (match_operand:V4SF 2 "nonimmediate_operand" "xm,0,xm*r,xm*r")) (clobber (reg:CC 17))] "TARGET_SSE && (reload_in_progress || reload_completed || (register_operand (operands[0], VOIDmode) && register_operand (operands[1], VOIDmode)))" "#") (define_split [(set (match_operand:SF 0 "memory_operand" "") (abs:SF (match_operand:SF 1 "memory_operand" ""))) (use (match_operand:V4SF 2 "" "")) (clobber (reg:CC 17))] "" [(parallel [(set (match_dup 0) (abs:SF (match_dup 1))) (clobber (reg:CC 17))])]) (define_split [(set (match_operand:SF 0 "register_operand" "") (abs:SF (match_operand:SF 1 "register_operand" ""))) (use (match_operand:V4SF 2 "" "")) (clobber (reg:CC 17))] "reload_completed && !SSE_REG_P (operands[0])" [(parallel [(set (match_dup 0) (abs:SF (match_dup 1))) (clobber (reg:CC 17))])]) (define_split [(set (match_operand:SF 0 "register_operand" "") (abs:SF (match_operand:SF 1 "register_operand" ""))) (use (match_operand:V4SF 2 "nonimmediate_operand" "")) (clobber (reg:CC 17))] "reload_completed && SSE_REG_P (operands[0])" [(set (match_dup 0) (and:V4SF (match_dup 1) (match_dup 2)))] { operands[0] = simplify_gen_subreg (V4SFmode, operands[0], SFmode, 0); operands[1] = simplify_gen_subreg (V4SFmode, operands[1], SFmode, 0); if (operands_match_p (operands[0], operands[2])) { rtx tmp; tmp = operands[1]; operands[1] = operands[2]; operands[2] = tmp; } }) ;; Keep 'f' and 'r' in separate alternatives to avoid reload problems ;; because of secondary memory needed to reload from class FLOAT_INT_REGS ;; to itself. (define_insn "*abssf2_if" [(set (match_operand:SF 0 "nonimmediate_operand" "=f#r,rm#f") (abs:SF (match_operand:SF 1 "nonimmediate_operand" "0,0"))) (clobber (reg:CC 17))] "TARGET_80387 && ix86_unary_operator_ok (ABS, SFmode, operands)" "#") (define_split [(set (match_operand:SF 0 "fp_register_operand" "") (abs:SF (match_operand:SF 1 "register_operand" ""))) (clobber (reg:CC 17))] "TARGET_80387 && reload_completed" [(set (match_dup 0) (abs:SF (match_dup 1)))] "") (define_split [(set (match_operand:SF 0 "register_and_not_fp_reg_operand" "") (abs:SF (match_operand:SF 1 "register_operand" ""))) (clobber (reg:CC 17))] "TARGET_80387 && reload_completed" [(parallel [(set (match_dup 0) (and:SI (match_dup 0) (match_dup 1))) (clobber (reg:CC 17))])] "operands[1] = gen_int_mode (~0x80000000, SImode); operands[0] = gen_lowpart (SImode, operands[0]);") (define_split [(set (match_operand 0 "memory_operand" "") (abs (match_operand 1 "memory_operand" ""))) (clobber (reg:CC 17))] "TARGET_80387 && reload_completed && FLOAT_MODE_P (GET_MODE (operands[0]))" [(parallel [(set (match_dup 0) (and:QI (match_dup 0) (match_dup 1))) (clobber (reg:CC 17))])] { int size = GET_MODE_SIZE (GET_MODE (operands[1])); if (GET_MODE (operands[1]) == XFmode) size = 10; operands[0] = adjust_address (operands[0], QImode, size - 1); operands[1] = gen_int_mode (~0x80, QImode); }) (define_expand "absdf2" [(parallel [(set (match_operand:DF 0 "nonimmediate_operand" "") (neg:DF (match_operand:DF 1 "nonimmediate_operand" ""))) (clobber (reg:CC 17))])] "TARGET_80387 || (TARGET_SSE2 && TARGET_SSE_MATH)" "if (TARGET_SSE2 && TARGET_SSE_MATH) { /* In case operand is in memory, we will not use SSE. */ if (memory_operand (operands[0], VOIDmode) && rtx_equal_p (operands[0], operands[1])) emit_insn (gen_absdf2_memory (operands[0], operands[1])); else { /* Using SSE is tricky, since we need bitwise negation of -0 in register. */ rtx reg = gen_reg_rtx (V2DFmode); #if HOST_BITS_PER_WIDE_INT >= 64 rtx imm = gen_int_mode (~(((HOST_WIDE_INT)1) << 63), DImode); #else rtx imm = immed_double_const (~0, ~0x80000000, DImode); #endif rtx dest = operands[0]; operands[1] = force_reg (DFmode, operands[1]); operands[0] = force_reg (DFmode, operands[0]); /* Produce LONG_DOUBLE with the proper immediate argument. */ imm = gen_lowpart (DFmode, imm); reg = force_reg (V2DFmode, gen_rtx_CONST_VECTOR (V2DFmode, gen_rtvec (2, imm, CONST0_RTX (DFmode)))); emit_insn (gen_absdf2_ifs (operands[0], operands[1], reg)); if (dest != operands[0]) emit_move_insn (dest, operands[0]); } DONE; } ix86_expand_unary_operator (ABS, DFmode, operands); DONE;") (define_insn "absdf2_memory" [(set (match_operand:DF 0 "memory_operand" "=m") (abs:DF (match_operand:DF 1 "memory_operand" "0"))) (clobber (reg:CC 17))] "ix86_unary_operator_ok (ABS, DFmode, operands)" "#") (define_insn "absdf2_ifs" [(set (match_operand:DF 0 "nonimmediate_operand" "=Y#fr,Y#fr,mf#Yr,mr#Yf") (abs:DF (match_operand:DF 1 "nonimmediate_operand" "0,Y#fr,0,0"))) (use (match_operand:V2DF 2 "nonimmediate_operand" "Ym,0,Ym*r,Ym*r")) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_SSE2 && (reload_in_progress || reload_completed || (register_operand (operands[0], VOIDmode) && register_operand (operands[1], VOIDmode)))" "#") (define_insn "*absdf2_ifs_rex64" [(set (match_operand:DF 0 "nonimmediate_operand" "=Y#fr,Y#fr,mf#Yr") (abs:DF (match_operand:DF 1 "nonimmediate_operand" "0,Y#fr,0"))) (use (match_operand:V2DF 2 "nonimmediate_operand" "Ym,0,Ym*r")) (clobber (reg:CC 17))] "TARGET_64BIT && TARGET_SSE2 && (reload_in_progress || reload_completed || (register_operand (operands[0], VOIDmode) && register_operand (operands[1], VOIDmode)))" "#") (define_split [(set (match_operand:DF 0 "memory_operand" "") (abs:DF (match_operand:DF 1 "memory_operand" ""))) (use (match_operand:V2DF 2 "" "")) (clobber (reg:CC 17))] "" [(parallel [(set (match_dup 0) (abs:DF (match_dup 1))) (clobber (reg:CC 17))])]) (define_split [(set (match_operand:DF 0 "register_operand" "") (abs:DF (match_operand:DF 1 "register_operand" ""))) (use (match_operand:V2DF 2 "" "")) (clobber (reg:CC 17))] "reload_completed && !SSE_REG_P (operands[0])" [(parallel [(set (match_dup 0) (abs:DF (match_dup 1))) (clobber (reg:CC 17))])]) (define_split [(set (match_operand:DF 0 "register_operand" "") (abs:DF (match_operand:DF 1 "register_operand" ""))) (use (match_operand:V2DF 2 "nonimmediate_operand" "")) (clobber (reg:CC 17))] "reload_completed && SSE_REG_P (operands[0])" [(set (match_dup 0) (and:V2DF (match_dup 1) (match_dup 2)))] { operands[0] = simplify_gen_subreg (V2DFmode, operands[0], DFmode, 0); operands[1] = simplify_gen_subreg (V2DFmode, operands[1], DFmode, 0); /* Avoid possible reformatting on the operands. */ if (TARGET_SSE_PARTIAL_REGS && !optimize_size) emit_insn (gen_sse2_unpcklpd (operands[0], operands[0], operands[0])); if (operands_match_p (operands[0], operands[2])) { rtx tmp; tmp = operands[1]; operands[1] = operands[2]; operands[2] = tmp; } }) ;; Keep 'f' and 'r' in separate alternatives to avoid reload problems ;; because of secondary memory needed to reload from class FLOAT_INT_REGS ;; to itself. (define_insn "*absdf2_if" [(set (match_operand:DF 0 "nonimmediate_operand" "=f#r,rm#f") (abs:DF (match_operand:DF 1 "nonimmediate_operand" "0,0"))) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_80387 && ix86_unary_operator_ok (ABS, DFmode, operands)" "#") ;; FIXME: We should to allow integer registers here. Problem is that ;; we need another scratch register to get constant from. ;; Forcing constant to mem if no register available in peep2 should be ;; safe even for PIC mode, because of RIP relative addressing. (define_insn "*absdf2_if_rex64" [(set (match_operand:DF 0 "nonimmediate_operand" "=f,mf") (abs:DF (match_operand:DF 1 "nonimmediate_operand" "0,0"))) (clobber (reg:CC 17))] "TARGET_64BIT && TARGET_80387 && ix86_unary_operator_ok (ABS, DFmode, operands)" "#") (define_split [(set (match_operand:DF 0 "fp_register_operand" "") (abs:DF (match_operand:DF 1 "register_operand" ""))) (clobber (reg:CC 17))] "TARGET_80387 && reload_completed" [(set (match_dup 0) (abs:DF (match_dup 1)))] "") (define_split [(set (match_operand:DF 0 "register_and_not_fp_reg_operand" "") (abs:DF (match_operand:DF 1 "register_operand" ""))) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_80387 && reload_completed" [(parallel [(set (match_dup 3) (and:SI (match_dup 3) (match_dup 4))) (clobber (reg:CC 17))])] "operands[4] = gen_int_mode (~0x80000000, SImode); split_di (operands+0, 1, operands+2, operands+3);") (define_expand "absxf2" [(parallel [(set (match_operand:XF 0 "nonimmediate_operand" "") (neg:XF (match_operand:XF 1 "nonimmediate_operand" ""))) (clobber (reg:CC 17))])] "TARGET_80387" "ix86_expand_unary_operator (ABS, XFmode, operands); DONE;") ;; Keep 'f' and 'r' in separate alternatives to avoid reload problems ;; because of secondary memory needed to reload from class FLOAT_INT_REGS ;; to itself. (define_insn "*absxf2_if" [(set (match_operand:XF 0 "nonimmediate_operand" "=f#r,rm#f") (abs:XF (match_operand:XF 1 "nonimmediate_operand" "0,0"))) (clobber (reg:CC 17))] "TARGET_80387 && ix86_unary_operator_ok (ABS, XFmode, operands)" "#") (define_split [(set (match_operand:XF 0 "fp_register_operand" "") (abs:XF (match_operand:XF 1 "register_operand" ""))) (clobber (reg:CC 17))] "TARGET_80387 && reload_completed" [(set (match_dup 0) (abs:XF (match_dup 1)))] "") (define_split [(set (match_operand:XF 0 "register_and_not_fp_reg_operand" "") (abs:XF (match_operand:XF 1 "register_operand" ""))) (clobber (reg:CC 17))] "TARGET_80387 && reload_completed" [(parallel [(set (match_dup 0) (and:SI (match_dup 0) (match_dup 1))) (clobber (reg:CC 17))])] "operands[1] = GEN_INT (~0x8000); operands[0] = gen_rtx_REG (SImode, true_regnum (operands[0]) + (TARGET_64BIT ? 1 : 2));") (define_insn "*abssf2_1" [(set (match_operand:SF 0 "register_operand" "=f") (abs:SF (match_operand:SF 1 "register_operand" "0")))] "TARGET_80387 && reload_completed" "fabs" [(set_attr "type" "fsgn") (set_attr "mode" "SF")]) (define_insn "*absdf2_1" [(set (match_operand:DF 0 "register_operand" "=f") (abs:DF (match_operand:DF 1 "register_operand" "0")))] "TARGET_80387 && reload_completed" "fabs" [(set_attr "type" "fsgn") (set_attr "mode" "DF")]) (define_insn "*absextendsfdf2" [(set (match_operand:DF 0 "register_operand" "=f") (abs:DF (float_extend:DF (match_operand:SF 1 "register_operand" "0"))))] "TARGET_80387" "fabs" [(set_attr "type" "fsgn") (set_attr "mode" "DF")]) (define_insn "*absxf2_1" [(set (match_operand:XF 0 "register_operand" "=f") (abs:XF (match_operand:XF 1 "register_operand" "0")))] "TARGET_80387 && reload_completed" "fabs" [(set_attr "type" "fsgn") (set_attr "mode" "DF")]) (define_insn "*absextenddfxf2" [(set (match_operand:XF 0 "register_operand" "=f") (abs:XF (float_extend:XF (match_operand:DF 1 "register_operand" "0"))))] "TARGET_80387" "fabs" [(set_attr "type" "fsgn") (set_attr "mode" "XF")]) (define_insn "*absextendsfxf2" [(set (match_operand:XF 0 "register_operand" "=f") (abs:XF (float_extend:XF (match_operand:SF 1 "register_operand" "0"))))] "TARGET_80387" "fabs" [(set_attr "type" "fsgn") (set_attr "mode" "XF")]) ;; One complement instructions (define_expand "one_cmpldi2" [(set (match_operand:DI 0 "nonimmediate_operand" "") (not:DI (match_operand:DI 1 "nonimmediate_operand" "")))] "TARGET_64BIT" "ix86_expand_unary_operator (NOT, DImode, operands); DONE;") (define_insn "*one_cmpldi2_1_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=rm") (not:DI (match_operand:DI 1 "nonimmediate_operand" "0")))] "TARGET_64BIT && ix86_unary_operator_ok (NOT, DImode, operands)" "not{q}\t%0" [(set_attr "type" "negnot") (set_attr "mode" "DI")]) (define_insn "*one_cmpldi2_2_rex64" [(set (reg 17) (compare (not:DI (match_operand:DI 1 "nonimmediate_operand" "0")) (const_int 0))) (set (match_operand:DI 0 "nonimmediate_operand" "=rm") (not:DI (match_dup 1)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode) && ix86_unary_operator_ok (NOT, DImode, operands)" "#" [(set_attr "type" "alu1") (set_attr "mode" "DI")]) (define_split [(set (match_operand 0 "flags_reg_operand" "") (match_operator 2 "compare_operator" [(not:DI (match_operand:DI 3 "nonimmediate_operand" "")) (const_int 0)])) (set (match_operand:DI 1 "nonimmediate_operand" "") (not:DI (match_dup 3)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode)" [(parallel [(set (match_dup 0) (match_op_dup 2 [(xor:DI (match_dup 3) (const_int -1)) (const_int 0)])) (set (match_dup 1) (xor:DI (match_dup 3) (const_int -1)))])] "") (define_expand "one_cmplsi2" [(set (match_operand:SI 0 "nonimmediate_operand" "") (not:SI (match_operand:SI 1 "nonimmediate_operand" "")))] "" "ix86_expand_unary_operator (NOT, SImode, operands); DONE;") (define_insn "*one_cmplsi2_1" [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") (not:SI (match_operand:SI 1 "nonimmediate_operand" "0")))] "ix86_unary_operator_ok (NOT, SImode, operands)" "not{l}\t%0" [(set_attr "type" "negnot") (set_attr "mode" "SI")]) ;; ??? Currently never generated - xor is used instead. (define_insn "*one_cmplsi2_1_zext" [(set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (not:SI (match_operand:SI 1 "register_operand" "0"))))] "TARGET_64BIT && ix86_unary_operator_ok (NOT, SImode, operands)" "not{l}\t%k0" [(set_attr "type" "negnot") (set_attr "mode" "SI")]) (define_insn "*one_cmplsi2_2" [(set (reg 17) (compare (not:SI (match_operand:SI 1 "nonimmediate_operand" "0")) (const_int 0))) (set (match_operand:SI 0 "nonimmediate_operand" "=rm") (not:SI (match_dup 1)))] "ix86_match_ccmode (insn, CCNOmode) && ix86_unary_operator_ok (NOT, SImode, operands)" "#" [(set_attr "type" "alu1") (set_attr "mode" "SI")]) (define_split [(set (match_operand 0 "flags_reg_operand" "") (match_operator 2 "compare_operator" [(not:SI (match_operand:SI 3 "nonimmediate_operand" "")) (const_int 0)])) (set (match_operand:SI 1 "nonimmediate_operand" "") (not:SI (match_dup 3)))] "ix86_match_ccmode (insn, CCNOmode)" [(parallel [(set (match_dup 0) (match_op_dup 2 [(xor:SI (match_dup 3) (const_int -1)) (const_int 0)])) (set (match_dup 1) (xor:SI (match_dup 3) (const_int -1)))])] "") ;; ??? Currently never generated - xor is used instead. (define_insn "*one_cmplsi2_2_zext" [(set (reg 17) (compare (not:SI (match_operand:SI 1 "register_operand" "0")) (const_int 0))) (set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (not:SI (match_dup 1))))] "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode) && ix86_unary_operator_ok (NOT, SImode, operands)" "#" [(set_attr "type" "alu1") (set_attr "mode" "SI")]) (define_split [(set (match_operand 0 "flags_reg_operand" "") (match_operator 2 "compare_operator" [(not:SI (match_operand:SI 3 "register_operand" "")) (const_int 0)])) (set (match_operand:DI 1 "register_operand" "") (zero_extend:DI (not:SI (match_dup 3))))] "ix86_match_ccmode (insn, CCNOmode)" [(parallel [(set (match_dup 0) (match_op_dup 2 [(xor:SI (match_dup 3) (const_int -1)) (const_int 0)])) (set (match_dup 1) (zero_extend:DI (xor:SI (match_dup 3) (const_int -1))))])] "") (define_expand "one_cmplhi2" [(set (match_operand:HI 0 "nonimmediate_operand" "") (not:HI (match_operand:HI 1 "nonimmediate_operand" "")))] "TARGET_HIMODE_MATH" "ix86_expand_unary_operator (NOT, HImode, operands); DONE;") (define_insn "*one_cmplhi2_1" [(set (match_operand:HI 0 "nonimmediate_operand" "=rm") (not:HI (match_operand:HI 1 "nonimmediate_operand" "0")))] "ix86_unary_operator_ok (NOT, HImode, operands)" "not{w}\t%0" [(set_attr "type" "negnot") (set_attr "mode" "HI")]) (define_insn "*one_cmplhi2_2" [(set (reg 17) (compare (not:HI (match_operand:HI 1 "nonimmediate_operand" "0")) (const_int 0))) (set (match_operand:HI 0 "nonimmediate_operand" "=rm") (not:HI (match_dup 1)))] "ix86_match_ccmode (insn, CCNOmode) && ix86_unary_operator_ok (NEG, HImode, operands)" "#" [(set_attr "type" "alu1") (set_attr "mode" "HI")]) (define_split [(set (match_operand 0 "flags_reg_operand" "") (match_operator 2 "compare_operator" [(not:HI (match_operand:HI 3 "nonimmediate_operand" "")) (const_int 0)])) (set (match_operand:HI 1 "nonimmediate_operand" "") (not:HI (match_dup 3)))] "ix86_match_ccmode (insn, CCNOmode)" [(parallel [(set (match_dup 0) (match_op_dup 2 [(xor:HI (match_dup 3) (const_int -1)) (const_int 0)])) (set (match_dup 1) (xor:HI (match_dup 3) (const_int -1)))])] "") ;; %%% Potential partial reg stall on alternative 1. What to do? (define_expand "one_cmplqi2" [(set (match_operand:QI 0 "nonimmediate_operand" "") (not:QI (match_operand:QI 1 "nonimmediate_operand" "")))] "TARGET_QIMODE_MATH" "ix86_expand_unary_operator (NOT, QImode, operands); DONE;") (define_insn "*one_cmplqi2_1" [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,r") (not:QI (match_operand:QI 1 "nonimmediate_operand" "0,0")))] "ix86_unary_operator_ok (NOT, QImode, operands)" "@ not{b}\t%0 not{l}\t%k0" [(set_attr "type" "negnot") (set_attr "mode" "QI,SI")]) (define_insn "*one_cmplqi2_2" [(set (reg 17) (compare (not:QI (match_operand:QI 1 "nonimmediate_operand" "0")) (const_int 0))) (set (match_operand:QI 0 "nonimmediate_operand" "=qm") (not:QI (match_dup 1)))] "ix86_match_ccmode (insn, CCNOmode) && ix86_unary_operator_ok (NOT, QImode, operands)" "#" [(set_attr "type" "alu1") (set_attr "mode" "QI")]) (define_split [(set (match_operand 0 "flags_reg_operand" "") (match_operator 2 "compare_operator" [(not:QI (match_operand:QI 3 "nonimmediate_operand" "")) (const_int 0)])) (set (match_operand:QI 1 "nonimmediate_operand" "") (not:QI (match_dup 3)))] "ix86_match_ccmode (insn, CCNOmode)" [(parallel [(set (match_dup 0) (match_op_dup 2 [(xor:QI (match_dup 3) (const_int -1)) (const_int 0)])) (set (match_dup 1) (xor:QI (match_dup 3) (const_int -1)))])] "") ;; Arithmetic shift instructions ;; DImode shifts are implemented using the i386 "shift double" opcode, ;; which is written as "sh[lr]d[lw] imm,reg,reg/mem". If the shift count ;; is variable, then the count is in %cl and the "imm" operand is dropped ;; from the assembler input. ;; ;; This instruction shifts the target reg/mem as usual, but instead of ;; shifting in zeros, bits are shifted in from reg operand. If the insn ;; is a left shift double, bits are taken from the high order bits of ;; reg, else if the insn is a shift right double, bits are taken from the ;; low order bits of reg. So if %eax is "1234" and %edx is "5678", ;; "shldl $8,%edx,%eax" leaves %edx unchanged and sets %eax to "2345". ;; ;; Since sh[lr]d does not change the `reg' operand, that is done ;; separately, making all shifts emit pairs of shift double and normal ;; shift. Since sh[lr]d does not shift more than 31 bits, and we wish to ;; support a 63 bit shift, each shift where the count is in a reg expands ;; to a pair of shifts, a branch, a shift by 32 and a label. ;; ;; If the shift count is a constant, we need never emit more than one ;; shift pair, instead using moves and sign extension for counts greater ;; than 31. (define_expand "ashldi3" [(parallel [(set (match_operand:DI 0 "shiftdi_operand" "") (ashift:DI (match_operand:DI 1 "shiftdi_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))])] "" { if (!TARGET_64BIT && TARGET_CMOVE && ! immediate_operand (operands[2], QImode)) { emit_insn (gen_ashldi3_1 (operands[0], operands[1], operands[2])); DONE; } ix86_expand_binary_operator (ASHIFT, DImode, operands); DONE; }) (define_insn "*ashldi3_1_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=rm,r") (ashift:DI (match_operand:DI 1 "nonimmediate_operand" "0,r") (match_operand:QI 2 "nonmemory_operand" "cJ,M"))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (ASHIFT, DImode, operands)" { switch (get_attr_type (insn)) { case TYPE_ALU: if (operands[2] != const1_rtx) abort (); if (!rtx_equal_p (operands[0], operands[1])) abort (); return "add{q}\t{%0, %0|%0, %0}"; case TYPE_LEA: if (GET_CODE (operands[2]) != CONST_INT || (unsigned HOST_WIDE_INT) INTVAL (operands[2]) > 3) abort (); operands[1] = gen_rtx_MULT (DImode, operands[1], GEN_INT (1 << INTVAL (operands[2]))); return "lea{q}\t{%a1, %0|%0, %a1}"; default: if (REG_P (operands[2])) return "sal{q}\t{%b2, %0|%0, %b2}"; else if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 1 && (TARGET_SHIFT1 || optimize_size)) return "sal{q}\t%0"; else return "sal{q}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (cond [(eq_attr "alternative" "1") (const_string "lea") (and (and (ne (symbol_ref "TARGET_DOUBLE_WITH_ADD") (const_int 0)) (match_operand 0 "register_operand" "")) (match_operand 2 "const1_operand" "")) (const_string "alu") ] (const_string "ishift"))) (set_attr "mode" "DI")]) ;; Convert lea to the lea pattern to avoid flags dependency. (define_split [(set (match_operand:DI 0 "register_operand" "") (ashift:DI (match_operand:DI 1 "register_operand" "") (match_operand:QI 2 "immediate_operand" ""))) (clobber (reg:CC 17))] "TARGET_64BIT && reload_completed && true_regnum (operands[0]) != true_regnum (operands[1])" [(set (match_dup 0) (mult:DI (match_dup 1) (match_dup 2)))] "operands[2] = gen_int_mode (1 << INTVAL (operands[2]), DImode);") ;; This pattern can't accept a variable shift count, since shifts by ;; zero don't affect the flags. We assume that shifts by constant ;; zero are optimized away. (define_insn "*ashldi3_cmp_rex64" [(set (reg 17) (compare (ashift:DI (match_operand:DI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "immediate_operand" "e")) (const_int 0))) (set (match_operand:DI 0 "nonimmediate_operand" "=rm") (ashift:DI (match_dup 1) (match_dup 2)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (ASHIFT, DImode, operands)" { switch (get_attr_type (insn)) { case TYPE_ALU: if (operands[2] != const1_rtx) abort (); return "add{q}\t{%0, %0|%0, %0}"; default: if (REG_P (operands[2])) return "sal{q}\t{%b2, %0|%0, %b2}"; else if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 1 && (TARGET_SHIFT1 || optimize_size)) return "sal{q}\t%0"; else return "sal{q}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (cond [(and (and (ne (symbol_ref "TARGET_DOUBLE_WITH_ADD") (const_int 0)) (match_operand 0 "register_operand" "")) (match_operand 2 "const1_operand" "")) (const_string "alu") ] (const_string "ishift"))) (set_attr "mode" "DI")]) (define_insn "ashldi3_1" [(set (match_operand:DI 0 "register_operand" "=r") (ashift:DI (match_operand:DI 1 "register_operand" "0") (match_operand:QI 2 "nonmemory_operand" "Jc"))) (clobber (match_scratch:SI 3 "=&r")) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_CMOVE" "#" [(set_attr "type" "multi")]) (define_insn "*ashldi3_2" [(set (match_operand:DI 0 "register_operand" "=r") (ashift:DI (match_operand:DI 1 "register_operand" "0") (match_operand:QI 2 "nonmemory_operand" "Jc"))) (clobber (reg:CC 17))] "!TARGET_64BIT" "#" [(set_attr "type" "multi")]) (define_split [(set (match_operand:DI 0 "register_operand" "") (ashift:DI (match_operand:DI 1 "register_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (match_scratch:SI 3 "")) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_CMOVE && reload_completed" [(const_int 0)] "ix86_split_ashldi (operands, operands[3]); DONE;") (define_split [(set (match_operand:DI 0 "register_operand" "") (ashift:DI (match_operand:DI 1 "register_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "!TARGET_64BIT && reload_completed" [(const_int 0)] "ix86_split_ashldi (operands, NULL_RTX); DONE;") (define_insn "x86_shld_1" [(set (match_operand:SI 0 "nonimmediate_operand" "+r*m,r*m") (ior:SI (ashift:SI (match_dup 0) (match_operand:QI 2 "nonmemory_operand" "I,c")) (lshiftrt:SI (match_operand:SI 1 "register_operand" "r,r") (minus:QI (const_int 32) (match_dup 2))))) (clobber (reg:CC 17))] "" "@ shld{l}\t{%2, %1, %0|%0, %1, %2} shld{l}\t{%s2%1, %0|%0, %1, %2}" [(set_attr "type" "ishift") (set_attr "prefix_0f" "1") (set_attr "mode" "SI") (set_attr "pent_pair" "np") (set_attr "athlon_decode" "vector") (set_attr "ppro_uops" "few")]) (define_expand "x86_shift_adj_1" [(set (reg:CCZ 17) (compare:CCZ (and:QI (match_operand:QI 2 "register_operand" "") (const_int 32)) (const_int 0))) (set (match_operand:SI 0 "register_operand" "") (if_then_else:SI (ne (reg:CCZ 17) (const_int 0)) (match_operand:SI 1 "register_operand" "") (match_dup 0))) (set (match_dup 1) (if_then_else:SI (ne (reg:CCZ 17) (const_int 0)) (match_operand:SI 3 "register_operand" "r") (match_dup 1)))] "TARGET_CMOVE" "") (define_expand "x86_shift_adj_2" [(use (match_operand:SI 0 "register_operand" "")) (use (match_operand:SI 1 "register_operand" "")) (use (match_operand:QI 2 "register_operand" ""))] "" { rtx label = gen_label_rtx (); rtx tmp; emit_insn (gen_testqi_ccz_1 (operands[2], GEN_INT (32))); tmp = gen_rtx_REG (CCZmode, FLAGS_REG); tmp = gen_rtx_EQ (VOIDmode, tmp, const0_rtx); tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp, gen_rtx_LABEL_REF (VOIDmode, label), pc_rtx); tmp = emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp)); JUMP_LABEL (tmp) = label; emit_move_insn (operands[0], operands[1]); emit_move_insn (operands[1], const0_rtx); emit_label (label); LABEL_NUSES (label) = 1; DONE; }) (define_expand "ashlsi3" [(set (match_operand:SI 0 "nonimmediate_operand" "") (ashift:SI (match_operand:SI 1 "nonimmediate_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "" "ix86_expand_binary_operator (ASHIFT, SImode, operands); DONE;") (define_insn "*ashlsi3_1" [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r") (ashift:SI (match_operand:SI 1 "nonimmediate_operand" "0,r") (match_operand:QI 2 "nonmemory_operand" "cI,M"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (ASHIFT, SImode, operands)" { switch (get_attr_type (insn)) { case TYPE_ALU: if (operands[2] != const1_rtx) abort (); if (!rtx_equal_p (operands[0], operands[1])) abort (); return "add{l}\t{%0, %0|%0, %0}"; case TYPE_LEA: return "#"; default: if (REG_P (operands[2])) return "sal{l}\t{%b2, %0|%0, %b2}"; else if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 1 && (TARGET_SHIFT1 || optimize_size)) return "sal{l}\t%0"; else return "sal{l}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (cond [(eq_attr "alternative" "1") (const_string "lea") (and (and (ne (symbol_ref "TARGET_DOUBLE_WITH_ADD") (const_int 0)) (match_operand 0 "register_operand" "")) (match_operand 2 "const1_operand" "")) (const_string "alu") ] (const_string "ishift"))) (set_attr "mode" "SI")]) ;; Convert lea to the lea pattern to avoid flags dependency. (define_split [(set (match_operand 0 "register_operand" "") (ashift (match_operand 1 "index_register_operand" "") (match_operand:QI 2 "const_int_operand" ""))) (clobber (reg:CC 17))] "reload_completed && true_regnum (operands[0]) != true_regnum (operands[1])" [(const_int 0)] { rtx pat; operands[0] = gen_lowpart (SImode, operands[0]); operands[1] = gen_lowpart (Pmode, operands[1]); operands[2] = gen_int_mode (1 << INTVAL (operands[2]), Pmode); pat = gen_rtx_MULT (Pmode, operands[1], operands[2]); if (Pmode != SImode) pat = gen_rtx_SUBREG (SImode, pat, 0); emit_insn (gen_rtx_SET (VOIDmode, operands[0], pat)); DONE; }) ;; Rare case of shifting RSP is handled by generating move and shift (define_split [(set (match_operand 0 "register_operand" "") (ashift (match_operand 1 "register_operand" "") (match_operand:QI 2 "const_int_operand" ""))) (clobber (reg:CC 17))] "reload_completed && true_regnum (operands[0]) != true_regnum (operands[1])" [(const_int 0)] { rtx pat, clob; emit_move_insn (operands[1], operands[0]); pat = gen_rtx_SET (VOIDmode, operands[0], gen_rtx_ASHIFT (GET_MODE (operands[0]), operands[0], operands[2])); clob = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, FLAGS_REG)); emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, pat, clob))); DONE; }) (define_insn "*ashlsi3_1_zext" [(set (match_operand:DI 0 "register_operand" "=r,r") (zero_extend:DI (ashift:SI (match_operand:SI 1 "register_operand" "0,r") (match_operand:QI 2 "nonmemory_operand" "cI,M")))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (ASHIFT, SImode, operands)" { switch (get_attr_type (insn)) { case TYPE_ALU: if (operands[2] != const1_rtx) abort (); return "add{l}\t{%k0, %k0|%k0, %k0}"; case TYPE_LEA: return "#"; default: if (REG_P (operands[2])) return "sal{l}\t{%b2, %k0|%k0, %b2}"; else if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 1 && (TARGET_SHIFT1 || optimize_size)) return "sal{l}\t%k0"; else return "sal{l}\t{%2, %k0|%k0, %2}"; } } [(set (attr "type") (cond [(eq_attr "alternative" "1") (const_string "lea") (and (ne (symbol_ref "TARGET_DOUBLE_WITH_ADD") (const_int 0)) (match_operand 2 "const1_operand" "")) (const_string "alu") ] (const_string "ishift"))) (set_attr "mode" "SI")]) ;; Convert lea to the lea pattern to avoid flags dependency. (define_split [(set (match_operand:DI 0 "register_operand" "") (zero_extend:DI (ashift (match_operand 1 "register_operand" "") (match_operand:QI 2 "const_int_operand" "")))) (clobber (reg:CC 17))] "TARGET_64BIT && reload_completed && true_regnum (operands[0]) != true_regnum (operands[1])" [(set (match_dup 0) (zero_extend:DI (subreg:SI (mult:SI (match_dup 1) (match_dup 2)) 0)))] { operands[1] = gen_lowpart (Pmode, operands[1]); operands[2] = gen_int_mode (1 << INTVAL (operands[2]), Pmode); }) ;; This pattern can't accept a variable shift count, since shifts by ;; zero don't affect the flags. We assume that shifts by constant ;; zero are optimized away. (define_insn "*ashlsi3_cmp" [(set (reg 17) (compare (ashift:SI (match_operand:SI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const_int_1_31_operand" "I")) (const_int 0))) (set (match_operand:SI 0 "nonimmediate_operand" "=rm") (ashift:SI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (ASHIFT, SImode, operands)" { switch (get_attr_type (insn)) { case TYPE_ALU: if (operands[2] != const1_rtx) abort (); return "add{l}\t{%0, %0|%0, %0}"; default: if (REG_P (operands[2])) return "sal{l}\t{%b2, %0|%0, %b2}"; else if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 1 && (TARGET_SHIFT1 || optimize_size)) return "sal{l}\t%0"; else return "sal{l}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (cond [(and (and (ne (symbol_ref "TARGET_DOUBLE_WITH_ADD") (const_int 0)) (match_operand 0 "register_operand" "")) (match_operand 2 "const1_operand" "")) (const_string "alu") ] (const_string "ishift"))) (set_attr "mode" "SI")]) (define_insn "*ashlsi3_cmp_zext" [(set (reg 17) (compare (ashift:SI (match_operand:SI 1 "register_operand" "0") (match_operand:QI 2 "const_int_1_31_operand" "I")) (const_int 0))) (set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (ashift:SI (match_dup 1) (match_dup 2))))] "TARGET_64BIT && ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (ASHIFT, SImode, operands)" { switch (get_attr_type (insn)) { case TYPE_ALU: if (operands[2] != const1_rtx) abort (); return "add{l}\t{%k0, %k0|%k0, %k0}"; default: if (REG_P (operands[2])) return "sal{l}\t{%b2, %k0|%k0, %b2}"; else if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 1 && (TARGET_SHIFT1 || optimize_size)) return "sal{l}\t%k0"; else return "sal{l}\t{%2, %k0|%k0, %2}"; } } [(set (attr "type") (cond [(and (ne (symbol_ref "TARGET_DOUBLE_WITH_ADD") (const_int 0)) (match_operand 2 "const1_operand" "")) (const_string "alu") ] (const_string "ishift"))) (set_attr "mode" "SI")]) (define_expand "ashlhi3" [(set (match_operand:HI 0 "nonimmediate_operand" "") (ashift:HI (match_operand:HI 1 "nonimmediate_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "TARGET_HIMODE_MATH" "ix86_expand_binary_operator (ASHIFT, HImode, operands); DONE;") (define_insn "*ashlhi3_1_lea" [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,r") (ashift:HI (match_operand:HI 1 "nonimmediate_operand" "0,r") (match_operand:QI 2 "nonmemory_operand" "cI,M"))) (clobber (reg:CC 17))] "!TARGET_PARTIAL_REG_STALL && ix86_binary_operator_ok (ASHIFT, HImode, operands)" { switch (get_attr_type (insn)) { case TYPE_LEA: return "#"; case TYPE_ALU: if (operands[2] != const1_rtx) abort (); return "add{w}\t{%0, %0|%0, %0}"; default: if (REG_P (operands[2])) return "sal{w}\t{%b2, %0|%0, %b2}"; else if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 1 && (TARGET_SHIFT1 || optimize_size)) return "sal{w}\t%0"; else return "sal{w}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (cond [(eq_attr "alternative" "1") (const_string "lea") (and (and (ne (symbol_ref "TARGET_DOUBLE_WITH_ADD") (const_int 0)) (match_operand 0 "register_operand" "")) (match_operand 2 "const1_operand" "")) (const_string "alu") ] (const_string "ishift"))) (set_attr "mode" "HI,SI")]) (define_insn "*ashlhi3_1" [(set (match_operand:HI 0 "nonimmediate_operand" "=rm") (ashift:HI (match_operand:HI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "nonmemory_operand" "cI"))) (clobber (reg:CC 17))] "TARGET_PARTIAL_REG_STALL && ix86_binary_operator_ok (ASHIFT, HImode, operands)" { switch (get_attr_type (insn)) { case TYPE_ALU: if (operands[2] != const1_rtx) abort (); return "add{w}\t{%0, %0|%0, %0}"; default: if (REG_P (operands[2])) return "sal{w}\t{%b2, %0|%0, %b2}"; else if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 1 && (TARGET_SHIFT1 || optimize_size)) return "sal{w}\t%0"; else return "sal{w}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (cond [(and (and (ne (symbol_ref "TARGET_DOUBLE_WITH_ADD") (const_int 0)) (match_operand 0 "register_operand" "")) (match_operand 2 "const1_operand" "")) (const_string "alu") ] (const_string "ishift"))) (set_attr "mode" "HI")]) ;; This pattern can't accept a variable shift count, since shifts by ;; zero don't affect the flags. We assume that shifts by constant ;; zero are optimized away. (define_insn "*ashlhi3_cmp" [(set (reg 17) (compare (ashift:HI (match_operand:HI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const_int_1_31_operand" "I")) (const_int 0))) (set (match_operand:HI 0 "nonimmediate_operand" "=rm") (ashift:HI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (ASHIFT, HImode, operands)" { switch (get_attr_type (insn)) { case TYPE_ALU: if (operands[2] != const1_rtx) abort (); return "add{w}\t{%0, %0|%0, %0}"; default: if (REG_P (operands[2])) return "sal{w}\t{%b2, %0|%0, %b2}"; else if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 1 && (TARGET_SHIFT1 || optimize_size)) return "sal{w}\t%0"; else return "sal{w}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (cond [(and (and (ne (symbol_ref "TARGET_DOUBLE_WITH_ADD") (const_int 0)) (match_operand 0 "register_operand" "")) (match_operand 2 "const1_operand" "")) (const_string "alu") ] (const_string "ishift"))) (set_attr "mode" "HI")]) (define_expand "ashlqi3" [(set (match_operand:QI 0 "nonimmediate_operand" "") (ashift:QI (match_operand:QI 1 "nonimmediate_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "TARGET_QIMODE_MATH" "ix86_expand_binary_operator (ASHIFT, QImode, operands); DONE;") ;; %%% Potential partial reg stall on alternative 2. What to do? (define_insn "*ashlqi3_1_lea" [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,r,r") (ashift:QI (match_operand:QI 1 "nonimmediate_operand" "0,0,r") (match_operand:QI 2 "nonmemory_operand" "cI,cI,M"))) (clobber (reg:CC 17))] "!TARGET_PARTIAL_REG_STALL && ix86_binary_operator_ok (ASHIFT, QImode, operands)" { switch (get_attr_type (insn)) { case TYPE_LEA: return "#"; case TYPE_ALU: if (operands[2] != const1_rtx) abort (); if (REG_P (operands[1]) && !ANY_QI_REG_P (operands[1])) return "add{l}\t{%k0, %k0|%k0, %k0}"; else return "add{b}\t{%0, %0|%0, %0}"; default: if (REG_P (operands[2])) { if (get_attr_mode (insn) == MODE_SI) return "sal{l}\t{%b2, %k0|%k0, %b2}"; else return "sal{b}\t{%b2, %0|%0, %b2}"; } else if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 1 && (TARGET_SHIFT1 || optimize_size)) { if (get_attr_mode (insn) == MODE_SI) return "sal{l}\t%0"; else return "sal{b}\t%0"; } else { if (get_attr_mode (insn) == MODE_SI) return "sal{l}\t{%2, %k0|%k0, %2}"; else return "sal{b}\t{%2, %0|%0, %2}"; } } } [(set (attr "type") (cond [(eq_attr "alternative" "2") (const_string "lea") (and (and (ne (symbol_ref "TARGET_DOUBLE_WITH_ADD") (const_int 0)) (match_operand 0 "register_operand" "")) (match_operand 2 "const1_operand" "")) (const_string "alu") ] (const_string "ishift"))) (set_attr "mode" "QI,SI,SI")]) (define_insn "*ashlqi3_1" [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,r") (ashift:QI (match_operand:QI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "cI,cI"))) (clobber (reg:CC 17))] "TARGET_PARTIAL_REG_STALL && ix86_binary_operator_ok (ASHIFT, QImode, operands)" { switch (get_attr_type (insn)) { case TYPE_ALU: if (operands[2] != const1_rtx) abort (); if (REG_P (operands[1]) && !ANY_QI_REG_P (operands[1])) return "add{l}\t{%k0, %k0|%k0, %k0}"; else return "add{b}\t{%0, %0|%0, %0}"; default: if (REG_P (operands[2])) { if (get_attr_mode (insn) == MODE_SI) return "sal{l}\t{%b2, %k0|%k0, %b2}"; else return "sal{b}\t{%b2, %0|%0, %b2}"; } else if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 1 && (TARGET_SHIFT1 || optimize_size)) { if (get_attr_mode (insn) == MODE_SI) return "sal{l}\t%0"; else return "sal{b}\t%0"; } else { if (get_attr_mode (insn) == MODE_SI) return "sal{l}\t{%2, %k0|%k0, %2}"; else return "sal{b}\t{%2, %0|%0, %2}"; } } } [(set (attr "type") (cond [(and (and (ne (symbol_ref "TARGET_DOUBLE_WITH_ADD") (const_int 0)) (match_operand 0 "register_operand" "")) (match_operand 2 "const1_operand" "")) (const_string "alu") ] (const_string "ishift"))) (set_attr "mode" "QI,SI")]) ;; This pattern can't accept a variable shift count, since shifts by ;; zero don't affect the flags. We assume that shifts by constant ;; zero are optimized away. (define_insn "*ashlqi3_cmp" [(set (reg 17) (compare (ashift:QI (match_operand:QI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const_int_1_31_operand" "I")) (const_int 0))) (set (match_operand:QI 0 "nonimmediate_operand" "=qm") (ashift:QI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (ASHIFT, QImode, operands)" { switch (get_attr_type (insn)) { case TYPE_ALU: if (operands[2] != const1_rtx) abort (); return "add{b}\t{%0, %0|%0, %0}"; default: if (REG_P (operands[2])) return "sal{b}\t{%b2, %0|%0, %b2}"; else if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 1 && (TARGET_SHIFT1 || optimize_size)) return "sal{b}\t%0"; else return "sal{b}\t{%2, %0|%0, %2}"; } } [(set (attr "type") (cond [(and (and (ne (symbol_ref "TARGET_DOUBLE_WITH_ADD") (const_int 0)) (match_operand 0 "register_operand" "")) (match_operand 2 "const1_operand" "")) (const_string "alu") ] (const_string "ishift"))) (set_attr "mode" "QI")]) ;; See comment above `ashldi3' about how this works. (define_expand "ashrdi3" [(parallel [(set (match_operand:DI 0 "shiftdi_operand" "") (ashiftrt:DI (match_operand:DI 1 "shiftdi_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))])] "" { if (!TARGET_64BIT && TARGET_CMOVE && ! immediate_operand (operands[2], QImode)) { emit_insn (gen_ashrdi3_1 (operands[0], operands[1], operands[2])); DONE; } ix86_expand_binary_operator (ASHIFTRT, DImode, operands); DONE; }) (define_insn "ashrdi3_63_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=*d,rm") (ashiftrt:DI (match_operand:DI 1 "nonimmediate_operand" "*a,0") (match_operand:DI 2 "const_int_operand" "i,i"))) (clobber (reg:CC 17))] "TARGET_64BIT && INTVAL (operands[2]) == 63 && (TARGET_USE_CLTD || optimize_size) && ix86_binary_operator_ok (ASHIFTRT, DImode, operands)" "@ {cqto|cqo} sar{q}\t{%2, %0|%0, %2}" [(set_attr "type" "imovx,ishift") (set_attr "prefix_0f" "0,*") (set_attr "length_immediate" "0,*") (set_attr "modrm" "0,1") (set_attr "mode" "DI")]) (define_insn "*ashrdi3_1_one_bit_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=rm") (ashiftrt:DI (match_operand:DI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" ""))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (ASHIFTRT, DImode, operands) && (TARGET_SHIFT1 || optimize_size)" "sar{q}\t%0" [(set_attr "type" "ishift") (set (attr "length") (if_then_else (match_operand:DI 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*ashrdi3_1_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=rm,rm") (ashiftrt:DI (match_operand:DI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "J,c"))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (ASHIFTRT, DImode, operands)" "@ sar{q}\t{%2, %0|%0, %2} sar{q}\t{%b2, %0|%0, %b2}" [(set_attr "type" "ishift") (set_attr "mode" "DI")]) ;; This pattern can't accept a variable shift count, since shifts by ;; zero don't affect the flags. We assume that shifts by constant ;; zero are optimized away. (define_insn "*ashrdi3_one_bit_cmp_rex64" [(set (reg 17) (compare (ashiftrt:DI (match_operand:DI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" "")) (const_int 0))) (set (match_operand:DI 0 "nonimmediate_operand" "=rm") (ashiftrt:DI (match_dup 1) (match_dup 2)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCGOCmode) && (TARGET_SHIFT1 || optimize_size) && ix86_binary_operator_ok (ASHIFTRT, DImode, operands)" "sar{q}\t%0" [(set_attr "type" "ishift") (set (attr "length") (if_then_else (match_operand:DI 0 "register_operand" "") (const_string "2") (const_string "*")))]) ;; This pattern can't accept a variable shift count, since shifts by ;; zero don't affect the flags. We assume that shifts by constant ;; zero are optimized away. (define_insn "*ashrdi3_cmp_rex64" [(set (reg 17) (compare (ashiftrt:DI (match_operand:DI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const_int_operand" "n")) (const_int 0))) (set (match_operand:DI 0 "nonimmediate_operand" "=rm") (ashiftrt:DI (match_dup 1) (match_dup 2)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (ASHIFTRT, DImode, operands)" "sar{q}\t{%2, %0|%0, %2}" [(set_attr "type" "ishift") (set_attr "mode" "DI")]) (define_insn "ashrdi3_1" [(set (match_operand:DI 0 "register_operand" "=r") (ashiftrt:DI (match_operand:DI 1 "register_operand" "0") (match_operand:QI 2 "nonmemory_operand" "Jc"))) (clobber (match_scratch:SI 3 "=&r")) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_CMOVE" "#" [(set_attr "type" "multi")]) (define_insn "*ashrdi3_2" [(set (match_operand:DI 0 "register_operand" "=r") (ashiftrt:DI (match_operand:DI 1 "register_operand" "0") (match_operand:QI 2 "nonmemory_operand" "Jc"))) (clobber (reg:CC 17))] "!TARGET_64BIT" "#" [(set_attr "type" "multi")]) (define_split [(set (match_operand:DI 0 "register_operand" "") (ashiftrt:DI (match_operand:DI 1 "register_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (match_scratch:SI 3 "")) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_CMOVE && reload_completed" [(const_int 0)] "ix86_split_ashrdi (operands, operands[3]); DONE;") (define_split [(set (match_operand:DI 0 "register_operand" "") (ashiftrt:DI (match_operand:DI 1 "register_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "!TARGET_64BIT && reload_completed" [(const_int 0)] "ix86_split_ashrdi (operands, NULL_RTX); DONE;") (define_insn "x86_shrd_1" [(set (match_operand:SI 0 "nonimmediate_operand" "+r*m,r*m") (ior:SI (ashiftrt:SI (match_dup 0) (match_operand:QI 2 "nonmemory_operand" "I,c")) (ashift:SI (match_operand:SI 1 "register_operand" "r,r") (minus:QI (const_int 32) (match_dup 2))))) (clobber (reg:CC 17))] "" "@ shrd{l}\t{%2, %1, %0|%0, %1, %2} shrd{l}\t{%s2%1, %0|%0, %1, %2}" [(set_attr "type" "ishift") (set_attr "prefix_0f" "1") (set_attr "pent_pair" "np") (set_attr "ppro_uops" "few") (set_attr "mode" "SI")]) (define_expand "x86_shift_adj_3" [(use (match_operand:SI 0 "register_operand" "")) (use (match_operand:SI 1 "register_operand" "")) (use (match_operand:QI 2 "register_operand" ""))] "" { rtx label = gen_label_rtx (); rtx tmp; emit_insn (gen_testqi_ccz_1 (operands[2], GEN_INT (32))); tmp = gen_rtx_REG (CCZmode, FLAGS_REG); tmp = gen_rtx_EQ (VOIDmode, tmp, const0_rtx); tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp, gen_rtx_LABEL_REF (VOIDmode, label), pc_rtx); tmp = emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp)); JUMP_LABEL (tmp) = label; emit_move_insn (operands[0], operands[1]); emit_insn (gen_ashrsi3_31 (operands[1], operands[1], GEN_INT (31))); emit_label (label); LABEL_NUSES (label) = 1; DONE; }) (define_insn "ashrsi3_31" [(set (match_operand:SI 0 "nonimmediate_operand" "=*d,rm") (ashiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "*a,0") (match_operand:SI 2 "const_int_operand" "i,i"))) (clobber (reg:CC 17))] "INTVAL (operands[2]) == 31 && (TARGET_USE_CLTD || optimize_size) && ix86_binary_operator_ok (ASHIFTRT, SImode, operands)" "@ {cltd|cdq} sar{l}\t{%2, %0|%0, %2}" [(set_attr "type" "imovx,ishift") (set_attr "prefix_0f" "0,*") (set_attr "length_immediate" "0,*") (set_attr "modrm" "0,1") (set_attr "mode" "SI")]) (define_insn "*ashrsi3_31_zext" [(set (match_operand:DI 0 "register_operand" "=*d,r") (zero_extend:DI (ashiftrt:SI (match_operand:SI 1 "register_operand" "*a,0") (match_operand:SI 2 "const_int_operand" "i,i")))) (clobber (reg:CC 17))] "TARGET_64BIT && (TARGET_USE_CLTD || optimize_size) && INTVAL (operands[2]) == 31 && ix86_binary_operator_ok (ASHIFTRT, SImode, operands)" "@ {cltd|cdq} sar{l}\t{%2, %k0|%k0, %2}" [(set_attr "type" "imovx,ishift") (set_attr "prefix_0f" "0,*") (set_attr "length_immediate" "0,*") (set_attr "modrm" "0,1") (set_attr "mode" "SI")]) (define_expand "ashrsi3" [(set (match_operand:SI 0 "nonimmediate_operand" "") (ashiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "" "ix86_expand_binary_operator (ASHIFTRT, SImode, operands); DONE;") (define_insn "*ashrsi3_1_one_bit" [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") (ashiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" ""))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (ASHIFTRT, SImode, operands) && (TARGET_SHIFT1 || optimize_size)" "sar{l}\t%0" [(set_attr "type" "ishift") (set (attr "length") (if_then_else (match_operand:SI 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*ashrsi3_1_one_bit_zext" [(set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (ashiftrt:SI (match_operand:SI 1 "register_operand" "0") (match_operand:QI 2 "const1_operand" "")))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (ASHIFTRT, SImode, operands) && (TARGET_SHIFT1 || optimize_size)" "sar{l}\t%k0" [(set_attr "type" "ishift") (set_attr "length" "2")]) (define_insn "*ashrsi3_1" [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm") (ashiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "I,c"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (ASHIFTRT, SImode, operands)" "@ sar{l}\t{%2, %0|%0, %2} sar{l}\t{%b2, %0|%0, %b2}" [(set_attr "type" "ishift") (set_attr "mode" "SI")]) (define_insn "*ashrsi3_1_zext" [(set (match_operand:DI 0 "register_operand" "=r,r") (zero_extend:DI (ashiftrt:SI (match_operand:SI 1 "register_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "I,c")))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (ASHIFTRT, SImode, operands)" "@ sar{l}\t{%2, %k0|%k0, %2} sar{l}\t{%b2, %k0|%k0, %b2}" [(set_attr "type" "ishift") (set_attr "mode" "SI")]) ;; This pattern can't accept a variable shift count, since shifts by ;; zero don't affect the flags. We assume that shifts by constant ;; zero are optimized away. (define_insn "*ashrsi3_one_bit_cmp" [(set (reg 17) (compare (ashiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" "")) (const_int 0))) (set (match_operand:SI 0 "nonimmediate_operand" "=rm") (ashiftrt:SI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && (TARGET_SHIFT1 || optimize_size) && ix86_binary_operator_ok (ASHIFTRT, SImode, operands)" "sar{l}\t%0" [(set_attr "type" "ishift") (set (attr "length") (if_then_else (match_operand:SI 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*ashrsi3_one_bit_cmp_zext" [(set (reg 17) (compare (ashiftrt:SI (match_operand:SI 1 "register_operand" "0") (match_operand:QI 2 "const1_operand" "")) (const_int 0))) (set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (ashiftrt:SI (match_dup 1) (match_dup 2))))] "TARGET_64BIT && ix86_match_ccmode (insn, CCmode) && (TARGET_SHIFT1 || optimize_size) && ix86_binary_operator_ok (ASHIFTRT, SImode, operands)" "sar{l}\t%k0" [(set_attr "type" "ishift") (set_attr "length" "2")]) ;; This pattern can't accept a variable shift count, since shifts by ;; zero don't affect the flags. We assume that shifts by constant ;; zero are optimized away. (define_insn "*ashrsi3_cmp" [(set (reg 17) (compare (ashiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const_int_1_31_operand" "I")) (const_int 0))) (set (match_operand:SI 0 "nonimmediate_operand" "=rm") (ashiftrt:SI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (ASHIFTRT, SImode, operands)" "sar{l}\t{%2, %0|%0, %2}" [(set_attr "type" "ishift") (set_attr "mode" "SI")]) (define_insn "*ashrsi3_cmp_zext" [(set (reg 17) (compare (ashiftrt:SI (match_operand:SI 1 "register_operand" "0") (match_operand:QI 2 "const_int_1_31_operand" "I")) (const_int 0))) (set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (ashiftrt:SI (match_dup 1) (match_dup 2))))] "TARGET_64BIT && ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (ASHIFTRT, SImode, operands)" "sar{l}\t{%2, %k0|%k0, %2}" [(set_attr "type" "ishift") (set_attr "mode" "SI")]) (define_expand "ashrhi3" [(set (match_operand:HI 0 "nonimmediate_operand" "") (ashiftrt:HI (match_operand:HI 1 "nonimmediate_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "TARGET_HIMODE_MATH" "ix86_expand_binary_operator (ASHIFTRT, HImode, operands); DONE;") (define_insn "*ashrhi3_1_one_bit" [(set (match_operand:HI 0 "nonimmediate_operand" "=rm") (ashiftrt:HI (match_operand:HI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" ""))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (ASHIFTRT, HImode, operands) && (TARGET_SHIFT1 || optimize_size)" "sar{w}\t%0" [(set_attr "type" "ishift") (set (attr "length") (if_then_else (match_operand 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*ashrhi3_1" [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,rm") (ashiftrt:HI (match_operand:HI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "I,c"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (ASHIFTRT, HImode, operands)" "@ sar{w}\t{%2, %0|%0, %2} sar{w}\t{%b2, %0|%0, %b2}" [(set_attr "type" "ishift") (set_attr "mode" "HI")]) ;; This pattern can't accept a variable shift count, since shifts by ;; zero don't affect the flags. We assume that shifts by constant ;; zero are optimized away. (define_insn "*ashrhi3_one_bit_cmp" [(set (reg 17) (compare (ashiftrt:HI (match_operand:HI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" "")) (const_int 0))) (set (match_operand:HI 0 "nonimmediate_operand" "=rm") (ashiftrt:HI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && (TARGET_SHIFT1 || optimize_size) && ix86_binary_operator_ok (ASHIFTRT, HImode, operands)" "sar{w}\t%0" [(set_attr "type" "ishift") (set (attr "length") (if_then_else (match_operand 0 "register_operand" "") (const_string "2") (const_string "*")))]) ;; This pattern can't accept a variable shift count, since shifts by ;; zero don't affect the flags. We assume that shifts by constant ;; zero are optimized away. (define_insn "*ashrhi3_cmp" [(set (reg 17) (compare (ashiftrt:HI (match_operand:HI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const_int_1_31_operand" "I")) (const_int 0))) (set (match_operand:HI 0 "nonimmediate_operand" "=rm") (ashiftrt:HI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (ASHIFTRT, HImode, operands)" "sar{w}\t{%2, %0|%0, %2}" [(set_attr "type" "ishift") (set_attr "mode" "HI")]) (define_expand "ashrqi3" [(set (match_operand:QI 0 "nonimmediate_operand" "") (ashiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "TARGET_QIMODE_MATH" "ix86_expand_binary_operator (ASHIFTRT, QImode, operands); DONE;") (define_insn "*ashrqi3_1_one_bit" [(set (match_operand:QI 0 "nonimmediate_operand" "=qm") (ashiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" ""))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (ASHIFTRT, QImode, operands) && (TARGET_SHIFT1 || optimize_size)" "sar{b}\t%0" [(set_attr "type" "ishift") (set (attr "length") (if_then_else (match_operand 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*ashrqi3_1_one_bit_slp" [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "+qm")) (ashiftrt:QI (match_dup 0) (match_operand:QI 1 "const1_operand" ""))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (ASHIFTRT, QImode, operands) && (! TARGET_PARTIAL_REG_STALL || optimize_size) && (TARGET_SHIFT1 || optimize_size)" "sar{b}\t%0" [(set_attr "type" "ishift1") (set (attr "length") (if_then_else (match_operand 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*ashrqi3_1" [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,qm") (ashiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "I,c"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (ASHIFTRT, QImode, operands)" "@ sar{b}\t{%2, %0|%0, %2} sar{b}\t{%b2, %0|%0, %b2}" [(set_attr "type" "ishift") (set_attr "mode" "QI")]) (define_insn "*ashrqi3_1_slp" [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "+qm,qm")) (ashiftrt:QI (match_dup 0) (match_operand:QI 1 "nonmemory_operand" "I,c"))) (clobber (reg:CC 17))] "(! TARGET_PARTIAL_REG_STALL || optimize_size) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "@ sar{b}\t{%1, %0|%0, %1} sar{b}\t{%b1, %0|%0, %b1}" [(set_attr "type" "ishift1") (set_attr "mode" "QI")]) ;; This pattern can't accept a variable shift count, since shifts by ;; zero don't affect the flags. We assume that shifts by constant ;; zero are optimized away. (define_insn "*ashrqi3_one_bit_cmp" [(set (reg 17) (compare (ashiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" "I")) (const_int 0))) (set (match_operand:QI 0 "nonimmediate_operand" "=qm") (ashiftrt:QI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && (TARGET_SHIFT1 || optimize_size) && ix86_binary_operator_ok (ASHIFTRT, QImode, operands)" "sar{b}\t%0" [(set_attr "type" "ishift") (set (attr "length") (if_then_else (match_operand 0 "register_operand" "") (const_string "2") (const_string "*")))]) ;; This pattern can't accept a variable shift count, since shifts by ;; zero don't affect the flags. We assume that shifts by constant ;; zero are optimized away. (define_insn "*ashrqi3_cmp" [(set (reg 17) (compare (ashiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const_int_1_31_operand" "I")) (const_int 0))) (set (match_operand:QI 0 "nonimmediate_operand" "=qm") (ashiftrt:QI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (ASHIFTRT, QImode, operands)" "sar{b}\t{%2, %0|%0, %2}" [(set_attr "type" "ishift") (set_attr "mode" "QI")]) ;; Logical shift instructions ;; See comment above `ashldi3' about how this works. (define_expand "lshrdi3" [(parallel [(set (match_operand:DI 0 "shiftdi_operand" "") (lshiftrt:DI (match_operand:DI 1 "shiftdi_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))])] "" { if (!TARGET_64BIT && TARGET_CMOVE && ! immediate_operand (operands[2], QImode)) { emit_insn (gen_lshrdi3_1 (operands[0], operands[1], operands[2])); DONE; } ix86_expand_binary_operator (LSHIFTRT, DImode, operands); DONE; }) (define_insn "*lshrdi3_1_one_bit_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=rm") (lshiftrt:DI (match_operand:DI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" ""))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (LSHIFTRT, HImode, operands) && (TARGET_SHIFT1 || optimize_size)" "shr{q}\t%0" [(set_attr "type" "ishift") (set (attr "length") (if_then_else (match_operand:DI 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*lshrdi3_1_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=rm,rm") (lshiftrt:DI (match_operand:DI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "J,c"))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (LSHIFTRT, HImode, operands)" "@ shr{q}\t{%2, %0|%0, %2} shr{q}\t{%b2, %0|%0, %b2}" [(set_attr "type" "ishift") (set_attr "mode" "DI")]) ;; This pattern can't accept a variable shift count, since shifts by ;; zero don't affect the flags. We assume that shifts by constant ;; zero are optimized away. (define_insn "*lshrdi3_cmp_one_bit_rex64" [(set (reg 17) (compare (lshiftrt:DI (match_operand:DI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" "")) (const_int 0))) (set (match_operand:DI 0 "nonimmediate_operand" "=rm") (lshiftrt:DI (match_dup 1) (match_dup 2)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCGOCmode) && (TARGET_SHIFT1 || optimize_size) && ix86_binary_operator_ok (LSHIFTRT, HImode, operands)" "shr{q}\t%0" [(set_attr "type" "ishift") (set (attr "length") (if_then_else (match_operand:DI 0 "register_operand" "") (const_string "2") (const_string "*")))]) ;; This pattern can't accept a variable shift count, since shifts by ;; zero don't affect the flags. We assume that shifts by constant ;; zero are optimized away. (define_insn "*lshrdi3_cmp_rex64" [(set (reg 17) (compare (lshiftrt:DI (match_operand:DI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const_int_operand" "e")) (const_int 0))) (set (match_operand:DI 0 "nonimmediate_operand" "=rm") (lshiftrt:DI (match_dup 1) (match_dup 2)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (LSHIFTRT, HImode, operands)" "shr{q}\t{%2, %0|%0, %2}" [(set_attr "type" "ishift") (set_attr "mode" "DI")]) (define_insn "lshrdi3_1" [(set (match_operand:DI 0 "register_operand" "=r") (lshiftrt:DI (match_operand:DI 1 "register_operand" "0") (match_operand:QI 2 "nonmemory_operand" "Jc"))) (clobber (match_scratch:SI 3 "=&r")) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_CMOVE" "#" [(set_attr "type" "multi")]) (define_insn "*lshrdi3_2" [(set (match_operand:DI 0 "register_operand" "=r") (lshiftrt:DI (match_operand:DI 1 "register_operand" "0") (match_operand:QI 2 "nonmemory_operand" "Jc"))) (clobber (reg:CC 17))] "!TARGET_64BIT" "#" [(set_attr "type" "multi")]) (define_split [(set (match_operand:DI 0 "register_operand" "") (lshiftrt:DI (match_operand:DI 1 "register_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (match_scratch:SI 3 "")) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_CMOVE && reload_completed" [(const_int 0)] "ix86_split_lshrdi (operands, operands[3]); DONE;") (define_split [(set (match_operand:DI 0 "register_operand" "") (lshiftrt:DI (match_operand:DI 1 "register_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "!TARGET_64BIT && reload_completed" [(const_int 0)] "ix86_split_lshrdi (operands, NULL_RTX); DONE;") (define_expand "lshrsi3" [(set (match_operand:SI 0 "nonimmediate_operand" "") (lshiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "" "ix86_expand_binary_operator (LSHIFTRT, SImode, operands); DONE;") (define_insn "*lshrsi3_1_one_bit" [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") (lshiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" ""))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (LSHIFTRT, HImode, operands) && (TARGET_SHIFT1 || optimize_size)" "shr{l}\t%0" [(set_attr "type" "ishift") (set (attr "length") (if_then_else (match_operand:SI 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*lshrsi3_1_one_bit_zext" [(set (match_operand:DI 0 "register_operand" "=r") (lshiftrt:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "0")) (match_operand:QI 2 "const1_operand" ""))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (LSHIFTRT, HImode, operands) && (TARGET_SHIFT1 || optimize_size)" "shr{l}\t%k0" [(set_attr "type" "ishift") (set_attr "length" "2")]) (define_insn "*lshrsi3_1" [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm") (lshiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "I,c"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (LSHIFTRT, HImode, operands)" "@ shr{l}\t{%2, %0|%0, %2} shr{l}\t{%b2, %0|%0, %b2}" [(set_attr "type" "ishift") (set_attr "mode" "SI")]) (define_insn "*lshrsi3_1_zext" [(set (match_operand:DI 0 "register_operand" "=r,r") (zero_extend:DI (lshiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "I,c")))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (LSHIFTRT, HImode, operands)" "@ shr{l}\t{%2, %k0|%k0, %2} shr{l}\t{%b2, %k0|%k0, %b2}" [(set_attr "type" "ishift") (set_attr "mode" "SI")]) ;; This pattern can't accept a variable shift count, since shifts by ;; zero don't affect the flags. We assume that shifts by constant ;; zero are optimized away. (define_insn "*lshrsi3_one_bit_cmp" [(set (reg 17) (compare (lshiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" "")) (const_int 0))) (set (match_operand:SI 0 "nonimmediate_operand" "=rm") (lshiftrt:SI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && (TARGET_SHIFT1 || optimize_size) && ix86_binary_operator_ok (LSHIFTRT, HImode, operands)" "shr{l}\t%0" [(set_attr "type" "ishift") (set (attr "length") (if_then_else (match_operand:SI 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*lshrsi3_cmp_one_bit_zext" [(set (reg 17) (compare (lshiftrt:SI (match_operand:SI 1 "register_operand" "0") (match_operand:QI 2 "const1_operand" "")) (const_int 0))) (set (match_operand:DI 0 "register_operand" "=r") (lshiftrt:DI (zero_extend:DI (match_dup 1)) (match_dup 2)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCGOCmode) && (TARGET_SHIFT1 || optimize_size) && ix86_binary_operator_ok (LSHIFTRT, HImode, operands)" "shr{l}\t%k0" [(set_attr "type" "ishift") (set_attr "length" "2")]) ;; This pattern can't accept a variable shift count, since shifts by ;; zero don't affect the flags. We assume that shifts by constant ;; zero are optimized away. (define_insn "*lshrsi3_cmp" [(set (reg 17) (compare (lshiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const_int_1_31_operand" "I")) (const_int 0))) (set (match_operand:SI 0 "nonimmediate_operand" "=rm") (lshiftrt:SI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (LSHIFTRT, HImode, operands)" "shr{l}\t{%2, %0|%0, %2}" [(set_attr "type" "ishift") (set_attr "mode" "SI")]) (define_insn "*lshrsi3_cmp_zext" [(set (reg 17) (compare (lshiftrt:SI (match_operand:SI 1 "register_operand" "0") (match_operand:QI 2 "const_int_1_31_operand" "I")) (const_int 0))) (set (match_operand:DI 0 "register_operand" "=r") (lshiftrt:DI (zero_extend:DI (match_dup 1)) (match_dup 2)))] "TARGET_64BIT && ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (LSHIFTRT, HImode, operands)" "shr{l}\t{%2, %k0|%k0, %2}" [(set_attr "type" "ishift") (set_attr "mode" "SI")]) (define_expand "lshrhi3" [(set (match_operand:HI 0 "nonimmediate_operand" "") (lshiftrt:HI (match_operand:HI 1 "nonimmediate_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "TARGET_HIMODE_MATH" "ix86_expand_binary_operator (LSHIFTRT, HImode, operands); DONE;") (define_insn "*lshrhi3_1_one_bit" [(set (match_operand:HI 0 "nonimmediate_operand" "=rm") (lshiftrt:HI (match_operand:HI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" ""))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (LSHIFTRT, HImode, operands) && (TARGET_SHIFT1 || optimize_size)" "shr{w}\t%0" [(set_attr "type" "ishift") (set (attr "length") (if_then_else (match_operand 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*lshrhi3_1" [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,rm") (lshiftrt:HI (match_operand:HI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "I,c"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (LSHIFTRT, HImode, operands)" "@ shr{w}\t{%2, %0|%0, %2} shr{w}\t{%b2, %0|%0, %b2}" [(set_attr "type" "ishift") (set_attr "mode" "HI")]) ;; This pattern can't accept a variable shift count, since shifts by ;; zero don't affect the flags. We assume that shifts by constant ;; zero are optimized away. (define_insn "*lshrhi3_one_bit_cmp" [(set (reg 17) (compare (lshiftrt:HI (match_operand:HI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" "")) (const_int 0))) (set (match_operand:HI 0 "nonimmediate_operand" "=rm") (lshiftrt:HI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && (TARGET_SHIFT1 || optimize_size) && ix86_binary_operator_ok (LSHIFTRT, HImode, operands)" "shr{w}\t%0" [(set_attr "type" "ishift") (set (attr "length") (if_then_else (match_operand:SI 0 "register_operand" "") (const_string "2") (const_string "*")))]) ;; This pattern can't accept a variable shift count, since shifts by ;; zero don't affect the flags. We assume that shifts by constant ;; zero are optimized away. (define_insn "*lshrhi3_cmp" [(set (reg 17) (compare (lshiftrt:HI (match_operand:HI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const_int_1_31_operand" "I")) (const_int 0))) (set (match_operand:HI 0 "nonimmediate_operand" "=rm") (lshiftrt:HI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (LSHIFTRT, HImode, operands)" "shr{w}\t{%2, %0|%0, %2}" [(set_attr "type" "ishift") (set_attr "mode" "HI")]) (define_expand "lshrqi3" [(set (match_operand:QI 0 "nonimmediate_operand" "") (lshiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "TARGET_QIMODE_MATH" "ix86_expand_binary_operator (LSHIFTRT, QImode, operands); DONE;") (define_insn "*lshrqi3_1_one_bit" [(set (match_operand:QI 0 "nonimmediate_operand" "=qm") (lshiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" ""))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (LSHIFTRT, QImode, operands) && (TARGET_SHIFT1 || optimize_size)" "shr{b}\t%0" [(set_attr "type" "ishift") (set (attr "length") (if_then_else (match_operand 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*lshrqi3_1_one_bit_slp" [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "+qm")) (lshiftrt:QI (match_dup 0) (match_operand:QI 1 "const1_operand" ""))) (clobber (reg:CC 17))] "(! TARGET_PARTIAL_REG_STALL || optimize_size) && (TARGET_SHIFT1 || optimize_size)" "shr{b}\t%0" [(set_attr "type" "ishift1") (set (attr "length") (if_then_else (match_operand 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*lshrqi3_1" [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,qm") (lshiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "I,c"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (LSHIFTRT, QImode, operands)" "@ shr{b}\t{%2, %0|%0, %2} shr{b}\t{%b2, %0|%0, %b2}" [(set_attr "type" "ishift") (set_attr "mode" "QI")]) (define_insn "*lshrqi3_1_slp" [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "+qm,qm")) (lshiftrt:QI (match_dup 0) (match_operand:QI 1 "nonmemory_operand" "I,c"))) (clobber (reg:CC 17))] "(! TARGET_PARTIAL_REG_STALL || optimize_size) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "@ shr{b}\t{%1, %0|%0, %1} shr{b}\t{%b1, %0|%0, %b1}" [(set_attr "type" "ishift1") (set_attr "mode" "QI")]) ;; This pattern can't accept a variable shift count, since shifts by ;; zero don't affect the flags. We assume that shifts by constant ;; zero are optimized away. (define_insn "*lshrqi2_one_bit_cmp" [(set (reg 17) (compare (lshiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" "")) (const_int 0))) (set (match_operand:QI 0 "nonimmediate_operand" "=qm") (lshiftrt:QI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && (TARGET_SHIFT1 || optimize_size) && ix86_binary_operator_ok (LSHIFTRT, QImode, operands)" "shr{b}\t%0" [(set_attr "type" "ishift") (set (attr "length") (if_then_else (match_operand:SI 0 "register_operand" "") (const_string "2") (const_string "*")))]) ;; This pattern can't accept a variable shift count, since shifts by ;; zero don't affect the flags. We assume that shifts by constant ;; zero are optimized away. (define_insn "*lshrqi2_cmp" [(set (reg 17) (compare (lshiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const_int_1_31_operand" "I")) (const_int 0))) (set (match_operand:QI 0 "nonimmediate_operand" "=qm") (lshiftrt:QI (match_dup 1) (match_dup 2)))] "ix86_match_ccmode (insn, CCGOCmode) && ix86_binary_operator_ok (LSHIFTRT, QImode, operands)" "shr{b}\t{%2, %0|%0, %2}" [(set_attr "type" "ishift") (set_attr "mode" "QI")]) ;; Rotate instructions (define_expand "rotldi3" [(set (match_operand:DI 0 "nonimmediate_operand" "") (rotate:DI (match_operand:DI 1 "nonimmediate_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "TARGET_64BIT" "ix86_expand_binary_operator (ROTATE, DImode, operands); DONE;") (define_insn "*rotlsi3_1_one_bit_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=rm") (rotate:DI (match_operand:DI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" ""))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (ROTATE, DImode, operands) && (TARGET_SHIFT1 || optimize_size)" "rol{q}\t%0" [(set_attr "type" "rotate") (set (attr "length") (if_then_else (match_operand:DI 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*rotldi3_1_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=rm,rm") (rotate:DI (match_operand:DI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "e,c"))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (ROTATE, DImode, operands)" "@ rol{q}\t{%2, %0|%0, %2} rol{q}\t{%b2, %0|%0, %b2}" [(set_attr "type" "rotate") (set_attr "mode" "DI")]) (define_expand "rotlsi3" [(set (match_operand:SI 0 "nonimmediate_operand" "") (rotate:SI (match_operand:SI 1 "nonimmediate_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "" "ix86_expand_binary_operator (ROTATE, SImode, operands); DONE;") (define_insn "*rotlsi3_1_one_bit" [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") (rotate:SI (match_operand:SI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" ""))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (ROTATE, SImode, operands) && (TARGET_SHIFT1 || optimize_size)" "rol{l}\t%0" [(set_attr "type" "rotate") (set (attr "length") (if_then_else (match_operand:SI 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*rotlsi3_1_one_bit_zext" [(set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (rotate:SI (match_operand:SI 1 "register_operand" "0") (match_operand:QI 2 "const1_operand" "")))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (ROTATE, SImode, operands) && (TARGET_SHIFT1 || optimize_size)" "rol{l}\t%k0" [(set_attr "type" "rotate") (set_attr "length" "2")]) (define_insn "*rotlsi3_1" [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm") (rotate:SI (match_operand:SI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "I,c"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (ROTATE, SImode, operands)" "@ rol{l}\t{%2, %0|%0, %2} rol{l}\t{%b2, %0|%0, %b2}" [(set_attr "type" "rotate") (set_attr "mode" "SI")]) (define_insn "*rotlsi3_1_zext" [(set (match_operand:DI 0 "register_operand" "=r,r") (zero_extend:DI (rotate:SI (match_operand:SI 1 "register_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "I,c")))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (ROTATE, SImode, operands)" "@ rol{l}\t{%2, %k0|%k0, %2} rol{l}\t{%b2, %k0|%k0, %b2}" [(set_attr "type" "rotate") (set_attr "mode" "SI")]) (define_expand "rotlhi3" [(set (match_operand:HI 0 "nonimmediate_operand" "") (rotate:HI (match_operand:HI 1 "nonimmediate_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "TARGET_HIMODE_MATH" "ix86_expand_binary_operator (ROTATE, HImode, operands); DONE;") (define_insn "*rotlhi3_1_one_bit" [(set (match_operand:HI 0 "nonimmediate_operand" "=rm") (rotate:HI (match_operand:HI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" ""))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (ROTATE, HImode, operands) && (TARGET_SHIFT1 || optimize_size)" "rol{w}\t%0" [(set_attr "type" "rotate") (set (attr "length") (if_then_else (match_operand 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*rotlhi3_1" [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,rm") (rotate:HI (match_operand:HI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "I,c"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (ROTATE, HImode, operands)" "@ rol{w}\t{%2, %0|%0, %2} rol{w}\t{%b2, %0|%0, %b2}" [(set_attr "type" "rotate") (set_attr "mode" "HI")]) (define_expand "rotlqi3" [(set (match_operand:QI 0 "nonimmediate_operand" "") (rotate:QI (match_operand:QI 1 "nonimmediate_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "TARGET_QIMODE_MATH" "ix86_expand_binary_operator (ROTATE, QImode, operands); DONE;") (define_insn "*rotlqi3_1_one_bit_slp" [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "+qm")) (rotate:QI (match_dup 0) (match_operand:QI 1 "const1_operand" ""))) (clobber (reg:CC 17))] "(! TARGET_PARTIAL_REG_STALL || optimize_size) && (TARGET_SHIFT1 || optimize_size)" "rol{b}\t%0" [(set_attr "type" "rotate1") (set (attr "length") (if_then_else (match_operand 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*rotlqi3_1_one_bit" [(set (match_operand:QI 0 "nonimmediate_operand" "=qm") (rotate:QI (match_operand:QI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" ""))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (ROTATE, QImode, operands) && (TARGET_SHIFT1 || optimize_size)" "rol{b}\t%0" [(set_attr "type" "rotate") (set (attr "length") (if_then_else (match_operand 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*rotlqi3_1_slp" [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "+qm,qm")) (rotate:QI (match_dup 0) (match_operand:QI 1 "nonmemory_operand" "I,c"))) (clobber (reg:CC 17))] "(! TARGET_PARTIAL_REG_STALL || optimize_size) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "@ rol{b}\t{%1, %0|%0, %1} rol{b}\t{%b1, %0|%0, %b1}" [(set_attr "type" "rotate1") (set_attr "mode" "QI")]) (define_insn "*rotlqi3_1" [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,qm") (rotate:QI (match_operand:QI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "I,c"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (ROTATE, QImode, operands)" "@ rol{b}\t{%2, %0|%0, %2} rol{b}\t{%b2, %0|%0, %b2}" [(set_attr "type" "rotate") (set_attr "mode" "QI")]) (define_expand "rotrdi3" [(set (match_operand:DI 0 "nonimmediate_operand" "") (rotatert:DI (match_operand:DI 1 "nonimmediate_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "TARGET_64BIT" "ix86_expand_binary_operator (ROTATERT, DImode, operands); DONE;") (define_insn "*rotrdi3_1_one_bit_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=rm") (rotatert:DI (match_operand:DI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" ""))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (ROTATERT, DImode, operands) && (TARGET_SHIFT1 || optimize_size)" "ror{q}\t%0" [(set_attr "type" "rotate") (set (attr "length") (if_then_else (match_operand:DI 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*rotrdi3_1_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=rm,rm") (rotatert:DI (match_operand:DI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "J,c"))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (ROTATERT, DImode, operands)" "@ ror{q}\t{%2, %0|%0, %2} ror{q}\t{%b2, %0|%0, %b2}" [(set_attr "type" "rotate") (set_attr "mode" "DI")]) (define_expand "rotrsi3" [(set (match_operand:SI 0 "nonimmediate_operand" "") (rotatert:SI (match_operand:SI 1 "nonimmediate_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "" "ix86_expand_binary_operator (ROTATERT, SImode, operands); DONE;") (define_insn "*rotrsi3_1_one_bit" [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") (rotatert:SI (match_operand:SI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" ""))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (ROTATERT, SImode, operands) && (TARGET_SHIFT1 || optimize_size)" "ror{l}\t%0" [(set_attr "type" "rotate") (set (attr "length") (if_then_else (match_operand:SI 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*rotrsi3_1_one_bit_zext" [(set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (rotatert:SI (match_operand:SI 1 "register_operand" "0") (match_operand:QI 2 "const1_operand" "")))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (ROTATERT, SImode, operands) && (TARGET_SHIFT1 || optimize_size)" "ror{l}\t%k0" [(set_attr "type" "rotate") (set (attr "length") (if_then_else (match_operand:SI 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*rotrsi3_1" [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm") (rotatert:SI (match_operand:SI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "I,c"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (ROTATERT, SImode, operands)" "@ ror{l}\t{%2, %0|%0, %2} ror{l}\t{%b2, %0|%0, %b2}" [(set_attr "type" "rotate") (set_attr "mode" "SI")]) (define_insn "*rotrsi3_1_zext" [(set (match_operand:DI 0 "register_operand" "=r,r") (zero_extend:DI (rotatert:SI (match_operand:SI 1 "register_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "I,c")))) (clobber (reg:CC 17))] "TARGET_64BIT && ix86_binary_operator_ok (ROTATERT, SImode, operands)" "@ ror{l}\t{%2, %k0|%k0, %2} ror{l}\t{%b2, %k0|%k0, %b2}" [(set_attr "type" "rotate") (set_attr "mode" "SI")]) (define_expand "rotrhi3" [(set (match_operand:HI 0 "nonimmediate_operand" "") (rotatert:HI (match_operand:HI 1 "nonimmediate_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "TARGET_HIMODE_MATH" "ix86_expand_binary_operator (ROTATERT, HImode, operands); DONE;") (define_insn "*rotrhi3_one_bit" [(set (match_operand:HI 0 "nonimmediate_operand" "=rm") (rotatert:HI (match_operand:HI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" ""))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (ROTATERT, HImode, operands) && (TARGET_SHIFT1 || optimize_size)" "ror{w}\t%0" [(set_attr "type" "rotate") (set (attr "length") (if_then_else (match_operand 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*rotrhi3" [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,rm") (rotatert:HI (match_operand:HI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "I,c"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (ROTATERT, HImode, operands)" "@ ror{w}\t{%2, %0|%0, %2} ror{w}\t{%b2, %0|%0, %b2}" [(set_attr "type" "rotate") (set_attr "mode" "HI")]) (define_expand "rotrqi3" [(set (match_operand:QI 0 "nonimmediate_operand" "") (rotatert:QI (match_operand:QI 1 "nonimmediate_operand" "") (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC 17))] "TARGET_QIMODE_MATH" "ix86_expand_binary_operator (ROTATERT, QImode, operands); DONE;") (define_insn "*rotrqi3_1_one_bit" [(set (match_operand:QI 0 "nonimmediate_operand" "=qm") (rotatert:QI (match_operand:QI 1 "nonimmediate_operand" "0") (match_operand:QI 2 "const1_operand" ""))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (ROTATERT, QImode, operands) && (TARGET_SHIFT1 || optimize_size)" "ror{b}\t%0" [(set_attr "type" "rotate") (set (attr "length") (if_then_else (match_operand 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*rotrqi3_1_one_bit_slp" [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "+qm")) (rotatert:QI (match_dup 0) (match_operand:QI 1 "const1_operand" ""))) (clobber (reg:CC 17))] "(! TARGET_PARTIAL_REG_STALL || optimize_size) && (TARGET_SHIFT1 || optimize_size)" "ror{b}\t%0" [(set_attr "type" "rotate1") (set (attr "length") (if_then_else (match_operand 0 "register_operand" "") (const_string "2") (const_string "*")))]) (define_insn "*rotrqi3_1" [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,qm") (rotatert:QI (match_operand:QI 1 "nonimmediate_operand" "0,0") (match_operand:QI 2 "nonmemory_operand" "I,c"))) (clobber (reg:CC 17))] "ix86_binary_operator_ok (ROTATERT, QImode, operands)" "@ ror{b}\t{%2, %0|%0, %2} ror{b}\t{%b2, %0|%0, %b2}" [(set_attr "type" "rotate") (set_attr "mode" "QI")]) (define_insn "*rotrqi3_1_slp" [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "+qm,qm")) (rotatert:QI (match_dup 0) (match_operand:QI 1 "nonmemory_operand" "I,c"))) (clobber (reg:CC 17))] "(! TARGET_PARTIAL_REG_STALL || optimize_size) && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "@ ror{b}\t{%1, %0|%0, %1} ror{b}\t{%b1, %0|%0, %b1}" [(set_attr "type" "rotate1") (set_attr "mode" "QI")]) ;; Bit set / bit test instructions (define_expand "extv" [(set (match_operand:SI 0 "register_operand" "") (sign_extract:SI (match_operand:SI 1 "register_operand" "") (match_operand:SI 2 "immediate_operand" "") (match_operand:SI 3 "immediate_operand" "")))] "" { /* Handle extractions from %ah et al. */ if (INTVAL (operands[2]) != 8 || INTVAL (operands[3]) != 8) FAIL; /* From mips.md: extract_bit_field doesn't verify that our source matches the predicate, so check it again here. */ if (! register_operand (operands[1], VOIDmode)) FAIL; }) (define_expand "extzv" [(set (match_operand:SI 0 "register_operand" "") (zero_extract:SI (match_operand 1 "ext_register_operand" "") (match_operand:SI 2 "immediate_operand" "") (match_operand:SI 3 "immediate_operand" "")))] "" { /* Handle extractions from %ah et al. */ if (INTVAL (operands[2]) != 8 || INTVAL (operands[3]) != 8) FAIL; /* From mips.md: extract_bit_field doesn't verify that our source matches the predicate, so check it again here. */ if (! register_operand (operands[1], VOIDmode)) FAIL; }) (define_expand "insv" [(set (zero_extract (match_operand 0 "ext_register_operand" "") (match_operand 1 "immediate_operand" "") (match_operand 2 "immediate_operand" "")) (match_operand 3 "register_operand" ""))] "" { /* Handle extractions from %ah et al. */ if (INTVAL (operands[1]) != 8 || INTVAL (operands[2]) != 8) FAIL; /* From mips.md: insert_bit_field doesn't verify that our source matches the predicate, so check it again here. */ if (! register_operand (operands[0], VOIDmode)) FAIL; if (TARGET_64BIT) emit_insn (gen_movdi_insv_1_rex64 (operands[0], operands[3])); else emit_insn (gen_movsi_insv_1 (operands[0], operands[3])); DONE; }) ;; %%% bts, btr, btc, bt. ;; Store-flag instructions. ;; For all sCOND expanders, also expand the compare or test insn that ;; generates cc0. Generate an equality comparison if `seq' or `sne'. ;; %%% Do the expansion to SImode. If PII, do things the xor+setcc way ;; to avoid partial register stalls. Otherwise do things the setcc+movzx ;; way, which can later delete the movzx if only QImode is needed. (define_expand "seq" [(set (match_operand:QI 0 "register_operand" "") (eq:QI (reg:CC 17) (const_int 0)))] "" "if (ix86_expand_setcc (EQ, operands[0])) DONE; else FAIL;") (define_expand "sne" [(set (match_operand:QI 0 "register_operand" "") (ne:QI (reg:CC 17) (const_int 0)))] "" "if (ix86_expand_setcc (NE, operands[0])) DONE; else FAIL;") (define_expand "sgt" [(set (match_operand:QI 0 "register_operand" "") (gt:QI (reg:CC 17) (const_int 0)))] "" "if (ix86_expand_setcc (GT, operands[0])) DONE; else FAIL;") (define_expand "sgtu" [(set (match_operand:QI 0 "register_operand" "") (gtu:QI (reg:CC 17) (const_int 0)))] "" "if (ix86_expand_setcc (GTU, operands[0])) DONE; else FAIL;") (define_expand "slt" [(set (match_operand:QI 0 "register_operand" "") (lt:QI (reg:CC 17) (const_int 0)))] "" "if (ix86_expand_setcc (LT, operands[0])) DONE; else FAIL;") (define_expand "sltu" [(set (match_operand:QI 0 "register_operand" "") (ltu:QI (reg:CC 17) (const_int 0)))] "" "if (ix86_expand_setcc (LTU, operands[0])) DONE; else FAIL;") (define_expand "sge" [(set (match_operand:QI 0 "register_operand" "") (ge:QI (reg:CC 17) (const_int 0)))] "" "if (ix86_expand_setcc (GE, operands[0])) DONE; else FAIL;") (define_expand "sgeu" [(set (match_operand:QI 0 "register_operand" "") (geu:QI (reg:CC 17) (const_int 0)))] "" "if (ix86_expand_setcc (GEU, operands[0])) DONE; else FAIL;") (define_expand "sle" [(set (match_operand:QI 0 "register_operand" "") (le:QI (reg:CC 17) (const_int 0)))] "" "if (ix86_expand_setcc (LE, operands[0])) DONE; else FAIL;") (define_expand "sleu" [(set (match_operand:QI 0 "register_operand" "") (leu:QI (reg:CC 17) (const_int 0)))] "" "if (ix86_expand_setcc (LEU, operands[0])) DONE; else FAIL;") (define_expand "sunordered" [(set (match_operand:QI 0 "register_operand" "") (unordered:QI (reg:CC 17) (const_int 0)))] "TARGET_80387 || TARGET_SSE" "if (ix86_expand_setcc (UNORDERED, operands[0])) DONE; else FAIL;") (define_expand "sordered" [(set (match_operand:QI 0 "register_operand" "") (ordered:QI (reg:CC 17) (const_int 0)))] "TARGET_80387" "if (ix86_expand_setcc (ORDERED, operands[0])) DONE; else FAIL;") (define_expand "suneq" [(set (match_operand:QI 0 "register_operand" "") (uneq:QI (reg:CC 17) (const_int 0)))] "TARGET_80387 || TARGET_SSE" "if (ix86_expand_setcc (UNEQ, operands[0])) DONE; else FAIL;") (define_expand "sunge" [(set (match_operand:QI 0 "register_operand" "") (unge:QI (reg:CC 17) (const_int 0)))] "TARGET_80387 || TARGET_SSE" "if (ix86_expand_setcc (UNGE, operands[0])) DONE; else FAIL;") (define_expand "sungt" [(set (match_operand:QI 0 "register_operand" "") (ungt:QI (reg:CC 17) (const_int 0)))] "TARGET_80387 || TARGET_SSE" "if (ix86_expand_setcc (UNGT, operands[0])) DONE; else FAIL;") (define_expand "sunle" [(set (match_operand:QI 0 "register_operand" "") (unle:QI (reg:CC 17) (const_int 0)))] "TARGET_80387 || TARGET_SSE" "if (ix86_expand_setcc (UNLE, operands[0])) DONE; else FAIL;") (define_expand "sunlt" [(set (match_operand:QI 0 "register_operand" "") (unlt:QI (reg:CC 17) (const_int 0)))] "TARGET_80387 || TARGET_SSE" "if (ix86_expand_setcc (UNLT, operands[0])) DONE; else FAIL;") (define_expand "sltgt" [(set (match_operand:QI 0 "register_operand" "") (ltgt:QI (reg:CC 17) (const_int 0)))] "TARGET_80387 || TARGET_SSE" "if (ix86_expand_setcc (LTGT, operands[0])) DONE; else FAIL;") (define_insn "*setcc_1" [(set (match_operand:QI 0 "nonimmediate_operand" "=qm") (match_operator:QI 1 "ix86_comparison_operator" [(reg 17) (const_int 0)]))] "" "set%C1\t%0" [(set_attr "type" "setcc") (set_attr "mode" "QI")]) (define_insn "setcc_2" [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "+qm")) (match_operator:QI 1 "ix86_comparison_operator" [(reg 17) (const_int 0)]))] "" "set%C1\t%0" [(set_attr "type" "setcc") (set_attr "mode" "QI")]) ;; In general it is not safe to assume too much about CCmode registers, ;; so simplify-rtx stops when it sees a second one. Under certain ;; conditions this is safe on x86, so help combine not create ;; ;; seta %al ;; testb %al, %al ;; sete %al (define_split [(set (match_operand:QI 0 "nonimmediate_operand" "") (ne:QI (match_operator 1 "ix86_comparison_operator" [(reg 17) (const_int 0)]) (const_int 0)))] "" [(set (match_dup 0) (match_dup 1))] { PUT_MODE (operands[1], QImode); }) (define_split [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "")) (ne:QI (match_operator 1 "ix86_comparison_operator" [(reg 17) (const_int 0)]) (const_int 0)))] "" [(set (match_dup 0) (match_dup 1))] { PUT_MODE (operands[1], QImode); }) (define_split [(set (match_operand:QI 0 "nonimmediate_operand" "") (eq:QI (match_operator 1 "ix86_comparison_operator" [(reg 17) (const_int 0)]) (const_int 0)))] "" [(set (match_dup 0) (match_dup 1))] { rtx new_op1 = copy_rtx (operands[1]); operands[1] = new_op1; PUT_MODE (new_op1, QImode); PUT_CODE (new_op1, REVERSE_CONDITION (GET_CODE (new_op1), GET_MODE (XEXP (new_op1, 0)))); /* Make sure that (a) the CCmode we have for the flags is strong enough for the reversed compare or (b) we have a valid FP compare. */ if (! ix86_comparison_operator (new_op1, VOIDmode)) FAIL; }) (define_split [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "")) (eq:QI (match_operator 1 "ix86_comparison_operator" [(reg 17) (const_int 0)]) (const_int 0)))] "" [(set (match_dup 0) (match_dup 1))] { rtx new_op1 = copy_rtx (operands[1]); operands[1] = new_op1; PUT_MODE (new_op1, QImode); PUT_CODE (new_op1, REVERSE_CONDITION (GET_CODE (new_op1), GET_MODE (XEXP (new_op1, 0)))); /* Make sure that (a) the CCmode we have for the flags is strong enough for the reversed compare or (b) we have a valid FP compare. */ if (! ix86_comparison_operator (new_op1, VOIDmode)) FAIL; }) ;; The SSE store flag instructions saves 0 or 0xffffffff to the result. ;; subsequent logical operations are used to imitate conditional moves. ;; 0xffffffff is NaN, but not in normalized form, so we can't represent ;; it directly. Further holding this value in pseudo register might bring ;; problem in implicit normalization in spill code. ;; So we don't define FLOAT_STORE_FLAG_VALUE and create these ;; instructions after reload by splitting the conditional move patterns. (define_insn "*sse_setccsf" [(set (match_operand:SF 0 "register_operand" "=x") (match_operator:SF 1 "sse_comparison_operator" [(match_operand:SF 2 "register_operand" "0") (match_operand:SF 3 "nonimmediate_operand" "xm")]))] "TARGET_SSE && reload_completed" "cmp%D1ss\t{%3, %0|%0, %3}" [(set_attr "type" "ssecmp") (set_attr "mode" "SF")]) (define_insn "*sse_setccdf" [(set (match_operand:DF 0 "register_operand" "=Y") (match_operator:DF 1 "sse_comparison_operator" [(match_operand:DF 2 "register_operand" "0") (match_operand:DF 3 "nonimmediate_operand" "Ym")]))] "TARGET_SSE2 && reload_completed" "cmp%D1sd\t{%3, %0|%0, %3}" [(set_attr "type" "ssecmp") (set_attr "mode" "DF")]) ;; Basic conditional jump instructions. ;; We ignore the overflow flag for signed branch instructions. ;; For all bCOND expanders, also expand the compare or test insn that ;; generates reg 17. Generate an equality comparison if `beq' or `bne'. (define_expand "beq" [(set (pc) (if_then_else (match_dup 1) (label_ref (match_operand 0 "" "")) (pc)))] "" "ix86_expand_branch (EQ, operands[0]); DONE;") (define_expand "bne" [(set (pc) (if_then_else (match_dup 1) (label_ref (match_operand 0 "" "")) (pc)))] "" "ix86_expand_branch (NE, operands[0]); DONE;") (define_expand "bgt" [(set (pc) (if_then_else (match_dup 1) (label_ref (match_operand 0 "" "")) (pc)))] "" "ix86_expand_branch (GT, operands[0]); DONE;") (define_expand "bgtu" [(set (pc) (if_then_else (match_dup 1) (label_ref (match_operand 0 "" "")) (pc)))] "" "ix86_expand_branch (GTU, operands[0]); DONE;") (define_expand "blt" [(set (pc) (if_then_else (match_dup 1) (label_ref (match_operand 0 "" "")) (pc)))] "" "ix86_expand_branch (LT, operands[0]); DONE;") (define_expand "bltu" [(set (pc) (if_then_else (match_dup 1) (label_ref (match_operand 0 "" "")) (pc)))] "" "ix86_expand_branch (LTU, operands[0]); DONE;") (define_expand "bge" [(set (pc) (if_then_else (match_dup 1) (label_ref (match_operand 0 "" "")) (pc)))] "" "ix86_expand_branch (GE, operands[0]); DONE;") (define_expand "bgeu" [(set (pc) (if_then_else (match_dup 1) (label_ref (match_operand 0 "" "")) (pc)))] "" "ix86_expand_branch (GEU, operands[0]); DONE;") (define_expand "ble" [(set (pc) (if_then_else (match_dup 1) (label_ref (match_operand 0 "" "")) (pc)))] "" "ix86_expand_branch (LE, operands[0]); DONE;") (define_expand "bleu" [(set (pc) (if_then_else (match_dup 1) (label_ref (match_operand 0 "" "")) (pc)))] "" "ix86_expand_branch (LEU, operands[0]); DONE;") (define_expand "bunordered" [(set (pc) (if_then_else (match_dup 1) (label_ref (match_operand 0 "" "")) (pc)))] "TARGET_80387 || TARGET_SSE" "ix86_expand_branch (UNORDERED, operands[0]); DONE;") (define_expand "bordered" [(set (pc) (if_then_else (match_dup 1) (label_ref (match_operand 0 "" "")) (pc)))] "TARGET_80387 || TARGET_SSE" "ix86_expand_branch (ORDERED, operands[0]); DONE;") (define_expand "buneq" [(set (pc) (if_then_else (match_dup 1) (label_ref (match_operand 0 "" "")) (pc)))] "TARGET_80387 || TARGET_SSE" "ix86_expand_branch (UNEQ, operands[0]); DONE;") (define_expand "bunge" [(set (pc) (if_then_else (match_dup 1) (label_ref (match_operand 0 "" "")) (pc)))] "TARGET_80387 || TARGET_SSE" "ix86_expand_branch (UNGE, operands[0]); DONE;") (define_expand "bungt" [(set (pc) (if_then_else (match_dup 1) (label_ref (match_operand 0 "" "")) (pc)))] "TARGET_80387 || TARGET_SSE" "ix86_expand_branch (UNGT, operands[0]); DONE;") (define_expand "bunle" [(set (pc) (if_then_else (match_dup 1) (label_ref (match_operand 0 "" "")) (pc)))] "TARGET_80387 || TARGET_SSE" "ix86_expand_branch (UNLE, operands[0]); DONE;") (define_expand "bunlt" [(set (pc) (if_then_else (match_dup 1) (label_ref (match_operand 0 "" "")) (pc)))] "TARGET_80387 || TARGET_SSE" "ix86_expand_branch (UNLT, operands[0]); DONE;") (define_expand "bltgt" [(set (pc) (if_then_else (match_dup 1) (label_ref (match_operand 0 "" "")) (pc)))] "TARGET_80387 || TARGET_SSE" "ix86_expand_branch (LTGT, operands[0]); DONE;") (define_insn "*jcc_1" [(set (pc) (if_then_else (match_operator 1 "ix86_comparison_operator" [(reg 17) (const_int 0)]) (label_ref (match_operand 0 "" "")) (pc)))] "" "%+j%C1\t%l0" [(set_attr "type" "ibr") (set_attr "modrm" "0") (set (attr "length") (if_then_else (and (ge (minus (match_dup 0) (pc)) (const_int -126)) (lt (minus (match_dup 0) (pc)) (const_int 128))) (const_int 2) (const_int 6)))]) (define_insn "*jcc_2" [(set (pc) (if_then_else (match_operator 1 "ix86_comparison_operator" [(reg 17) (const_int 0)]) (pc) (label_ref (match_operand 0 "" ""))))] "" "%+j%c1\t%l0" [(set_attr "type" "ibr") (set_attr "modrm" "0") (set (attr "length") (if_then_else (and (ge (minus (match_dup 0) (pc)) (const_int -126)) (lt (minus (match_dup 0) (pc)) (const_int 128))) (const_int 2) (const_int 6)))]) ;; In general it is not safe to assume too much about CCmode registers, ;; so simplify-rtx stops when it sees a second one. Under certain ;; conditions this is safe on x86, so help combine not create ;; ;; seta %al ;; testb %al, %al ;; je Lfoo (define_split [(set (pc) (if_then_else (ne (match_operator 0 "ix86_comparison_operator" [(reg 17) (const_int 0)]) (const_int 0)) (label_ref (match_operand 1 "" "")) (pc)))] "" [(set (pc) (if_then_else (match_dup 0) (label_ref (match_dup 1)) (pc)))] { PUT_MODE (operands[0], VOIDmode); }) (define_split [(set (pc) (if_then_else (eq (match_operator 0 "ix86_comparison_operator" [(reg 17) (const_int 0)]) (const_int 0)) (label_ref (match_operand 1 "" "")) (pc)))] "" [(set (pc) (if_then_else (match_dup 0) (label_ref (match_dup 1)) (pc)))] { rtx new_op0 = copy_rtx (operands[0]); operands[0] = new_op0; PUT_MODE (new_op0, VOIDmode); PUT_CODE (new_op0, REVERSE_CONDITION (GET_CODE (new_op0), GET_MODE (XEXP (new_op0, 0)))); /* Make sure that (a) the CCmode we have for the flags is strong enough for the reversed compare or (b) we have a valid FP compare. */ if (! ix86_comparison_operator (new_op0, VOIDmode)) FAIL; }) ;; Define combination compare-and-branch fp compare instructions to use ;; during early optimization. Splitting the operation apart early makes ;; for bad code when we want to reverse the operation. (define_insn "*fp_jcc_1" [(set (pc) (if_then_else (match_operator 0 "comparison_operator" [(match_operand 1 "register_operand" "f") (match_operand 2 "register_operand" "f")]) (label_ref (match_operand 3 "" "")) (pc))) (clobber (reg:CCFP 18)) (clobber (reg:CCFP 17))] "TARGET_CMOVE && TARGET_80387 && !SSE_FLOAT_MODE_P (GET_MODE (operands[1])) && FLOAT_MODE_P (GET_MODE (operands[1])) && GET_MODE (operands[1]) == GET_MODE (operands[2]) && ix86_fp_jump_nontrivial_p (GET_CODE (operands[0]))" "#") (define_insn "*fp_jcc_1_sse" [(set (pc) (if_then_else (match_operator 0 "comparison_operator" [(match_operand 1 "register_operand" "f#x,x#f") (match_operand 2 "nonimmediate_operand" "f#x,xm#f")]) (label_ref (match_operand 3 "" "")) (pc))) (clobber (reg:CCFP 18)) (clobber (reg:CCFP 17))] "TARGET_80387 && SSE_FLOAT_MODE_P (GET_MODE (operands[1])) && GET_MODE (operands[1]) == GET_MODE (operands[2]) && ix86_fp_jump_nontrivial_p (GET_CODE (operands[0]))" "#") (define_insn "*fp_jcc_1_sse_only" [(set (pc) (if_then_else (match_operator 0 "comparison_operator" [(match_operand 1 "register_operand" "x") (match_operand 2 "nonimmediate_operand" "xm")]) (label_ref (match_operand 3 "" "")) (pc))) (clobber (reg:CCFP 18)) (clobber (reg:CCFP 17))] "SSE_FLOAT_MODE_P (GET_MODE (operands[1])) && GET_MODE (operands[1]) == GET_MODE (operands[2]) && ix86_fp_jump_nontrivial_p (GET_CODE (operands[0]))" "#") (define_insn "*fp_jcc_2" [(set (pc) (if_then_else (match_operator 0 "comparison_operator" [(match_operand 1 "register_operand" "f") (match_operand 2 "register_operand" "f")]) (pc) (label_ref (match_operand 3 "" "")))) (clobber (reg:CCFP 18)) (clobber (reg:CCFP 17))] "TARGET_CMOVE && TARGET_80387 && !SSE_FLOAT_MODE_P (GET_MODE (operands[1])) && FLOAT_MODE_P (GET_MODE (operands[1])) && GET_MODE (operands[1]) == GET_MODE (operands[2]) && ix86_fp_jump_nontrivial_p (GET_CODE (operands[0]))" "#") (define_insn "*fp_jcc_2_sse" [(set (pc) (if_then_else (match_operator 0 "comparison_operator" [(match_operand 1 "register_operand" "f#x,x#f") (match_operand 2 "nonimmediate_operand" "f#x,xm#f")]) (pc) (label_ref (match_operand 3 "" "")))) (clobber (reg:CCFP 18)) (clobber (reg:CCFP 17))] "TARGET_80387 && SSE_FLOAT_MODE_P (GET_MODE (operands[1])) && GET_MODE (operands[1]) == GET_MODE (operands[2]) && ix86_fp_jump_nontrivial_p (GET_CODE (operands[0]))" "#") (define_insn "*fp_jcc_2_sse_only" [(set (pc) (if_then_else (match_operator 0 "comparison_operator" [(match_operand 1 "register_operand" "x") (match_operand 2 "nonimmediate_operand" "xm")]) (pc) (label_ref (match_operand 3 "" "")))) (clobber (reg:CCFP 18)) (clobber (reg:CCFP 17))] "SSE_FLOAT_MODE_P (GET_MODE (operands[1])) && GET_MODE (operands[1]) == GET_MODE (operands[2]) && ix86_fp_jump_nontrivial_p (GET_CODE (operands[0]))" "#") (define_insn "*fp_jcc_3" [(set (pc) (if_then_else (match_operator 0 "comparison_operator" [(match_operand 1 "register_operand" "f") (match_operand 2 "nonimmediate_operand" "fm")]) (label_ref (match_operand 3 "" "")) (pc))) (clobber (reg:CCFP 18)) (clobber (reg:CCFP 17)) (clobber (match_scratch:HI 4 "=a"))] "TARGET_80387 && (GET_MODE (operands[1]) == SFmode || GET_MODE (operands[1]) == DFmode) && GET_MODE (operands[1]) == GET_MODE (operands[2]) && !ix86_use_fcomi_compare (GET_CODE (operands[0])) && SELECT_CC_MODE (GET_CODE (operands[0]), operands[1], operands[2]) == CCFPmode && ix86_fp_jump_nontrivial_p (GET_CODE (operands[0]))" "#") (define_insn "*fp_jcc_4" [(set (pc) (if_then_else (match_operator 0 "comparison_operator" [(match_operand 1 "register_operand" "f") (match_operand 2 "nonimmediate_operand" "fm")]) (pc) (label_ref (match_operand 3 "" "")))) (clobber (reg:CCFP 18)) (clobber (reg:CCFP 17)) (clobber (match_scratch:HI 4 "=a"))] "TARGET_80387 && (GET_MODE (operands[1]) == SFmode || GET_MODE (operands[1]) == DFmode) && GET_MODE (operands[1]) == GET_MODE (operands[2]) && !ix86_use_fcomi_compare (GET_CODE (operands[0])) && SELECT_CC_MODE (GET_CODE (operands[0]), operands[1], operands[2]) == CCFPmode && ix86_fp_jump_nontrivial_p (GET_CODE (operands[0]))" "#") (define_insn "*fp_jcc_5" [(set (pc) (if_then_else (match_operator 0 "comparison_operator" [(match_operand 1 "register_operand" "f") (match_operand 2 "register_operand" "f")]) (label_ref (match_operand 3 "" "")) (pc))) (clobber (reg:CCFP 18)) (clobber (reg:CCFP 17)) (clobber (match_scratch:HI 4 "=a"))] "TARGET_80387 && FLOAT_MODE_P (GET_MODE (operands[1])) && GET_MODE (operands[1]) == GET_MODE (operands[2]) && ix86_fp_jump_nontrivial_p (GET_CODE (operands[0]))" "#") (define_insn "*fp_jcc_6" [(set (pc) (if_then_else (match_operator 0 "comparison_operator" [(match_operand 1 "register_operand" "f") (match_operand 2 "register_operand" "f")]) (pc) (label_ref (match_operand 3 "" "")))) (clobber (reg:CCFP 18)) (clobber (reg:CCFP 17)) (clobber (match_scratch:HI 4 "=a"))] "TARGET_80387 && FLOAT_MODE_P (GET_MODE (operands[1])) && GET_MODE (operands[1]) == GET_MODE (operands[2]) && ix86_fp_jump_nontrivial_p (GET_CODE (operands[0]))" "#") (define_split [(set (pc) (if_then_else (match_operator 0 "comparison_operator" [(match_operand 1 "register_operand" "") (match_operand 2 "nonimmediate_operand" "")]) (match_operand 3 "" "") (match_operand 4 "" ""))) (clobber (reg:CCFP 18)) (clobber (reg:CCFP 17))] "reload_completed" [(const_int 0)] { ix86_split_fp_branch (GET_CODE (operands[0]), operands[1], operands[2], operands[3], operands[4], NULL_RTX); DONE; }) (define_split [(set (pc) (if_then_else (match_operator 0 "comparison_operator" [(match_operand 1 "register_operand" "") (match_operand 2 "nonimmediate_operand" "")]) (match_operand 3 "" "") (match_operand 4 "" ""))) (clobber (reg:CCFP 18)) (clobber (reg:CCFP 17)) (clobber (match_scratch:HI 5 "=a"))] "reload_completed" [(set (pc) (if_then_else (match_dup 6) (match_dup 3) (match_dup 4)))] { ix86_split_fp_branch (GET_CODE (operands[0]), operands[1], operands[2], operands[3], operands[4], operands[5]); DONE; }) ;; Unconditional and other jump instructions (define_insn "jump" [(set (pc) (label_ref (match_operand 0 "" "")))] "" "jmp\t%l0" [(set_attr "type" "ibr") (set (attr "length") (if_then_else (and (ge (minus (match_dup 0) (pc)) (const_int -126)) (lt (minus (match_dup 0) (pc)) (const_int 128))) (const_int 2) (const_int 5))) (set_attr "modrm" "0")]) (define_expand "indirect_jump" [(set (pc) (match_operand 0 "nonimmediate_operand" "rm"))] "" "") (define_insn "*indirect_jump" [(set (pc) (match_operand:SI 0 "nonimmediate_operand" "rm"))] "!TARGET_64BIT" "jmp\t%A0" [(set_attr "type" "ibr") (set_attr "length_immediate" "0")]) (define_insn "*indirect_jump_rtx64" [(set (pc) (match_operand:DI 0 "nonimmediate_operand" "rm"))] "TARGET_64BIT" "jmp\t%A0" [(set_attr "type" "ibr") (set_attr "length_immediate" "0")]) (define_expand "tablejump" [(parallel [(set (pc) (match_operand 0 "nonimmediate_operand" "rm")) (use (label_ref (match_operand 1 "" "")))])] "" { /* In PIC mode, the table entries are stored GOT (32-bit) or PC (64-bit) relative. Convert the relative address to an absolute address. */ if (flag_pic) { rtx op0, op1; enum rtx_code code; if (TARGET_64BIT) { code = PLUS; op0 = operands[0]; op1 = gen_rtx_LABEL_REF (Pmode, operands[1]); } else if (TARGET_MACHO || HAVE_AS_GOTOFF_IN_DATA) { code = PLUS; op0 = operands[0]; op1 = pic_offset_table_rtx; } else { code = MINUS; op0 = pic_offset_table_rtx; op1 = operands[0]; } operands[0] = expand_simple_binop (Pmode, code, op0, op1, NULL_RTX, 0, OPTAB_DIRECT); } }) (define_insn "*tablejump_1" [(set (pc) (match_operand:SI 0 "nonimmediate_operand" "rm")) (use (label_ref (match_operand 1 "" "")))] "!TARGET_64BIT" "jmp\t%A0" [(set_attr "type" "ibr") (set_attr "length_immediate" "0")]) (define_insn "*tablejump_1_rtx64" [(set (pc) (match_operand:DI 0 "nonimmediate_operand" "rm")) (use (label_ref (match_operand 1 "" "")))] "TARGET_64BIT" "jmp\t%A0" [(set_attr "type" "ibr") (set_attr "length_immediate" "0")]) ;; Loop instruction ;; ;; This is all complicated by the fact that since this is a jump insn ;; we must handle our own reloads. (define_expand "doloop_end" [(use (match_operand 0 "" "")) ; loop pseudo (use (match_operand 1 "" "")) ; iterations; zero if unknown (use (match_operand 2 "" "")) ; max iterations (use (match_operand 3 "" "")) ; loop level (use (match_operand 4 "" ""))] ; label "!TARGET_64BIT && TARGET_USE_LOOP" " { /* Only use cloop on innermost loops. */ if (INTVAL (operands[3]) > 1) FAIL; if (GET_MODE (operands[0]) != SImode) FAIL; emit_jump_insn (gen_doloop_end_internal (operands[4], operands[0], operands[0])); DONE; }") (define_insn "doloop_end_internal" [(set (pc) (if_then_else (ne (match_operand:SI 1 "register_operand" "c,?*r,?*r") (const_int 1)) (label_ref (match_operand 0 "" "")) (pc))) (set (match_operand:SI 2 "register_operand" "=1,1,*m*r") (plus:SI (match_dup 1) (const_int -1))) (clobber (match_scratch:SI 3 "=X,X,r")) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_USE_LOOP" { if (which_alternative != 0) return "#"; if (get_attr_length (insn) == 2) return "%+loop\t%l0"; else return "dec{l}\t%1\;%+jne\t%l0"; } [(set_attr "ppro_uops" "many") (set (attr "length") (if_then_else (and (eq_attr "alternative" "0") (and (ge (minus (match_dup 0) (pc)) (const_int -126)) (lt (minus (match_dup 0) (pc)) (const_int 128)))) (const_int 2) (const_int 16))) ;; We don't know the type before shorten branches. Optimistically expect ;; the loop instruction to match. (set (attr "type") (const_string "ibr"))]) (define_split [(set (pc) (if_then_else (ne (match_operand:SI 1 "register_operand" "") (const_int 1)) (match_operand 0 "" "") (pc))) (set (match_dup 1) (plus:SI (match_dup 1) (const_int -1))) (clobber (match_scratch:SI 2 "")) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_USE_LOOP && reload_completed && REGNO (operands[1]) != 2" [(parallel [(set (reg:CCZ 17) (compare:CCZ (plus:SI (match_dup 1) (const_int -1)) (const_int 0))) (set (match_dup 1) (plus:SI (match_dup 1) (const_int -1)))]) (set (pc) (if_then_else (ne (reg:CCZ 17) (const_int 0)) (match_dup 0) (pc)))] "") (define_split [(set (pc) (if_then_else (ne (match_operand:SI 1 "register_operand" "") (const_int 1)) (match_operand 0 "" "") (pc))) (set (match_operand:SI 2 "nonimmediate_operand" "") (plus:SI (match_dup 1) (const_int -1))) (clobber (match_scratch:SI 3 "")) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_USE_LOOP && reload_completed && (! REG_P (operands[2]) || ! rtx_equal_p (operands[1], operands[2]))" [(set (match_dup 3) (match_dup 1)) (parallel [(set (reg:CCZ 17) (compare:CCZ (plus:SI (match_dup 3) (const_int -1)) (const_int 0))) (set (match_dup 3) (plus:SI (match_dup 3) (const_int -1)))]) (set (match_dup 2) (match_dup 3)) (set (pc) (if_then_else (ne (reg:CCZ 17) (const_int 0)) (match_dup 0) (pc)))] "") ;; Convert setcc + movzbl to xor + setcc if operands don't overlap. (define_peephole2 [(set (reg 17) (match_operand 0 "" "")) (set (match_operand:QI 1 "register_operand" "") (match_operator:QI 2 "ix86_comparison_operator" [(reg 17) (const_int 0)])) (set (match_operand 3 "q_regs_operand" "") (zero_extend (match_dup 1)))] "(peep2_reg_dead_p (3, operands[1]) || operands_match_p (operands[1], operands[3])) && ! reg_overlap_mentioned_p (operands[3], operands[0])" [(set (match_dup 4) (match_dup 0)) (set (strict_low_part (match_dup 5)) (match_dup 2))] { operands[4] = gen_rtx_REG (GET_MODE (operands[0]), 17); operands[5] = gen_lowpart (QImode, operands[3]); ix86_expand_clear (operands[3]); }) ;; Similar, but match zero_extendhisi2_and, which adds a clobber. (define_peephole2 [(set (reg 17) (match_operand 0 "" "")) (set (match_operand:QI 1 "register_operand" "") (match_operator:QI 2 "ix86_comparison_operator" [(reg 17) (const_int 0)])) (parallel [(set (match_operand 3 "q_regs_operand" "") (zero_extend (match_dup 1))) (clobber (reg:CC 17))])] "(peep2_reg_dead_p (3, operands[1]) || operands_match_p (operands[1], operands[3])) && ! reg_overlap_mentioned_p (operands[3], operands[0])" [(set (match_dup 4) (match_dup 0)) (set (strict_low_part (match_dup 5)) (match_dup 2))] { operands[4] = gen_rtx_REG (GET_MODE (operands[0]), 17); operands[5] = gen_lowpart (QImode, operands[3]); ix86_expand_clear (operands[3]); }) ;; Call instructions. ;; The predicates normally associated with named expanders are not properly ;; checked for calls. This is a bug in the generic code, but it isn't that ;; easy to fix. Ignore it for now and be prepared to fix things up. ;; Call subroutine returning no value. (define_expand "call_pop" [(parallel [(call (match_operand:QI 0 "" "") (match_operand:SI 1 "" "")) (set (reg:SI 7) (plus:SI (reg:SI 7) (match_operand:SI 3 "" "")))])] "!TARGET_64BIT" { ix86_expand_call (NULL, operands[0], operands[1], operands[2], operands[3], 0); DONE; }) (define_insn "*call_pop_0" [(call (mem:QI (match_operand:SI 0 "constant_call_address_operand" "")) (match_operand:SI 1 "" "")) (set (reg:SI 7) (plus:SI (reg:SI 7) (match_operand:SI 2 "immediate_operand" "")))] "!TARGET_64BIT" { if (SIBLING_CALL_P (insn)) return "jmp\t%P0"; else return "call\t%P0"; } [(set_attr "type" "call")]) (define_insn "*call_pop_1" [(call (mem:QI (match_operand:SI 0 "call_insn_operand" "rsm")) (match_operand:SI 1 "" "")) (set (reg:SI 7) (plus:SI (reg:SI 7) (match_operand:SI 2 "immediate_operand" "i")))] "!TARGET_64BIT" { if (constant_call_address_operand (operands[0], Pmode)) { if (SIBLING_CALL_P (insn)) return "jmp\t%P0"; else return "call\t%P0"; } if (SIBLING_CALL_P (insn)) return "jmp\t%A0"; else return "call\t%A0"; } [(set_attr "type" "call")]) (define_expand "call" [(call (match_operand:QI 0 "" "") (match_operand 1 "" "")) (use (match_operand 2 "" ""))] "" { ix86_expand_call (NULL, operands[0], operands[1], operands[2], NULL, 0); DONE; }) (define_expand "sibcall" [(call (match_operand:QI 0 "" "") (match_operand 1 "" "")) (use (match_operand 2 "" ""))] "" { ix86_expand_call (NULL, operands[0], operands[1], operands[2], NULL, 1); DONE; }) (define_insn "*call_0" [(call (mem:QI (match_operand 0 "constant_call_address_operand" "")) (match_operand 1 "" ""))] "" { if (SIBLING_CALL_P (insn)) return "jmp\t%P0"; else return "call\t%P0"; } [(set_attr "type" "call")]) (define_insn "*call_1" [(call (mem:QI (match_operand:SI 0 "call_insn_operand" "rsm")) (match_operand 1 "" ""))] "!SIBLING_CALL_P (insn) && !TARGET_64BIT" { if (constant_call_address_operand (operands[0], QImode)) return "call\t%P0"; return "call\t%A0"; } [(set_attr "type" "call")]) (define_insn "*sibcall_1" [(call (mem:QI (match_operand:SI 0 "sibcall_insn_operand" "s,c,d,a")) (match_operand 1 "" ""))] "SIBLING_CALL_P (insn) && !TARGET_64BIT" { if (constant_call_address_operand (operands[0], QImode)) return "jmp\t%P0"; return "jmp\t%A0"; } [(set_attr "type" "call")]) (define_insn "*call_1_rex64" [(call (mem:QI (match_operand:DI 0 "call_insn_operand" "rsm")) (match_operand 1 "" ""))] "!SIBLING_CALL_P (insn) && TARGET_64BIT" { if (constant_call_address_operand (operands[0], QImode)) return "call\t%P0"; return "call\t%A0"; } [(set_attr "type" "call")]) (define_insn "*sibcall_1_rex64" [(call (mem:QI (match_operand:DI 0 "constant_call_address_operand" "")) (match_operand 1 "" ""))] "SIBLING_CALL_P (insn) && TARGET_64BIT" "jmp\t%P0" [(set_attr "type" "call")]) (define_insn "*sibcall_1_rex64_v" [(call (mem:QI (reg:DI 40)) (match_operand 0 "" ""))] "SIBLING_CALL_P (insn) && TARGET_64BIT" "jmp\t*%%r11" [(set_attr "type" "call")]) ;; Call subroutine, returning value in operand 0 (define_expand "call_value_pop" [(parallel [(set (match_operand 0 "" "") (call (match_operand:QI 1 "" "") (match_operand:SI 2 "" ""))) (set (reg:SI 7) (plus:SI (reg:SI 7) (match_operand:SI 4 "" "")))])] "!TARGET_64BIT" { ix86_expand_call (operands[0], operands[1], operands[2], operands[3], operands[4], 0); DONE; }) (define_expand "call_value" [(set (match_operand 0 "" "") (call (match_operand:QI 1 "" "") (match_operand:SI 2 "" ""))) (use (match_operand:SI 3 "" ""))] ;; Operand 2 not used on the i386. "" { ix86_expand_call (operands[0], operands[1], operands[2], operands[3], NULL, 0); DONE; }) (define_expand "sibcall_value" [(set (match_operand 0 "" "") (call (match_operand:QI 1 "" "") (match_operand:SI 2 "" ""))) (use (match_operand:SI 3 "" ""))] ;; Operand 2 not used on the i386. "" { ix86_expand_call (operands[0], operands[1], operands[2], operands[3], NULL, 1); DONE; }) ;; Call subroutine returning any type. (define_expand "untyped_call" [(parallel [(call (match_operand 0 "" "") (const_int 0)) (match_operand 1 "" "") (match_operand 2 "" "")])] "" { int i; /* In order to give reg-stack an easier job in validating two coprocessor registers as containing a possible return value, simply pretend the untyped call returns a complex long double value. */ ix86_expand_call ((TARGET_FLOAT_RETURNS_IN_80387 ? gen_rtx_REG (XCmode, FIRST_FLOAT_REG) : NULL), operands[0], const0_rtx, GEN_INT (SSE_REGPARM_MAX - 1), NULL, 0); for (i = 0; i < XVECLEN (operands[2], 0); i++) { rtx set = XVECEXP (operands[2], 0, i); emit_move_insn (SET_DEST (set), SET_SRC (set)); } /* The optimizer does not know that the call sets the function value registers we stored in the result block. We avoid problems by claiming that all hard registers are used and clobbered at this point. */ emit_insn (gen_blockage (const0_rtx)); DONE; }) ;; Prologue and epilogue instructions ;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and ;; all of memory. This blocks insns from being moved across this point. (define_insn "blockage" [(unspec_volatile [(match_operand 0 "" "")] UNSPECV_BLOCKAGE)] "" "" [(set_attr "length" "0")]) ;; Insn emitted into the body of a function to return from a function. ;; This is only done if the function's epilogue is known to be simple. ;; See comments for ix86_can_use_return_insn_p in i386.c. (define_expand "return" [(return)] "ix86_can_use_return_insn_p ()" { if (current_function_pops_args) { rtx popc = GEN_INT (current_function_pops_args); emit_jump_insn (gen_return_pop_internal (popc)); DONE; } }) (define_insn "return_internal" [(return)] "reload_completed" "ret" [(set_attr "length" "1") (set_attr "length_immediate" "0") (set_attr "modrm" "0")]) ;; Used by x86_machine_dependent_reorg to avoid penalty on single byte RET ;; instruction Athlon and K8 have. (define_insn "return_internal_long" [(return) (unspec [(const_int 0)] UNSPEC_REP)] "reload_completed" "rep {;} ret" [(set_attr "length" "1") (set_attr "length_immediate" "0") (set_attr "prefix_rep" "1") (set_attr "modrm" "0")]) (define_insn "return_pop_internal" [(return) (use (match_operand:SI 0 "const_int_operand" ""))] "reload_completed" "ret\t%0" [(set_attr "length" "3") (set_attr "length_immediate" "2") (set_attr "modrm" "0")]) (define_insn "return_indirect_internal" [(return) (use (match_operand:SI 0 "register_operand" "r"))] "reload_completed" "jmp\t%A0" [(set_attr "type" "ibr") (set_attr "length_immediate" "0")]) (define_insn "nop" [(const_int 0)] "" "nop" [(set_attr "length" "1") (set_attr "length_immediate" "0") (set_attr "modrm" "0") (set_attr "ppro_uops" "one")]) ;; Align to 16-byte boundary, max skip in op0. Used to avoid ;; branch prediction penalty for the third jump in a 16-byte ;; block on K8. (define_insn "align" [(unspec_volatile [(match_operand 0 "" "")] UNSPECV_ALIGN)] "" { #ifdef ASM_OUTPUT_MAX_SKIP_ALIGN ASM_OUTPUT_MAX_SKIP_ALIGN (asm_out_file, 4, (int)INTVAL (operands[0])); #else /* It is tempting to use ASM_OUTPUT_ALIGN here, but we don't want to do that. The align insn is used to avoid 3 jump instructions in the row to improve branch prediction and the benefits hardly outweight the cost of extra 8 nops on the average inserted by full alignment pseudo operation. */ #endif return ""; } [(set_attr "length" "16")]) (define_expand "prologue" [(const_int 1)] "" "ix86_expand_prologue (); DONE;") (define_insn "set_got" [(set (match_operand:SI 0 "register_operand" "=r") (unspec:SI [(const_int 0)] UNSPEC_SET_GOT)) (clobber (reg:CC 17))] "!TARGET_64BIT" { return output_set_got (operands[0]); } [(set_attr "type" "multi") (set_attr "length" "12")]) (define_expand "epilogue" [(const_int 1)] "" "ix86_expand_epilogue (1); DONE;") (define_expand "sibcall_epilogue" [(const_int 1)] "" "ix86_expand_epilogue (0); DONE;") (define_expand "eh_return" [(use (match_operand 0 "register_operand" ""))] "" { rtx tmp, sa = EH_RETURN_STACKADJ_RTX, ra = operands[0]; /* Tricky bit: we write the address of the handler to which we will be returning into someone else's stack frame, one word below the stack address we wish to restore. */ tmp = gen_rtx_PLUS (Pmode, arg_pointer_rtx, sa); tmp = plus_constant (tmp, -UNITS_PER_WORD); tmp = gen_rtx_MEM (Pmode, tmp); emit_move_insn (tmp, ra); if (Pmode == SImode) emit_insn (gen_eh_return_si (sa)); else emit_insn (gen_eh_return_di (sa)); emit_barrier (); DONE; }) (define_insn_and_split "eh_return_si" [(unspec_volatile [(match_operand:SI 0 "register_operand" "c")] UNSPECV_EH_RETURN)] "!TARGET_64BIT" "#" "reload_completed" [(const_int 1)] "ix86_expand_epilogue (2); DONE;") (define_insn_and_split "eh_return_di" [(unspec_volatile [(match_operand:DI 0 "register_operand" "c")] UNSPECV_EH_RETURN)] "TARGET_64BIT" "#" "reload_completed" [(const_int 1)] "ix86_expand_epilogue (2); DONE;") (define_insn "leave" [(set (reg:SI 7) (plus:SI (reg:SI 6) (const_int 4))) (set (reg:SI 6) (mem:SI (reg:SI 6))) (clobber (mem:BLK (scratch)))] "!TARGET_64BIT" "leave" [(set_attr "type" "leave")]) (define_insn "leave_rex64" [(set (reg:DI 7) (plus:DI (reg:DI 6) (const_int 8))) (set (reg:DI 6) (mem:DI (reg:DI 6))) (clobber (mem:BLK (scratch)))] "TARGET_64BIT" "leave" [(set_attr "type" "leave")]) (define_expand "ffssi2" [(parallel [(set (match_operand:SI 0 "register_operand" "") (ffs:SI (match_operand:SI 1 "nonimmediate_operand" ""))) (clobber (match_scratch:SI 2 "")) (clobber (reg:CC 17))])] "" "") (define_insn_and_split "*ffs_cmove" [(set (match_operand:SI 0 "register_operand" "=r") (ffs:SI (match_operand:SI 1 "nonimmediate_operand" "rm"))) (clobber (match_scratch:SI 2 "=&r")) (clobber (reg:CC 17))] "TARGET_CMOVE" "#" "&& reload_completed" [(set (match_dup 2) (const_int -1)) (parallel [(set (reg:CCZ 17) (compare:CCZ (match_dup 1) (const_int 0))) (set (match_dup 0) (ctz:SI (match_dup 1)))]) (set (match_dup 0) (if_then_else:SI (eq (reg:CCZ 17) (const_int 0)) (match_dup 2) (match_dup 0))) (parallel [(set (match_dup 0) (plus:SI (match_dup 0) (const_int 1))) (clobber (reg:CC 17))])] "") (define_insn_and_split "*ffs_no_cmove" [(set (match_operand:SI 0 "nonimmediate_operand" "=r") (ffs:SI (match_operand:SI 1 "nonimmediate_operand" "rm"))) (clobber (match_scratch:SI 2 "=&q")) (clobber (reg:CC 17))] "" "#" "reload_completed" [(parallel [(set (reg:CCZ 17) (compare:CCZ (match_dup 1) (const_int 0))) (set (match_dup 0) (ctz:SI (match_dup 1)))]) (set (strict_low_part (match_dup 3)) (eq:QI (reg:CCZ 17) (const_int 0))) (parallel [(set (match_dup 2) (neg:SI (match_dup 2))) (clobber (reg:CC 17))]) (parallel [(set (match_dup 0) (ior:SI (match_dup 0) (match_dup 2))) (clobber (reg:CC 17))]) (parallel [(set (match_dup 0) (plus:SI (match_dup 0) (const_int 1))) (clobber (reg:CC 17))])] { operands[3] = gen_lowpart (QImode, operands[2]); ix86_expand_clear (operands[2]); }) (define_insn "*ffssi_1" [(set (reg:CCZ 17) (compare:CCZ (match_operand:SI 1 "nonimmediate_operand" "rm") (const_int 0))) (set (match_operand:SI 0 "register_operand" "=r") (ctz:SI (match_dup 1)))] "" "bsf{l}\t{%1, %0|%0, %1}" [(set_attr "prefix_0f" "1") (set_attr "ppro_uops" "few")]) (define_insn "ctzsi2" [(set (match_operand:SI 0 "register_operand" "=r") (ctz:SI (match_operand:SI 1 "nonimmediate_operand" "rm"))) (clobber (reg:CC 17))] "" "bsf{l}\t{%1, %0|%0, %1}" [(set_attr "prefix_0f" "1") (set_attr "ppro_uops" "few")]) (define_expand "clzsi2" [(parallel [(set (match_operand:SI 0 "register_operand" "") (minus:SI (const_int 31) (clz:SI (match_operand:SI 1 "nonimmediate_operand" "")))) (clobber (reg:CC 17))]) (parallel [(set (match_dup 0) (xor:SI (match_dup 0) (const_int 31))) (clobber (reg:CC 17))])] "" "") (define_insn "*bsr" [(set (match_operand:SI 0 "register_operand" "=r") (minus:SI (const_int 31) (clz:SI (match_operand:SI 1 "nonimmediate_operand" "rm")))) (clobber (reg:CC 17))] "" "bsr{l}\t{%1, %0|%0, %1}" [(set_attr "prefix_0f" "1") (set_attr "ppro_uops" "few")]) ;; Thread-local storage patterns for ELF. ;; ;; Note that these code sequences must appear exactly as shown ;; in order to allow linker relaxation. (define_insn "*tls_global_dynamic_32_gnu" [(set (match_operand:SI 0 "register_operand" "=a") (unspec:SI [(match_operand:SI 1 "register_operand" "b") (match_operand:SI 2 "tls_symbolic_operand" "") (match_operand:SI 3 "call_insn_operand" "")] UNSPEC_TLS_GD)) (clobber (match_scratch:SI 4 "=d")) (clobber (match_scratch:SI 5 "=c")) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_GNU_TLS" "lea{l}\t{%a2@TLSGD(,%1,1), %0|%0, %a2@TLSGD[%1*1]}\;call\t%P3" [(set_attr "type" "multi") (set_attr "length" "12")]) (define_insn "*tls_global_dynamic_32_sun" [(set (match_operand:SI 0 "register_operand" "=a") (unspec:SI [(match_operand:SI 1 "register_operand" "b") (match_operand:SI 2 "tls_symbolic_operand" "") (match_operand:SI 3 "call_insn_operand" "")] UNSPEC_TLS_GD)) (clobber (match_scratch:SI 4 "=d")) (clobber (match_scratch:SI 5 "=c")) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_SUN_TLS" "lea{l}\t{%a2@DTLNDX(%1), %4|%4, %a2@DTLNDX[%1]} push{l}\t%4\;call\t%a2@TLSPLT\;pop{l}\t%4\;nop" [(set_attr "type" "multi") (set_attr "length" "14")]) (define_expand "tls_global_dynamic_32" [(parallel [(set (match_operand:SI 0 "register_operand" "") (unspec:SI [(match_dup 2) (match_operand:SI 1 "tls_symbolic_operand" "") (match_dup 3)] UNSPEC_TLS_GD)) (clobber (match_scratch:SI 4 "")) (clobber (match_scratch:SI 5 "")) (clobber (reg:CC 17))])] "" { if (flag_pic) operands[2] = pic_offset_table_rtx; else { operands[2] = gen_reg_rtx (Pmode); emit_insn (gen_set_got (operands[2])); } operands[3] = ix86_tls_get_addr (); }) (define_insn "*tls_global_dynamic_64" [(set (match_operand:DI 0 "register_operand" "=a") (call (mem:QI (match_operand:DI 2 "call_insn_operand" "")) (match_operand:DI 3 "" ""))) (unspec:DI [(match_operand:DI 1 "tls_symbolic_operand" "")] UNSPEC_TLS_GD)] "TARGET_64BIT" ".byte\t0x66\;lea{q}\t{%a1@TLSGD(%%rip), %%rdi|%%rdi, %a1@TLSGD[%%rip]}\;.word\t0x6666\;rex64\;call\t%P2" [(set_attr "type" "multi") (set_attr "length" "16")]) (define_expand "tls_global_dynamic_64" [(parallel [(set (match_operand:DI 0 "register_operand" "") (call (mem:QI (match_dup 2)) (const_int 0))) (unspec:DI [(match_operand:DI 1 "tls_symbolic_operand" "")] UNSPEC_TLS_GD)])] "" { operands[2] = ix86_tls_get_addr (); }) (define_insn "*tls_local_dynamic_base_32_gnu" [(set (match_operand:SI 0 "register_operand" "=a") (unspec:SI [(match_operand:SI 1 "register_operand" "b") (match_operand:SI 2 "call_insn_operand" "")] UNSPEC_TLS_LD_BASE)) (clobber (match_scratch:SI 3 "=d")) (clobber (match_scratch:SI 4 "=c")) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_GNU_TLS" "lea{l}\t{%&@TLSLDM(%1), %0|%0, %&@TLSLDM[%1]}\;call\t%P2" [(set_attr "type" "multi") (set_attr "length" "11")]) (define_insn "*tls_local_dynamic_base_32_sun" [(set (match_operand:SI 0 "register_operand" "=a") (unspec:SI [(match_operand:SI 1 "register_operand" "b") (match_operand:SI 2 "call_insn_operand" "")] UNSPEC_TLS_LD_BASE)) (clobber (match_scratch:SI 3 "=d")) (clobber (match_scratch:SI 4 "=c")) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_SUN_TLS" "lea{l}\t{%&@TMDNX(%1), %3|%3, %&@TMDNX[%1]} push{l}\t%3\;call\t%&@TLSPLT\;pop{l}\t%3" [(set_attr "type" "multi") (set_attr "length" "13")]) (define_expand "tls_local_dynamic_base_32" [(parallel [(set (match_operand:SI 0 "register_operand" "") (unspec:SI [(match_dup 1) (match_dup 2)] UNSPEC_TLS_LD_BASE)) (clobber (match_scratch:SI 3 "")) (clobber (match_scratch:SI 4 "")) (clobber (reg:CC 17))])] "" { if (flag_pic) operands[1] = pic_offset_table_rtx; else { operands[1] = gen_reg_rtx (Pmode); emit_insn (gen_set_got (operands[1])); } operands[2] = ix86_tls_get_addr (); }) (define_insn "*tls_local_dynamic_base_64" [(set (match_operand:DI 0 "register_operand" "=a") (call (mem:QI (match_operand:DI 1 "call_insn_operand" "")) (match_operand:DI 2 "" ""))) (unspec:DI [(const_int 0)] UNSPEC_TLS_LD_BASE)] "TARGET_64BIT" "lea{q}\t{%&@TLSLD(%%rip), %%rdi|%%rdi, %&@TLSLD[%%rip]}\;call\t%P1" [(set_attr "type" "multi") (set_attr "length" "12")]) (define_expand "tls_local_dynamic_base_64" [(parallel [(set (match_operand:DI 0 "register_operand" "") (call (mem:QI (match_dup 1)) (const_int 0))) (unspec:DI [(const_int 0)] UNSPEC_TLS_LD_BASE)])] "" { operands[1] = ix86_tls_get_addr (); }) ;; Local dynamic of a single variable is a lose. Show combine how ;; to convert that back to global dynamic. (define_insn_and_split "*tls_local_dynamic_32_once" [(set (match_operand:SI 0 "register_operand" "=a") (plus:SI (unspec:SI [(match_operand:SI 1 "register_operand" "b") (match_operand:SI 2 "call_insn_operand" "")] UNSPEC_TLS_LD_BASE) (const:SI (unspec:SI [(match_operand:SI 3 "tls_symbolic_operand" "")] UNSPEC_DTPOFF)))) (clobber (match_scratch:SI 4 "=d")) (clobber (match_scratch:SI 5 "=c")) (clobber (reg:CC 17))] "" "#" "" [(parallel [(set (match_dup 0) (unspec:SI [(match_dup 1) (match_dup 3) (match_dup 2)] UNSPEC_TLS_GD)) (clobber (match_dup 4)) (clobber (match_dup 5)) (clobber (reg:CC 17))])] "") ;; Load and add the thread base pointer from %gs:0. (define_insn "*load_tp_si" [(set (match_operand:SI 0 "register_operand" "=r") (unspec:SI [(const_int 0)] UNSPEC_TP))] "!TARGET_64BIT" "mov{l}\t{%%gs:0, %0|%0, DWORD PTR %%gs:0}" [(set_attr "type" "imov") (set_attr "modrm" "0") (set_attr "length" "7") (set_attr "memory" "load") (set_attr "imm_disp" "false")]) (define_insn "*add_tp_si" [(set (match_operand:SI 0 "register_operand" "=r") (plus:SI (unspec:SI [(const_int 0)] UNSPEC_TP) (match_operand:SI 1 "register_operand" "0"))) (clobber (reg:CC 17))] "!TARGET_64BIT" "add{l}\t{%%gs:0, %0|%0, DWORD PTR %%gs:0}" [(set_attr "type" "alu") (set_attr "modrm" "0") (set_attr "length" "7") (set_attr "memory" "load") (set_attr "imm_disp" "false")]) (define_insn "*load_tp_di" [(set (match_operand:DI 0 "register_operand" "=r") (unspec:DI [(const_int 0)] UNSPEC_TP))] "TARGET_64BIT" "mov{q}\t{%%fs:0, %0|%0, QWORD PTR %%fs:0}" [(set_attr "type" "imov") (set_attr "modrm" "0") (set_attr "length" "7") (set_attr "memory" "load") (set_attr "imm_disp" "false")]) (define_insn "*add_tp_di" [(set (match_operand:DI 0 "register_operand" "=r") (plus:DI (unspec:DI [(const_int 0)] UNSPEC_TP) (match_operand:DI 1 "register_operand" "0"))) (clobber (reg:CC 17))] "TARGET_64BIT" "add{q}\t{%%fs:0, %0|%0, QWORD PTR %%fs:0}" [(set_attr "type" "alu") (set_attr "modrm" "0") (set_attr "length" "7") (set_attr "memory" "load") (set_attr "imm_disp" "false")]) ;; These patterns match the binary 387 instructions for addM3, subM3, ;; mulM3 and divM3. There are three patterns for each of DFmode and ;; SFmode. The first is the normal insn, the second the same insn but ;; with one operand a conversion, and the third the same insn but with ;; the other operand a conversion. The conversion may be SFmode or ;; SImode if the target mode DFmode, but only SImode if the target mode ;; is SFmode. ;; Gcc is slightly more smart about handling normal two address instructions ;; so use special patterns for add and mull. (define_insn "*fop_sf_comm_nosse" [(set (match_operand:SF 0 "register_operand" "=f") (match_operator:SF 3 "binary_fp_operator" [(match_operand:SF 1 "nonimmediate_operand" "%0") (match_operand:SF 2 "nonimmediate_operand" "fm")]))] "TARGET_80387 && !TARGET_SSE_MATH && GET_RTX_CLASS (GET_CODE (operands[3])) == 'c' && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "* return output_387_binary_op (insn, operands);" [(set (attr "type") (if_then_else (match_operand:SF 3 "mult_operator" "") (const_string "fmul") (const_string "fop"))) (set_attr "mode" "SF")]) (define_insn "*fop_sf_comm" [(set (match_operand:SF 0 "register_operand" "=f#x,x#f") (match_operator:SF 3 "binary_fp_operator" [(match_operand:SF 1 "nonimmediate_operand" "%0,0") (match_operand:SF 2 "nonimmediate_operand" "fm#x,xm#f")]))] "TARGET_80387 && TARGET_SSE_MATH && TARGET_MIX_SSE_I387 && GET_RTX_CLASS (GET_CODE (operands[3])) == 'c' && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "* return output_387_binary_op (insn, operands);" [(set (attr "type") (if_then_else (eq_attr "alternative" "1") (if_then_else (match_operand:SF 3 "mult_operator" "") (const_string "ssemul") (const_string "sseadd")) (if_then_else (match_operand:SF 3 "mult_operator" "") (const_string "fmul") (const_string "fop")))) (set_attr "mode" "SF")]) (define_insn "*fop_sf_comm_sse" [(set (match_operand:SF 0 "register_operand" "=x") (match_operator:SF 3 "binary_fp_operator" [(match_operand:SF 1 "nonimmediate_operand" "%0") (match_operand:SF 2 "nonimmediate_operand" "xm")]))] "TARGET_SSE_MATH && GET_RTX_CLASS (GET_CODE (operands[3])) == 'c' && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "* return output_387_binary_op (insn, operands);" [(set (attr "type") (if_then_else (match_operand:SF 3 "mult_operator" "") (const_string "ssemul") (const_string "sseadd"))) (set_attr "mode" "SF")]) (define_insn "*fop_df_comm_nosse" [(set (match_operand:DF 0 "register_operand" "=f") (match_operator:DF 3 "binary_fp_operator" [(match_operand:DF 1 "nonimmediate_operand" "%0") (match_operand:DF 2 "nonimmediate_operand" "fm")]))] "TARGET_80387 && (!TARGET_SSE2 || !TARGET_SSE_MATH) && GET_RTX_CLASS (GET_CODE (operands[3])) == 'c' && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "* return output_387_binary_op (insn, operands);" [(set (attr "type") (if_then_else (match_operand:SF 3 "mult_operator" "") (const_string "fmul") (const_string "fop"))) (set_attr "mode" "DF")]) (define_insn "*fop_df_comm" [(set (match_operand:DF 0 "register_operand" "=f#Y,Y#f") (match_operator:DF 3 "binary_fp_operator" [(match_operand:DF 1 "nonimmediate_operand" "%0,0") (match_operand:DF 2 "nonimmediate_operand" "fm#Y,Ym#f")]))] "TARGET_80387 && TARGET_SSE_MATH && TARGET_SSE2 && TARGET_MIX_SSE_I387 && GET_RTX_CLASS (GET_CODE (operands[3])) == 'c' && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "* return output_387_binary_op (insn, operands);" [(set (attr "type") (if_then_else (eq_attr "alternative" "1") (if_then_else (match_operand:SF 3 "mult_operator" "") (const_string "ssemul") (const_string "sseadd")) (if_then_else (match_operand:SF 3 "mult_operator" "") (const_string "fmul") (const_string "fop")))) (set_attr "mode" "DF")]) (define_insn "*fop_df_comm_sse" [(set (match_operand:DF 0 "register_operand" "=Y") (match_operator:DF 3 "binary_fp_operator" [(match_operand:DF 1 "nonimmediate_operand" "%0") (match_operand:DF 2 "nonimmediate_operand" "Ym")]))] "TARGET_SSE2 && TARGET_SSE_MATH && GET_RTX_CLASS (GET_CODE (operands[3])) == 'c' && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "* return output_387_binary_op (insn, operands);" [(set (attr "type") (if_then_else (match_operand:SF 3 "mult_operator" "") (const_string "ssemul") (const_string "sseadd"))) (set_attr "mode" "DF")]) (define_insn "*fop_xf_comm" [(set (match_operand:XF 0 "register_operand" "=f") (match_operator:XF 3 "binary_fp_operator" [(match_operand:XF 1 "register_operand" "%0") (match_operand:XF 2 "register_operand" "f")]))] "TARGET_80387 && GET_RTX_CLASS (GET_CODE (operands[3])) == 'c'" "* return output_387_binary_op (insn, operands);" [(set (attr "type") (if_then_else (match_operand:XF 3 "mult_operator" "") (const_string "fmul") (const_string "fop"))) (set_attr "mode" "XF")]) (define_insn "*fop_sf_1_nosse" [(set (match_operand:SF 0 "register_operand" "=f,f") (match_operator:SF 3 "binary_fp_operator" [(match_operand:SF 1 "nonimmediate_operand" "0,fm") (match_operand:SF 2 "nonimmediate_operand" "fm,0")]))] "TARGET_80387 && !TARGET_SSE_MATH && GET_RTX_CLASS (GET_CODE (operands[3])) != 'c' && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "* return output_387_binary_op (insn, operands);" [(set (attr "type") (cond [(match_operand:SF 3 "mult_operator" "") (const_string "fmul") (match_operand:SF 3 "div_operator" "") (const_string "fdiv") ] (const_string "fop"))) (set_attr "mode" "SF")]) (define_insn "*fop_sf_1" [(set (match_operand:SF 0 "register_operand" "=f,f,x") (match_operator:SF 3 "binary_fp_operator" [(match_operand:SF 1 "nonimmediate_operand" "0,fm,0") (match_operand:SF 2 "nonimmediate_operand" "fm,0,xm#f")]))] "TARGET_80387 && TARGET_SSE_MATH && TARGET_MIX_SSE_I387 && GET_RTX_CLASS (GET_CODE (operands[3])) != 'c' && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "* return output_387_binary_op (insn, operands);" [(set (attr "type") (cond [(and (eq_attr "alternative" "2") (match_operand:SF 3 "mult_operator" "")) (const_string "ssemul") (and (eq_attr "alternative" "2") (match_operand:SF 3 "div_operator" "")) (const_string "ssediv") (eq_attr "alternative" "2") (const_string "sseadd") (match_operand:SF 3 "mult_operator" "") (const_string "fmul") (match_operand:SF 3 "div_operator" "") (const_string "fdiv") ] (const_string "fop"))) (set_attr "mode" "SF")]) (define_insn "*fop_sf_1_sse" [(set (match_operand:SF 0 "register_operand" "=x") (match_operator:SF 3 "binary_fp_operator" [(match_operand:SF 1 "register_operand" "0") (match_operand:SF 2 "nonimmediate_operand" "xm")]))] "TARGET_SSE_MATH && GET_RTX_CLASS (GET_CODE (operands[3])) != 'c'" "* return output_387_binary_op (insn, operands);" [(set (attr "type") (cond [(match_operand:SF 3 "mult_operator" "") (const_string "ssemul") (match_operand:SF 3 "div_operator" "") (const_string "ssediv") ] (const_string "sseadd"))) (set_attr "mode" "SF")]) ;; ??? Add SSE splitters for these! (define_insn "*fop_sf_2" [(set (match_operand:SF 0 "register_operand" "=f,f") (match_operator:SF 3 "binary_fp_operator" [(float:SF (match_operand:SI 1 "nonimmediate_operand" "m,?r")) (match_operand:SF 2 "register_operand" "0,0")]))] "TARGET_80387 && TARGET_USE_FIOP && !TARGET_SSE_MATH" "* return which_alternative ? \"#\" : output_387_binary_op (insn, operands);" [(set (attr "type") (cond [(match_operand:SF 3 "mult_operator" "") (const_string "fmul") (match_operand:SF 3 "div_operator" "") (const_string "fdiv") ] (const_string "fop"))) (set_attr "fp_int_src" "true") (set_attr "ppro_uops" "many") (set_attr "mode" "SI")]) (define_insn "*fop_sf_3" [(set (match_operand:SF 0 "register_operand" "=f,f") (match_operator:SF 3 "binary_fp_operator" [(match_operand:SF 1 "register_operand" "0,0") (float:SF (match_operand:SI 2 "nonimmediate_operand" "m,?r"))]))] "TARGET_80387 && TARGET_USE_FIOP && !TARGET_SSE_MATH" "* return which_alternative ? \"#\" : output_387_binary_op (insn, operands);" [(set (attr "type") (cond [(match_operand:SF 3 "mult_operator" "") (const_string "fmul") (match_operand:SF 3 "div_operator" "") (const_string "fdiv") ] (const_string "fop"))) (set_attr "fp_int_src" "true") (set_attr "ppro_uops" "many") (set_attr "mode" "SI")]) (define_insn "*fop_df_1_nosse" [(set (match_operand:DF 0 "register_operand" "=f,f") (match_operator:DF 3 "binary_fp_operator" [(match_operand:DF 1 "nonimmediate_operand" "0,fm") (match_operand:DF 2 "nonimmediate_operand" "fm,0")]))] "TARGET_80387 && (!TARGET_SSE2 || !TARGET_SSE_MATH) && GET_RTX_CLASS (GET_CODE (operands[3])) != 'c' && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "* return output_387_binary_op (insn, operands);" [(set (attr "type") (cond [(match_operand:DF 3 "mult_operator" "") (const_string "fmul") (match_operand:DF 3 "div_operator" "") (const_string "fdiv") ] (const_string "fop"))) (set_attr "mode" "DF")]) (define_insn "*fop_df_1" [(set (match_operand:DF 0 "register_operand" "=f#Y,f#Y,Y#f") (match_operator:DF 3 "binary_fp_operator" [(match_operand:DF 1 "nonimmediate_operand" "0,fm,0") (match_operand:DF 2 "nonimmediate_operand" "fm,0,Ym#f")]))] "TARGET_80387 && TARGET_SSE2 && TARGET_SSE_MATH && TARGET_MIX_SSE_I387 && GET_RTX_CLASS (GET_CODE (operands[3])) != 'c' && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "* return output_387_binary_op (insn, operands);" [(set (attr "type") (cond [(and (eq_attr "alternative" "2") (match_operand:SF 3 "mult_operator" "")) (const_string "ssemul") (and (eq_attr "alternative" "2") (match_operand:SF 3 "div_operator" "")) (const_string "ssediv") (eq_attr "alternative" "2") (const_string "sseadd") (match_operand:DF 3 "mult_operator" "") (const_string "fmul") (match_operand:DF 3 "div_operator" "") (const_string "fdiv") ] (const_string "fop"))) (set_attr "mode" "DF")]) (define_insn "*fop_df_1_sse" [(set (match_operand:DF 0 "register_operand" "=Y") (match_operator:DF 3 "binary_fp_operator" [(match_operand:DF 1 "register_operand" "0") (match_operand:DF 2 "nonimmediate_operand" "Ym")]))] "TARGET_SSE2 && TARGET_SSE_MATH && GET_RTX_CLASS (GET_CODE (operands[3])) != 'c'" "* return output_387_binary_op (insn, operands);" [(set_attr "mode" "DF") (set (attr "type") (cond [(match_operand:SF 3 "mult_operator" "") (const_string "ssemul") (match_operand:SF 3 "div_operator" "") (const_string "ssediv") ] (const_string "sseadd")))]) ;; ??? Add SSE splitters for these! (define_insn "*fop_df_2" [(set (match_operand:DF 0 "register_operand" "=f,f") (match_operator:DF 3 "binary_fp_operator" [(float:DF (match_operand:SI 1 "nonimmediate_operand" "m,?r")) (match_operand:DF 2 "register_operand" "0,0")]))] "TARGET_80387 && TARGET_USE_FIOP && !(TARGET_SSE2 && TARGET_SSE_MATH)" "* return which_alternative ? \"#\" : output_387_binary_op (insn, operands);" [(set (attr "type") (cond [(match_operand:DF 3 "mult_operator" "") (const_string "fmul") (match_operand:DF 3 "div_operator" "") (const_string "fdiv") ] (const_string "fop"))) (set_attr "fp_int_src" "true") (set_attr "ppro_uops" "many") (set_attr "mode" "SI")]) (define_insn "*fop_df_3" [(set (match_operand:DF 0 "register_operand" "=f,f") (match_operator:DF 3 "binary_fp_operator" [(match_operand:DF 1 "register_operand" "0,0") (float:DF (match_operand:SI 2 "nonimmediate_operand" "m,?r"))]))] "TARGET_80387 && TARGET_USE_FIOP && !(TARGET_SSE2 && TARGET_SSE_MATH)" "* return which_alternative ? \"#\" : output_387_binary_op (insn, operands);" [(set (attr "type") (cond [(match_operand:DF 3 "mult_operator" "") (const_string "fmul") (match_operand:DF 3 "div_operator" "") (const_string "fdiv") ] (const_string "fop"))) (set_attr "fp_int_src" "true") (set_attr "ppro_uops" "many") (set_attr "mode" "SI")]) (define_insn "*fop_df_4" [(set (match_operand:DF 0 "register_operand" "=f,f") (match_operator:DF 3 "binary_fp_operator" [(float_extend:DF (match_operand:SF 1 "nonimmediate_operand" "fm,0")) (match_operand:DF 2 "register_operand" "0,f")]))] "TARGET_80387 && (!TARGET_SSE2 || !TARGET_SSE_MATH) && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "* return output_387_binary_op (insn, operands);" [(set (attr "type") (cond [(match_operand:DF 3 "mult_operator" "") (const_string "fmul") (match_operand:DF 3 "div_operator" "") (const_string "fdiv") ] (const_string "fop"))) (set_attr "mode" "SF")]) (define_insn "*fop_df_5" [(set (match_operand:DF 0 "register_operand" "=f,f") (match_operator:DF 3 "binary_fp_operator" [(match_operand:DF 1 "register_operand" "0,f") (float_extend:DF (match_operand:SF 2 "nonimmediate_operand" "fm,0"))]))] "TARGET_80387 && !(TARGET_SSE2 && TARGET_SSE_MATH)" "* return output_387_binary_op (insn, operands);" [(set (attr "type") (cond [(match_operand:DF 3 "mult_operator" "") (const_string "fmul") (match_operand:DF 3 "div_operator" "") (const_string "fdiv") ] (const_string "fop"))) (set_attr "mode" "SF")]) (define_insn "*fop_df_6" [(set (match_operand:DF 0 "register_operand" "=f,f") (match_operator:DF 3 "binary_fp_operator" [(float_extend:DF (match_operand:SF 1 "register_operand" "0,f")) (float_extend:DF (match_operand:SF 2 "nonimmediate_operand" "fm,0"))]))] "TARGET_80387 && !(TARGET_SSE2 && TARGET_SSE_MATH)" "* return output_387_binary_op (insn, operands);" [(set (attr "type") (cond [(match_operand:DF 3 "mult_operator" "") (const_string "fmul") (match_operand:DF 3 "div_operator" "") (const_string "fdiv") ] (const_string "fop"))) (set_attr "mode" "SF")]) (define_insn "*fop_xf_1" [(set (match_operand:XF 0 "register_operand" "=f,f") (match_operator:XF 3 "binary_fp_operator" [(match_operand:XF 1 "register_operand" "0,f") (match_operand:XF 2 "register_operand" "f,0")]))] "TARGET_80387 && GET_RTX_CLASS (GET_CODE (operands[3])) != 'c'" "* return output_387_binary_op (insn, operands);" [(set (attr "type") (cond [(match_operand:XF 3 "mult_operator" "") (const_string "fmul") (match_operand:XF 3 "div_operator" "") (const_string "fdiv") ] (const_string "fop"))) (set_attr "mode" "XF")]) (define_insn "*fop_xf_2" [(set (match_operand:XF 0 "register_operand" "=f,f") (match_operator:XF 3 "binary_fp_operator" [(float:XF (match_operand:SI 1 "nonimmediate_operand" "m,?r")) (match_operand:XF 2 "register_operand" "0,0")]))] "TARGET_80387 && TARGET_USE_FIOP" "* return which_alternative ? \"#\" : output_387_binary_op (insn, operands);" [(set (attr "type") (cond [(match_operand:XF 3 "mult_operator" "") (const_string "fmul") (match_operand:XF 3 "div_operator" "") (const_string "fdiv") ] (const_string "fop"))) (set_attr "fp_int_src" "true") (set_attr "mode" "SI") (set_attr "ppro_uops" "many")]) (define_insn "*fop_xf_3" [(set (match_operand:XF 0 "register_operand" "=f,f") (match_operator:XF 3 "binary_fp_operator" [(match_operand:XF 1 "register_operand" "0,0") (float:XF (match_operand:SI 2 "nonimmediate_operand" "m,?r"))]))] "TARGET_80387 && TARGET_USE_FIOP" "* return which_alternative ? \"#\" : output_387_binary_op (insn, operands);" [(set (attr "type") (cond [(match_operand:XF 3 "mult_operator" "") (const_string "fmul") (match_operand:XF 3 "div_operator" "") (const_string "fdiv") ] (const_string "fop"))) (set_attr "fp_int_src" "true") (set_attr "mode" "SI") (set_attr "ppro_uops" "many")]) (define_insn "*fop_xf_4" [(set (match_operand:XF 0 "register_operand" "=f,f") (match_operator:XF 3 "binary_fp_operator" [(float_extend:XF (match_operand 1 "nonimmediate_operand" "fm,0")) (match_operand:XF 2 "register_operand" "0,f")]))] "TARGET_80387" "* return output_387_binary_op (insn, operands);" [(set (attr "type") (cond [(match_operand:XF 3 "mult_operator" "") (const_string "fmul") (match_operand:XF 3 "div_operator" "") (const_string "fdiv") ] (const_string "fop"))) (set_attr "mode" "SF")]) (define_insn "*fop_xf_5" [(set (match_operand:XF 0 "register_operand" "=f,f") (match_operator:XF 3 "binary_fp_operator" [(match_operand:XF 1 "register_operand" "0,f") (float_extend:XF (match_operand 2 "nonimmediate_operand" "fm,0"))]))] "TARGET_80387" "* return output_387_binary_op (insn, operands);" [(set (attr "type") (cond [(match_operand:XF 3 "mult_operator" "") (const_string "fmul") (match_operand:XF 3 "div_operator" "") (const_string "fdiv") ] (const_string "fop"))) (set_attr "mode" "SF")]) (define_insn "*fop_xf_6" [(set (match_operand:XF 0 "register_operand" "=f,f") (match_operator:XF 3 "binary_fp_operator" [(float_extend:XF (match_operand 1 "register_operand" "0,f")) (float_extend:XF (match_operand 2 "nonimmediate_operand" "fm,0"))]))] "TARGET_80387" "* return output_387_binary_op (insn, operands);" [(set (attr "type") (cond [(match_operand:XF 3 "mult_operator" "") (const_string "fmul") (match_operand:XF 3 "div_operator" "") (const_string "fdiv") ] (const_string "fop"))) (set_attr "mode" "SF")]) (define_split [(set (match_operand 0 "register_operand" "") (match_operator 3 "binary_fp_operator" [(float (match_operand:SI 1 "register_operand" "")) (match_operand 2 "register_operand" "")]))] "TARGET_80387 && reload_completed && FLOAT_MODE_P (GET_MODE (operands[0]))" [(const_int 0)] { operands[4] = ix86_force_to_memory (GET_MODE (operands[1]), operands[1]); operands[4] = gen_rtx_FLOAT (GET_MODE (operands[0]), operands[4]); emit_insn (gen_rtx_SET (VOIDmode, operands[0], gen_rtx_fmt_ee (GET_CODE (operands[3]), GET_MODE (operands[3]), operands[4], operands[2]))); ix86_free_from_memory (GET_MODE (operands[1])); DONE; }) (define_split [(set (match_operand 0 "register_operand" "") (match_operator 3 "binary_fp_operator" [(match_operand 1 "register_operand" "") (float (match_operand:SI 2 "register_operand" ""))]))] "TARGET_80387 && reload_completed && FLOAT_MODE_P (GET_MODE (operands[0]))" [(const_int 0)] { operands[4] = ix86_force_to_memory (GET_MODE (operands[2]), operands[2]); operands[4] = gen_rtx_FLOAT (GET_MODE (operands[0]), operands[4]); emit_insn (gen_rtx_SET (VOIDmode, operands[0], gen_rtx_fmt_ee (GET_CODE (operands[3]), GET_MODE (operands[3]), operands[1], operands[4]))); ix86_free_from_memory (GET_MODE (operands[2])); DONE; }) ;; FPU special functions. (define_expand "sqrtsf2" [(set (match_operand:SF 0 "register_operand" "") (sqrt:SF (match_operand:SF 1 "nonimmediate_operand" "")))] "(! TARGET_NO_FANCY_MATH_387 && TARGET_80387) || TARGET_SSE_MATH" { if (!TARGET_SSE_MATH) operands[1] = force_reg (SFmode, operands[1]); }) (define_insn "sqrtsf2_1" [(set (match_operand:SF 0 "register_operand" "=f#x,x#f") (sqrt:SF (match_operand:SF 1 "nonimmediate_operand" "0#x,xm#f")))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && (TARGET_SSE_MATH && TARGET_MIX_SSE_I387)" "@ fsqrt sqrtss\t{%1, %0|%0, %1}" [(set_attr "type" "fpspc,sse") (set_attr "mode" "SF,SF") (set_attr "athlon_decode" "direct,*")]) (define_insn "sqrtsf2_1_sse_only" [(set (match_operand:SF 0 "register_operand" "=x") (sqrt:SF (match_operand:SF 1 "nonimmediate_operand" "xm")))] "TARGET_SSE_MATH && (!TARGET_80387 || !TARGET_MIX_SSE_I387)" "sqrtss\t{%1, %0|%0, %1}" [(set_attr "type" "sse") (set_attr "mode" "SF") (set_attr "athlon_decode" "*")]) (define_insn "sqrtsf2_i387" [(set (match_operand:SF 0 "register_operand" "=f") (sqrt:SF (match_operand:SF 1 "register_operand" "0")))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && !TARGET_SSE_MATH" "fsqrt" [(set_attr "type" "fpspc") (set_attr "mode" "SF") (set_attr "athlon_decode" "direct")]) (define_expand "sqrtdf2" [(set (match_operand:DF 0 "register_operand" "") (sqrt:DF (match_operand:DF 1 "nonimmediate_operand" "")))] "(! TARGET_NO_FANCY_MATH_387 && TARGET_80387) || (TARGET_SSE2 && TARGET_SSE_MATH)" { if (!TARGET_SSE2 || !TARGET_SSE_MATH) operands[1] = force_reg (DFmode, operands[1]); }) (define_insn "sqrtdf2_1" [(set (match_operand:DF 0 "register_operand" "=f#Y,Y#f") (sqrt:DF (match_operand:DF 1 "nonimmediate_operand" "0#Y,Ym#f")))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && (TARGET_SSE2 && TARGET_SSE_MATH && TARGET_MIX_SSE_I387)" "@ fsqrt sqrtsd\t{%1, %0|%0, %1}" [(set_attr "type" "fpspc,sse") (set_attr "mode" "DF,DF") (set_attr "athlon_decode" "direct,*")]) (define_insn "sqrtdf2_1_sse_only" [(set (match_operand:DF 0 "register_operand" "=Y") (sqrt:DF (match_operand:DF 1 "nonimmediate_operand" "Ym")))] "TARGET_SSE2 && TARGET_SSE_MATH && (!TARGET_80387 || !TARGET_MIX_SSE_I387)" "sqrtsd\t{%1, %0|%0, %1}" [(set_attr "type" "sse") (set_attr "mode" "DF") (set_attr "athlon_decode" "*")]) (define_insn "sqrtdf2_i387" [(set (match_operand:DF 0 "register_operand" "=f") (sqrt:DF (match_operand:DF 1 "register_operand" "0")))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && (!TARGET_SSE2 || !TARGET_SSE_MATH)" "fsqrt" [(set_attr "type" "fpspc") (set_attr "mode" "DF") (set_attr "athlon_decode" "direct")]) (define_insn "*sqrtextendsfdf2" [(set (match_operand:DF 0 "register_operand" "=f") (sqrt:DF (float_extend:DF (match_operand:SF 1 "register_operand" "0"))))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && !(TARGET_SSE2 && TARGET_SSE_MATH)" "fsqrt" [(set_attr "type" "fpspc") (set_attr "mode" "DF") (set_attr "athlon_decode" "direct")]) (define_insn "sqrtxf2" [(set (match_operand:XF 0 "register_operand" "=f") (sqrt:XF (match_operand:XF 1 "register_operand" "0")))] "TARGET_80387 && !TARGET_NO_FANCY_MATH_387 && (TARGET_IEEE_FP || flag_unsafe_math_optimizations) " "fsqrt" [(set_attr "type" "fpspc") (set_attr "mode" "XF") (set_attr "athlon_decode" "direct")]) (define_insn "*sqrtextenddfxf2" [(set (match_operand:XF 0 "register_operand" "=f") (sqrt:XF (float_extend:XF (match_operand:DF 1 "register_operand" "0"))))] "TARGET_80387 && !TARGET_NO_FANCY_MATH_387" "fsqrt" [(set_attr "type" "fpspc") (set_attr "mode" "XF") (set_attr "athlon_decode" "direct")]) (define_insn "*sqrtextendsfxf2" [(set (match_operand:XF 0 "register_operand" "=f") (sqrt:XF (float_extend:XF (match_operand:SF 1 "register_operand" "0"))))] "TARGET_80387 && !TARGET_NO_FANCY_MATH_387" "fsqrt" [(set_attr "type" "fpspc") (set_attr "mode" "XF") (set_attr "athlon_decode" "direct")]) (define_insn "sindf2" [(set (match_operand:DF 0 "register_operand" "=f") (unspec:DF [(match_operand:DF 1 "register_operand" "0")] UNSPEC_SIN))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" "fsin" [(set_attr "type" "fpspc") (set_attr "mode" "DF")]) (define_insn "sinsf2" [(set (match_operand:SF 0 "register_operand" "=f") (unspec:SF [(match_operand:SF 1 "register_operand" "0")] UNSPEC_SIN))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" "fsin" [(set_attr "type" "fpspc") (set_attr "mode" "SF")]) (define_insn "*sinextendsfdf2" [(set (match_operand:DF 0 "register_operand" "=f") (unspec:DF [(float_extend:DF (match_operand:SF 1 "register_operand" "0"))] UNSPEC_SIN))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" "fsin" [(set_attr "type" "fpspc") (set_attr "mode" "DF")]) (define_insn "sinxf2" [(set (match_operand:XF 0 "register_operand" "=f") (unspec:XF [(match_operand:XF 1 "register_operand" "0")] UNSPEC_SIN))] "TARGET_80387 && !TARGET_NO_FANCY_MATH_387 && flag_unsafe_math_optimizations" "fsin" [(set_attr "type" "fpspc") (set_attr "mode" "XF")]) (define_insn "cosdf2" [(set (match_operand:DF 0 "register_operand" "=f") (unspec:DF [(match_operand:DF 1 "register_operand" "0")] UNSPEC_COS))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" "fcos" [(set_attr "type" "fpspc") (set_attr "mode" "DF")]) (define_insn "cossf2" [(set (match_operand:SF 0 "register_operand" "=f") (unspec:SF [(match_operand:SF 1 "register_operand" "0")] UNSPEC_COS))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" "fcos" [(set_attr "type" "fpspc") (set_attr "mode" "SF")]) (define_insn "*cosextendsfdf2" [(set (match_operand:DF 0 "register_operand" "=f") (unspec:DF [(float_extend:DF (match_operand:SF 1 "register_operand" "0"))] UNSPEC_COS))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" "fcos" [(set_attr "type" "fpspc") (set_attr "mode" "DF")]) (define_insn "cosxf2" [(set (match_operand:XF 0 "register_operand" "=f") (unspec:XF [(match_operand:XF 1 "register_operand" "0")] UNSPEC_COS))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" "fcos" [(set_attr "type" "fpspc") (set_attr "mode" "XF")]) (define_insn "atan2df3_1" [(set (match_operand:DF 0 "register_operand" "=f") (unspec:DF [(match_operand:DF 2 "register_operand" "0") (match_operand:DF 1 "register_operand" "u")] UNSPEC_FPATAN)) (clobber (match_scratch:DF 3 "=1"))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" "fpatan" [(set_attr "type" "fpspc") (set_attr "mode" "DF")]) (define_expand "atan2df3" [(use (match_operand:DF 0 "register_operand" "=f")) (use (match_operand:DF 2 "register_operand" "0")) (use (match_operand:DF 1 "register_operand" "u"))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" { rtx copy = gen_reg_rtx (DFmode); emit_move_insn (copy, operands[1]); emit_insn (gen_atan2df3_1 (operands[0], copy, operands[2])); DONE; }) (define_insn "atan2sf3_1" [(set (match_operand:SF 0 "register_operand" "=f") (unspec:SF [(match_operand:SF 2 "register_operand" "0") (match_operand:SF 1 "register_operand" "u")] UNSPEC_FPATAN)) (clobber (match_scratch:SF 3 "=1"))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" "fpatan" [(set_attr "type" "fpspc") (set_attr "mode" "SF")]) (define_expand "atan2sf3" [(use (match_operand:SF 0 "register_operand" "=f")) (use (match_operand:SF 2 "register_operand" "0")) (use (match_operand:SF 1 "register_operand" "u"))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" { rtx copy = gen_reg_rtx (SFmode); emit_move_insn (copy, operands[1]); emit_insn (gen_atan2sf3_1 (operands[0], copy, operands[2])); DONE; }) (define_insn "atan2xf3_1" [(set (match_operand:XF 0 "register_operand" "=f") (unspec:XF [(match_operand:XF 2 "register_operand" "0") (match_operand:XF 1 "register_operand" "u")] UNSPEC_FPATAN)) (clobber (match_scratch:XF 3 "=1"))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" "fpatan" [(set_attr "type" "fpspc") (set_attr "mode" "XF")]) (define_expand "atan2xf3" [(use (match_operand:XF 0 "register_operand" "=f")) (use (match_operand:XF 2 "register_operand" "0")) (use (match_operand:XF 1 "register_operand" "u"))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" { rtx copy = gen_reg_rtx (XFmode); emit_move_insn (copy, operands[1]); emit_insn (gen_atan2xf3_1 (operands[0], copy, operands[2])); DONE; }) (define_insn "*fyl2x_sfxf3" [(set (match_operand:SF 0 "register_operand" "=f") (unspec:SF [(match_operand:SF 2 "register_operand" "0") (match_operand:XF 1 "register_operand" "u")] UNSPEC_FYL2X)) (clobber (match_scratch:SF 3 "=1"))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" "fyl2x" [(set_attr "type" "fpspc") (set_attr "mode" "SF")]) (define_insn "*fyl2x_dfxf3" [(set (match_operand:DF 0 "register_operand" "=f") (unspec:DF [(match_operand:DF 2 "register_operand" "0") (match_operand:XF 1 "register_operand" "u")] UNSPEC_FYL2X)) (clobber (match_scratch:DF 3 "=1"))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" "fyl2x" [(set_attr "type" "fpspc") (set_attr "mode" "DF")]) (define_insn "*fyl2x_xf3" [(set (match_operand:XF 0 "register_operand" "=f") (unspec:XF [(match_operand:XF 2 "register_operand" "0") (match_operand:XF 1 "register_operand" "u")] UNSPEC_FYL2X)) (clobber (match_scratch:XF 3 "=1"))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" "fyl2x" [(set_attr "type" "fpspc") (set_attr "mode" "XF")]) (define_expand "logsf2" [(parallel [(set (match_operand:SF 0 "register_operand" "") (unspec:SF [(match_operand:SF 1 "register_operand" "") (match_dup 2)] UNSPEC_FYL2X)) (clobber (match_scratch:SF 3 ""))])] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" { rtx temp; operands[2] = gen_reg_rtx (XFmode); temp = standard_80387_constant_rtx (4); /* fldln2 */ emit_move_insn (operands[2], temp); }) (define_expand "logdf2" [(parallel [(set (match_operand:DF 0 "register_operand" "") (unspec:DF [(match_operand:DF 1 "register_operand" "") (match_dup 2)] UNSPEC_FYL2X)) (clobber (match_scratch:DF 3 ""))])] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" { rtx temp; operands[2] = gen_reg_rtx (XFmode); temp = standard_80387_constant_rtx (4); /* fldln2 */ emit_move_insn (operands[2], temp); }) (define_expand "logxf2" [(parallel [(set (match_operand:XF 0 "register_operand" "") (unspec:XF [(match_operand:XF 1 "register_operand" "") (match_dup 2)] UNSPEC_FYL2X)) (clobber (match_scratch:XF 3 ""))])] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" { rtx temp; operands[2] = gen_reg_rtx (XFmode); temp = standard_80387_constant_rtx (4); /* fldln2 */ emit_move_insn (operands[2], temp); }) (define_insn "*fscale_sfxf3" [(set (match_operand:SF 0 "register_operand" "=f") (unspec:SF [(match_operand:XF 2 "register_operand" "0") (match_operand:XF 1 "register_operand" "u")] UNSPEC_FSCALE)) (clobber (match_scratch:SF 3 "=1"))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" "fscale\;fstp\t%y1" [(set_attr "type" "fpspc") (set_attr "mode" "SF")]) (define_insn "*fscale_dfxf3" [(set (match_operand:DF 0 "register_operand" "=f") (unspec:DF [(match_operand:XF 2 "register_operand" "0") (match_operand:XF 1 "register_operand" "u")] UNSPEC_FSCALE)) (clobber (match_scratch:DF 3 "=1"))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" "fscale\;fstp\t%y1" [(set_attr "type" "fpspc") (set_attr "mode" "DF")]) (define_insn "*fscale_xf3" [(set (match_operand:XF 0 "register_operand" "=f") (unspec:XF [(match_operand:XF 2 "register_operand" "0") (match_operand:XF 1 "register_operand" "u")] UNSPEC_FSCALE)) (clobber (match_scratch:XF 3 "=1"))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" "fscale\;fstp\t%y1" [(set_attr "type" "fpspc") (set_attr "mode" "XF")]) (define_insn "*frndintxf2" [(set (match_operand:XF 0 "register_operand" "=f") (unspec:XF [(match_operand:XF 1 "register_operand" "0")] UNSPEC_FRNDINT))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" "frndint" [(set_attr "type" "fpspc") (set_attr "mode" "XF")]) (define_insn "*f2xm1xf2" [(set (match_operand:XF 0 "register_operand" "=f") (unspec:XF [(match_operand:XF 1 "register_operand" "0")] UNSPEC_F2XM1))] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" "f2xm1" [(set_attr "type" "fpspc") (set_attr "mode" "XF")]) (define_expand "expsf2" [(set (match_dup 2) (float_extend:XF (match_operand:SF 1 "register_operand" ""))) (set (match_dup 4) (mult:XF (match_dup 2) (match_dup 3))) (set (match_dup 5) (unspec:XF [(match_dup 4)] UNSPEC_FRNDINT)) (set (match_dup 6) (minus:XF (match_dup 4) (match_dup 5))) (set (match_dup 7) (unspec:XF [(match_dup 6)] UNSPEC_F2XM1)) (set (match_dup 9) (plus:XF (match_dup 7) (match_dup 8))) (parallel [(set (match_operand:SF 0 "register_operand" "") (unspec:SF [(match_dup 9) (match_dup 5)] UNSPEC_FSCALE)) (clobber (match_scratch:SF 5 ""))])] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" { rtx temp; int i; for (i=2; i<10; i++) operands[i] = gen_reg_rtx (XFmode); temp = standard_80387_constant_rtx (5); /* fldl2e */ emit_move_insn (operands[3], temp); emit_move_insn (operands[8], CONST1_RTX (XFmode)); /* fld1 */ }) (define_expand "expdf2" [(set (match_dup 2) (float_extend:XF (match_operand:DF 1 "register_operand" ""))) (set (match_dup 4) (mult:XF (match_dup 2) (match_dup 3))) (set (match_dup 5) (unspec:XF [(match_dup 4)] UNSPEC_FRNDINT)) (set (match_dup 6) (minus:XF (match_dup 4) (match_dup 5))) (set (match_dup 7) (unspec:XF [(match_dup 6)] UNSPEC_F2XM1)) (set (match_dup 9) (plus:XF (match_dup 7) (match_dup 8))) (parallel [(set (match_operand:DF 0 "register_operand" "") (unspec:DF [(match_dup 9) (match_dup 5)] UNSPEC_FSCALE)) (clobber (match_scratch:DF 5 ""))])] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" { rtx temp; int i; for (i=2; i<10; i++) operands[i] = gen_reg_rtx (XFmode); temp = standard_80387_constant_rtx (5); /* fldl2e */ emit_move_insn (operands[3], temp); emit_move_insn (operands[8], CONST1_RTX (XFmode)); /* fld1 */ }) (define_expand "expxf2" [(set (match_dup 3) (mult:XF (match_operand:XF 1 "register_operand" "") (match_dup 2))) (set (match_dup 4) (unspec:XF [(match_dup 3)] UNSPEC_FRNDINT)) (set (match_dup 5) (minus:XF (match_dup 3) (match_dup 4))) (set (match_dup 6) (unspec:XF [(match_dup 5)] UNSPEC_F2XM1)) (set (match_dup 8) (plus:XF (match_dup 6) (match_dup 7))) (parallel [(set (match_operand:XF 0 "register_operand" "") (unspec:XF [(match_dup 8) (match_dup 4)] UNSPEC_FSCALE)) (clobber (match_scratch:XF 5 ""))])] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" { rtx temp; int i; for (i=2; i<9; i++) operands[i] = gen_reg_rtx (XFmode); temp = standard_80387_constant_rtx (5); /* fldl2e */ emit_move_insn (operands[2], temp); emit_move_insn (operands[7], CONST1_RTX (XFmode)); /* fld1 */ }) (define_expand "atansf2" [(parallel [(set (match_operand:SF 0 "register_operand" "") (unspec:SF [(match_dup 2) (match_operand:SF 1 "register_operand" "")] UNSPEC_FPATAN)) (clobber (match_scratch:SF 3 ""))])] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" { operands[2] = gen_reg_rtx (SFmode); emit_move_insn (operands[2], CONST1_RTX (SFmode)); /* fld1 */ }) (define_expand "atandf2" [(parallel [(set (match_operand:DF 0 "register_operand" "") (unspec:DF [(match_dup 2) (match_operand:DF 1 "register_operand" "")] UNSPEC_FPATAN)) (clobber (match_scratch:DF 3 ""))])] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" { operands[2] = gen_reg_rtx (DFmode); emit_move_insn (operands[2], CONST1_RTX (DFmode)); /* fld1 */ }) (define_expand "atanxf2" [(parallel [(set (match_operand:XF 0 "register_operand" "") (unspec:XF [(match_dup 2) (match_operand:XF 1 "register_operand" "")] UNSPEC_FPATAN)) (clobber (match_scratch:XF 3 ""))])] "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_unsafe_math_optimizations" { operands[2] = gen_reg_rtx (XFmode); emit_move_insn (operands[2], CONST1_RTX (XFmode)); /* fld1 */ }) ;; Block operation instructions (define_insn "cld" [(set (reg:SI 19) (const_int 0))] "" "cld" [(set_attr "type" "cld")]) (define_expand "movstrsi" [(use (match_operand:BLK 0 "memory_operand" "")) (use (match_operand:BLK 1 "memory_operand" "")) (use (match_operand:SI 2 "nonmemory_operand" "")) (use (match_operand:SI 3 "const_int_operand" ""))] "! optimize_size" { if (ix86_expand_movstr (operands[0], operands[1], operands[2], operands[3])) DONE; else FAIL; }) (define_expand "movstrdi" [(use (match_operand:BLK 0 "memory_operand" "")) (use (match_operand:BLK 1 "memory_operand" "")) (use (match_operand:DI 2 "nonmemory_operand" "")) (use (match_operand:DI 3 "const_int_operand" ""))] "TARGET_64BIT" { if (ix86_expand_movstr (operands[0], operands[1], operands[2], operands[3])) DONE; else FAIL; }) ;; Most CPUs don't like single string operations ;; Handle this case here to simplify previous expander. (define_expand "strmov" [(set (match_dup 4) (match_operand 3 "memory_operand" "")) (set (match_operand 1 "memory_operand" "") (match_dup 4)) (parallel [(set (match_operand 0 "register_operand" "") (match_dup 5)) (clobber (reg:CC 17))]) (parallel [(set (match_operand 2 "register_operand" "") (match_dup 6)) (clobber (reg:CC 17))])] "" { rtx adjust = GEN_INT (GET_MODE_SIZE (GET_MODE (operands[1]))); /* If .md ever supports :P for Pmode, these can be directly in the pattern above. */ operands[5] = gen_rtx_PLUS (Pmode, operands[0], adjust); operands[6] = gen_rtx_PLUS (Pmode, operands[2], adjust); if (TARGET_SINGLE_STRINGOP || optimize_size) { emit_insn (gen_strmov_singleop (operands[0], operands[1], operands[2], operands[3], operands[5], operands[6])); DONE; } operands[4] = gen_reg_rtx (GET_MODE (operands[1])); }) (define_expand "strmov_singleop" [(parallel [(set (match_operand 1 "memory_operand" "") (match_operand 3 "memory_operand" "")) (set (match_operand 0 "register_operand" "") (match_operand 4 "" "")) (set (match_operand 2 "register_operand" "") (match_operand 5 "" "")) (use (reg:SI 19))])] "TARGET_SINGLE_STRINGOP || optimize_size" "") (define_insn "*strmovdi_rex_1" [(set (mem:DI (match_operand:DI 2 "register_operand" "0")) (mem:DI (match_operand:DI 3 "register_operand" "1"))) (set (match_operand:DI 0 "register_operand" "=D") (plus:DI (match_dup 2) (const_int 8))) (set (match_operand:DI 1 "register_operand" "=S") (plus:DI (match_dup 3) (const_int 8))) (use (reg:SI 19))] "TARGET_64BIT && (TARGET_SINGLE_STRINGOP || optimize_size)" "movsq" [(set_attr "type" "str") (set_attr "mode" "DI") (set_attr "memory" "both")]) (define_insn "*strmovsi_1" [(set (mem:SI (match_operand:SI 2 "register_operand" "0")) (mem:SI (match_operand:SI 3 "register_operand" "1"))) (set (match_operand:SI 0 "register_operand" "=D") (plus:SI (match_dup 2) (const_int 4))) (set (match_operand:SI 1 "register_operand" "=S") (plus:SI (match_dup 3) (const_int 4))) (use (reg:SI 19))] "!TARGET_64BIT && (TARGET_SINGLE_STRINGOP || optimize_size)" "{movsl|movsd}" [(set_attr "type" "str") (set_attr "mode" "SI") (set_attr "memory" "both")]) (define_insn "*strmovsi_rex_1" [(set (mem:SI (match_operand:DI 2 "register_operand" "0")) (mem:SI (match_operand:DI 3 "register_operand" "1"))) (set (match_operand:DI 0 "register_operand" "=D") (plus:DI (match_dup 2) (const_int 4))) (set (match_operand:DI 1 "register_operand" "=S") (plus:DI (match_dup 3) (const_int 4))) (use (reg:SI 19))] "TARGET_64BIT && (TARGET_SINGLE_STRINGOP || optimize_size)" "{movsl|movsd}" [(set_attr "type" "str") (set_attr "mode" "SI") (set_attr "memory" "both")]) (define_insn "*strmovhi_1" [(set (mem:HI (match_operand:SI 2 "register_operand" "0")) (mem:HI (match_operand:SI 3 "register_operand" "1"))) (set (match_operand:SI 0 "register_operand" "=D") (plus:SI (match_dup 2) (const_int 2))) (set (match_operand:SI 1 "register_operand" "=S") (plus:SI (match_dup 3) (const_int 2))) (use (reg:SI 19))] "!TARGET_64BIT && (TARGET_SINGLE_STRINGOP || optimize_size)" "movsw" [(set_attr "type" "str") (set_attr "memory" "both") (set_attr "mode" "HI")]) (define_insn "*strmovhi_rex_1" [(set (mem:HI (match_operand:DI 2 "register_operand" "0")) (mem:HI (match_operand:DI 3 "register_operand" "1"))) (set (match_operand:DI 0 "register_operand" "=D") (plus:DI (match_dup 2) (const_int 2))) (set (match_operand:DI 1 "register_operand" "=S") (plus:DI (match_dup 3) (const_int 2))) (use (reg:SI 19))] "TARGET_64BIT && (TARGET_SINGLE_STRINGOP || optimize_size)" "movsw" [(set_attr "type" "str") (set_attr "memory" "both") (set_attr "mode" "HI")]) (define_insn "*strmovqi_1" [(set (mem:QI (match_operand:SI 2 "register_operand" "0")) (mem:QI (match_operand:SI 3 "register_operand" "1"))) (set (match_operand:SI 0 "register_operand" "=D") (plus:SI (match_dup 2) (const_int 1))) (set (match_operand:SI 1 "register_operand" "=S") (plus:SI (match_dup 3) (const_int 1))) (use (reg:SI 19))] "!TARGET_64BIT && (TARGET_SINGLE_STRINGOP || optimize_size)" "movsb" [(set_attr "type" "str") (set_attr "memory" "both") (set_attr "mode" "QI")]) (define_insn "*strmovqi_rex_1" [(set (mem:QI (match_operand:DI 2 "register_operand" "0")) (mem:QI (match_operand:DI 3 "register_operand" "1"))) (set (match_operand:DI 0 "register_operand" "=D") (plus:DI (match_dup 2) (const_int 1))) (set (match_operand:DI 1 "register_operand" "=S") (plus:DI (match_dup 3) (const_int 1))) (use (reg:SI 19))] "TARGET_64BIT && (TARGET_SINGLE_STRINGOP || optimize_size)" "movsb" [(set_attr "type" "str") (set_attr "memory" "both") (set_attr "mode" "QI")]) (define_expand "rep_mov" [(parallel [(set (match_operand 4 "register_operand" "") (const_int 0)) (set (match_operand 0 "register_operand" "") (match_operand 5 "" "")) (set (match_operand 2 "register_operand" "") (match_operand 6 "" "")) (set (match_operand 1 "memory_operand" "") (match_operand 3 "memory_operand" "")) (use (match_dup 4)) (use (reg:SI 19))])] "" "") (define_insn "*rep_movdi_rex64" [(set (match_operand:DI 2 "register_operand" "=c") (const_int 0)) (set (match_operand:DI 0 "register_operand" "=D") (plus:DI (ashift:DI (match_operand:DI 5 "register_operand" "2") (const_int 3)) (match_operand:DI 3 "register_operand" "0"))) (set (match_operand:DI 1 "register_operand" "=S") (plus:DI (ashift:DI (match_dup 5) (const_int 3)) (match_operand:DI 4 "register_operand" "1"))) (set (mem:BLK (match_dup 3)) (mem:BLK (match_dup 4))) (use (match_dup 5)) (use (reg:SI 19))] "TARGET_64BIT" "{rep\;movsq|rep movsq}" [(set_attr "type" "str") (set_attr "prefix_rep" "1") (set_attr "memory" "both") (set_attr "mode" "DI")]) (define_insn "*rep_movsi" [(set (match_operand:SI 2 "register_operand" "=c") (const_int 0)) (set (match_operand:SI 0 "register_operand" "=D") (plus:SI (ashift:SI (match_operand:SI 5 "register_operand" "2") (const_int 2)) (match_operand:SI 3 "register_operand" "0"))) (set (match_operand:SI 1 "register_operand" "=S") (plus:SI (ashift:SI (match_dup 5) (const_int 2)) (match_operand:SI 4 "register_operand" "1"))) (set (mem:BLK (match_dup 3)) (mem:BLK (match_dup 4))) (use (match_dup 5)) (use (reg:SI 19))] "!TARGET_64BIT" "{rep\;movsl|rep movsd}" [(set_attr "type" "str") (set_attr "prefix_rep" "1") (set_attr "memory" "both") (set_attr "mode" "SI")]) (define_insn "*rep_movsi_rex64" [(set (match_operand:DI 2 "register_operand" "=c") (const_int 0)) (set (match_operand:DI 0 "register_operand" "=D") (plus:DI (ashift:DI (match_operand:DI 5 "register_operand" "2") (const_int 2)) (match_operand:DI 3 "register_operand" "0"))) (set (match_operand:DI 1 "register_operand" "=S") (plus:DI (ashift:DI (match_dup 5) (const_int 2)) (match_operand:DI 4 "register_operand" "1"))) (set (mem:BLK (match_dup 3)) (mem:BLK (match_dup 4))) (use (match_dup 5)) (use (reg:SI 19))] "TARGET_64BIT" "{rep\;movsl|rep movsd}" [(set_attr "type" "str") (set_attr "prefix_rep" "1") (set_attr "memory" "both") (set_attr "mode" "SI")]) (define_insn "*rep_movqi" [(set (match_operand:SI 2 "register_operand" "=c") (const_int 0)) (set (match_operand:SI 0 "register_operand" "=D") (plus:SI (match_operand:SI 3 "register_operand" "0") (match_operand:SI 5 "register_operand" "2"))) (set (match_operand:SI 1 "register_operand" "=S") (plus:SI (match_operand:SI 4 "register_operand" "1") (match_dup 5))) (set (mem:BLK (match_dup 3)) (mem:BLK (match_dup 4))) (use (match_dup 5)) (use (reg:SI 19))] "!TARGET_64BIT" "{rep\;movsb|rep movsb}" [(set_attr "type" "str") (set_attr "prefix_rep" "1") (set_attr "memory" "both") (set_attr "mode" "SI")]) (define_insn "*rep_movqi_rex64" [(set (match_operand:DI 2 "register_operand" "=c") (const_int 0)) (set (match_operand:DI 0 "register_operand" "=D") (plus:DI (match_operand:DI 3 "register_operand" "0") (match_operand:DI 5 "register_operand" "2"))) (set (match_operand:DI 1 "register_operand" "=S") (plus:DI (match_operand:DI 4 "register_operand" "1") (match_dup 5))) (set (mem:BLK (match_dup 3)) (mem:BLK (match_dup 4))) (use (match_dup 5)) (use (reg:SI 19))] "TARGET_64BIT" "{rep\;movsb|rep movsb}" [(set_attr "type" "str") (set_attr "prefix_rep" "1") (set_attr "memory" "both") (set_attr "mode" "SI")]) (define_expand "clrstrsi" [(use (match_operand:BLK 0 "memory_operand" "")) (use (match_operand:SI 1 "nonmemory_operand" "")) (use (match_operand 2 "const_int_operand" ""))] "" { if (ix86_expand_clrstr (operands[0], operands[1], operands[2])) DONE; else FAIL; }) (define_expand "clrstrdi" [(use (match_operand:BLK 0 "memory_operand" "")) (use (match_operand:DI 1 "nonmemory_operand" "")) (use (match_operand 2 "const_int_operand" ""))] "TARGET_64BIT" { if (ix86_expand_clrstr (operands[0], operands[1], operands[2])) DONE; else FAIL; }) ;; Most CPUs don't like single string operations ;; Handle this case here to simplify previous expander. (define_expand "strset" [(set (match_operand 1 "memory_operand" "") (match_operand 2 "register_operand" "")) (parallel [(set (match_operand 0 "register_operand" "") (match_dup 3)) (clobber (reg:CC 17))])] "" { if (GET_MODE (operands[1]) != GET_MODE (operands[2])) operands[1] = adjust_address_nv (operands[1], GET_MODE (operands[2]), 0); /* If .md ever supports :P for Pmode, this can be directly in the pattern above. */ operands[3] = gen_rtx_PLUS (Pmode, operands[0], GEN_INT (GET_MODE_SIZE (GET_MODE (operands[2])))); if (TARGET_SINGLE_STRINGOP || optimize_size) { emit_insn (gen_strset_singleop (operands[0], operands[1], operands[2], operands[3])); DONE; } }) (define_expand "strset_singleop" [(parallel [(set (match_operand 1 "memory_operand" "") (match_operand 2 "register_operand" "")) (set (match_operand 0 "register_operand" "") (match_operand 3 "" "")) (use (reg:SI 19))])] "TARGET_SINGLE_STRINGOP || optimize_size" "") (define_insn "*strsetdi_rex_1" [(set (mem:SI (match_operand:DI 1 "register_operand" "0")) (match_operand:SI 2 "register_operand" "a")) (set (match_operand:DI 0 "register_operand" "=D") (plus:DI (match_dup 1) (const_int 8))) (use (reg:SI 19))] "TARGET_64BIT && (TARGET_SINGLE_STRINGOP || optimize_size)" "stosq" [(set_attr "type" "str") (set_attr "memory" "store") (set_attr "mode" "DI")]) (define_insn "*strsetsi_1" [(set (mem:SI (match_operand:SI 1 "register_operand" "0")) (match_operand:SI 2 "register_operand" "a")) (set (match_operand:SI 0 "register_operand" "=D") (plus:SI (match_dup 1) (const_int 4))) (use (reg:SI 19))] "!TARGET_64BIT && (TARGET_SINGLE_STRINGOP || optimize_size)" "{stosl|stosd}" [(set_attr "type" "str") (set_attr "memory" "store") (set_attr "mode" "SI")]) (define_insn "*strsetsi_rex_1" [(set (mem:SI (match_operand:DI 1 "register_operand" "0")) (match_operand:SI 2 "register_operand" "a")) (set (match_operand:DI 0 "register_operand" "=D") (plus:DI (match_dup 1) (const_int 4))) (use (reg:SI 19))] "TARGET_64BIT && (TARGET_SINGLE_STRINGOP || optimize_size)" "{stosl|stosd}" [(set_attr "type" "str") (set_attr "memory" "store") (set_attr "mode" "SI")]) (define_insn "*strsethi_1" [(set (mem:HI (match_operand:SI 1 "register_operand" "0")) (match_operand:HI 2 "register_operand" "a")) (set (match_operand:SI 0 "register_operand" "=D") (plus:SI (match_dup 1) (const_int 2))) (use (reg:SI 19))] "!TARGET_64BIT && (TARGET_SINGLE_STRINGOP || optimize_size)" "stosw" [(set_attr "type" "str") (set_attr "memory" "store") (set_attr "mode" "HI")]) (define_insn "*strsethi_rex_1" [(set (mem:HI (match_operand:DI 1 "register_operand" "0")) (match_operand:HI 2 "register_operand" "a")) (set (match_operand:DI 0 "register_operand" "=D") (plus:DI (match_dup 1) (const_int 2))) (use (reg:SI 19))] "TARGET_64BIT && (TARGET_SINGLE_STRINGOP || optimize_size)" "stosw" [(set_attr "type" "str") (set_attr "memory" "store") (set_attr "mode" "HI")]) (define_insn "*strsetqi_1" [(set (mem:QI (match_operand:SI 1 "register_operand" "0")) (match_operand:QI 2 "register_operand" "a")) (set (match_operand:SI 0 "register_operand" "=D") (plus:SI (match_dup 1) (const_int 1))) (use (reg:SI 19))] "!TARGET_64BIT && (TARGET_SINGLE_STRINGOP || optimize_size)" "stosb" [(set_attr "type" "str") (set_attr "memory" "store") (set_attr "mode" "QI")]) (define_insn "*strsetqi_rex_1" [(set (mem:QI (match_operand:DI 1 "register_operand" "0")) (match_operand:QI 2 "register_operand" "a")) (set (match_operand:DI 0 "register_operand" "=D") (plus:DI (match_dup 1) (const_int 1))) (use (reg:SI 19))] "TARGET_64BIT && (TARGET_SINGLE_STRINGOP || optimize_size)" "stosb" [(set_attr "type" "str") (set_attr "memory" "store") (set_attr "mode" "QI")]) (define_expand "rep_stos" [(parallel [(set (match_operand 1 "register_operand" "") (const_int 0)) (set (match_operand 0 "register_operand" "") (match_operand 4 "" "")) (set (match_operand 2 "memory_operand" "") (const_int 0)) (use (match_operand 3 "register_operand" "")) (use (match_dup 1)) (use (reg:SI 19))])] "" "") (define_insn "*rep_stosdi_rex64" [(set (match_operand:DI 1 "register_operand" "=c") (const_int 0)) (set (match_operand:DI 0 "register_operand" "=D") (plus:DI (ashift:DI (match_operand:DI 4 "register_operand" "1") (const_int 3)) (match_operand:DI 3 "register_operand" "0"))) (set (mem:BLK (match_dup 3)) (const_int 0)) (use (match_operand:DI 2 "register_operand" "a")) (use (match_dup 4)) (use (reg:SI 19))] "TARGET_64BIT" "{rep\;stosq|rep stosq}" [(set_attr "type" "str") (set_attr "prefix_rep" "1") (set_attr "memory" "store") (set_attr "mode" "DI")]) (define_insn "*rep_stossi" [(set (match_operand:SI 1 "register_operand" "=c") (const_int 0)) (set (match_operand:SI 0 "register_operand" "=D") (plus:SI (ashift:SI (match_operand:SI 4 "register_operand" "1") (const_int 2)) (match_operand:SI 3 "register_operand" "0"))) (set (mem:BLK (match_dup 3)) (const_int 0)) (use (match_operand:SI 2 "register_operand" "a")) (use (match_dup 4)) (use (reg:SI 19))] "!TARGET_64BIT" "{rep\;stosl|rep stosd}" [(set_attr "type" "str") (set_attr "prefix_rep" "1") (set_attr "memory" "store") (set_attr "mode" "SI")]) (define_insn "*rep_stossi_rex64" [(set (match_operand:DI 1 "register_operand" "=c") (const_int 0)) (set (match_operand:DI 0 "register_operand" "=D") (plus:DI (ashift:DI (match_operand:DI 4 "register_operand" "1") (const_int 2)) (match_operand:DI 3 "register_operand" "0"))) (set (mem:BLK (match_dup 3)) (const_int 0)) (use (match_operand:SI 2 "register_operand" "a")) (use (match_dup 4)) (use (reg:SI 19))] "TARGET_64BIT" "{rep\;stosl|rep stosd}" [(set_attr "type" "str") (set_attr "prefix_rep" "1") (set_attr "memory" "store") (set_attr "mode" "SI")]) (define_insn "*rep_stosqi" [(set (match_operand:SI 1 "register_operand" "=c") (const_int 0)) (set (match_operand:SI 0 "register_operand" "=D") (plus:SI (match_operand:SI 3 "register_operand" "0") (match_operand:SI 4 "register_operand" "1"))) (set (mem:BLK (match_dup 3)) (const_int 0)) (use (match_operand:QI 2 "register_operand" "a")) (use (match_dup 4)) (use (reg:SI 19))] "!TARGET_64BIT" "{rep\;stosb|rep stosb}" [(set_attr "type" "str") (set_attr "prefix_rep" "1") (set_attr "memory" "store") (set_attr "mode" "QI")]) (define_insn "*rep_stosqi_rex64" [(set (match_operand:DI 1 "register_operand" "=c") (const_int 0)) (set (match_operand:DI 0 "register_operand" "=D") (plus:DI (match_operand:DI 3 "register_operand" "0") (match_operand:DI 4 "register_operand" "1"))) (set (mem:BLK (match_dup 3)) (const_int 0)) (use (match_operand:QI 2 "register_operand" "a")) (use (match_dup 4)) (use (reg:SI 19))] "TARGET_64BIT" "{rep\;stosb|rep stosb}" [(set_attr "type" "str") (set_attr "prefix_rep" "1") (set_attr "memory" "store") (set_attr "mode" "QI")]) (define_expand "cmpstrsi" [(set (match_operand:SI 0 "register_operand" "") (compare:SI (match_operand:BLK 1 "general_operand" "") (match_operand:BLK 2 "general_operand" ""))) (use (match_operand 3 "general_operand" "")) (use (match_operand 4 "immediate_operand" ""))] "! optimize_size || TARGET_INLINE_ALL_STRINGOPS" { rtx addr1, addr2, out, outlow, count, countreg, align; /* Can't use this if the user has appropriated esi or edi. */ if (global_regs[4] || global_regs[5]) FAIL; out = operands[0]; if (GET_CODE (out) != REG) out = gen_reg_rtx (SImode); addr1 = copy_to_mode_reg (Pmode, XEXP (operands[1], 0)); addr2 = copy_to_mode_reg (Pmode, XEXP (operands[2], 0)); if (addr1 != XEXP (operands[1], 0)) operands[1] = replace_equiv_address_nv (operands[1], addr1); if (addr2 != XEXP (operands[2], 0)) operands[2] = replace_equiv_address_nv (operands[2], addr2); count = operands[3]; countreg = ix86_zero_extend_to_Pmode (count); /* %%% Iff we are testing strict equality, we can use known alignment to good advantage. This may be possible with combine, particularly once cc0 is dead. */ align = operands[4]; emit_insn (gen_cld ()); if (GET_CODE (count) == CONST_INT) { if (INTVAL (count) == 0) { emit_move_insn (operands[0], const0_rtx); DONE; } emit_insn (gen_cmpstrqi_nz_1 (addr1, addr2, countreg, align, operands[1], operands[2])); } else { if (TARGET_64BIT) emit_insn (gen_cmpdi_1_rex64 (countreg, countreg)); else emit_insn (gen_cmpsi_1 (countreg, countreg)); emit_insn (gen_cmpstrqi_1 (addr1, addr2, countreg, align, operands[1], operands[2])); } outlow = gen_lowpart (QImode, out); emit_insn (gen_cmpintqi (outlow)); emit_move_insn (out, gen_rtx_SIGN_EXTEND (SImode, outlow)); if (operands[0] != out) emit_move_insn (operands[0], out); DONE; }) ;; Produce a tri-state integer (-1, 0, 1) from condition codes. (define_expand "cmpintqi" [(set (match_dup 1) (gtu:QI (reg:CC 17) (const_int 0))) (set (match_dup 2) (ltu:QI (reg:CC 17) (const_int 0))) (parallel [(set (match_operand:QI 0 "register_operand" "") (minus:QI (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))])] "" "operands[1] = gen_reg_rtx (QImode); operands[2] = gen_reg_rtx (QImode);") ;; memcmp recognizers. The `cmpsb' opcode does nothing if the count is ;; zero. Emit extra code to make sure that a zero-length compare is EQ. (define_expand "cmpstrqi_nz_1" [(parallel [(set (reg:CC 17) (compare:CC (match_operand 4 "memory_operand" "") (match_operand 5 "memory_operand" ""))) (use (match_operand 2 "register_operand" "")) (use (match_operand:SI 3 "immediate_operand" "")) (use (reg:SI 19)) (clobber (match_operand 0 "register_operand" "")) (clobber (match_operand 1 "register_operand" "")) (clobber (match_dup 2))])] "" "") (define_insn "*cmpstrqi_nz_1" [(set (reg:CC 17) (compare:CC (mem:BLK (match_operand:SI 4 "register_operand" "0")) (mem:BLK (match_operand:SI 5 "register_operand" "1")))) (use (match_operand:SI 6 "register_operand" "2")) (use (match_operand:SI 3 "immediate_operand" "i")) (use (reg:SI 19)) (clobber (match_operand:SI 0 "register_operand" "=S")) (clobber (match_operand:SI 1 "register_operand" "=D")) (clobber (match_operand:SI 2 "register_operand" "=c"))] "!TARGET_64BIT" "repz{\;| }cmpsb" [(set_attr "type" "str") (set_attr "mode" "QI") (set_attr "prefix_rep" "1")]) (define_insn "*cmpstrqi_nz_rex_1" [(set (reg:CC 17) (compare:CC (mem:BLK (match_operand:DI 4 "register_operand" "0")) (mem:BLK (match_operand:DI 5 "register_operand" "1")))) (use (match_operand:DI 6 "register_operand" "2")) (use (match_operand:SI 3 "immediate_operand" "i")) (use (reg:SI 19)) (clobber (match_operand:DI 0 "register_operand" "=S")) (clobber (match_operand:DI 1 "register_operand" "=D")) (clobber (match_operand:DI 2 "register_operand" "=c"))] "TARGET_64BIT" "repz{\;| }cmpsb" [(set_attr "type" "str") (set_attr "mode" "QI") (set_attr "prefix_rep" "1")]) ;; The same, but the count is not known to not be zero. (define_expand "cmpstrqi_1" [(parallel [(set (reg:CC 17) (if_then_else:CC (ne (match_operand 2 "register_operand" "") (const_int 0)) (compare:CC (match_operand 4 "memory_operand" "") (match_operand 5 "memory_operand" "")) (const_int 0))) (use (match_operand:SI 3 "immediate_operand" "")) (use (reg:CC 17)) (use (reg:SI 19)) (clobber (match_operand 0 "register_operand" "")) (clobber (match_operand 1 "register_operand" "")) (clobber (match_dup 2))])] "" "") (define_insn "*cmpstrqi_1" [(set (reg:CC 17) (if_then_else:CC (ne (match_operand:SI 6 "register_operand" "2") (const_int 0)) (compare:CC (mem:BLK (match_operand:SI 4 "register_operand" "0")) (mem:BLK (match_operand:SI 5 "register_operand" "1"))) (const_int 0))) (use (match_operand:SI 3 "immediate_operand" "i")) (use (reg:CC 17)) (use (reg:SI 19)) (clobber (match_operand:SI 0 "register_operand" "=S")) (clobber (match_operand:SI 1 "register_operand" "=D")) (clobber (match_operand:SI 2 "register_operand" "=c"))] "!TARGET_64BIT" "repz{\;| }cmpsb" [(set_attr "type" "str") (set_attr "mode" "QI") (set_attr "prefix_rep" "1")]) (define_insn "*cmpstrqi_rex_1" [(set (reg:CC 17) (if_then_else:CC (ne (match_operand:DI 6 "register_operand" "2") (const_int 0)) (compare:CC (mem:BLK (match_operand:DI 4 "register_operand" "0")) (mem:BLK (match_operand:DI 5 "register_operand" "1"))) (const_int 0))) (use (match_operand:SI 3 "immediate_operand" "i")) (use (reg:CC 17)) (use (reg:SI 19)) (clobber (match_operand:DI 0 "register_operand" "=S")) (clobber (match_operand:DI 1 "register_operand" "=D")) (clobber (match_operand:DI 2 "register_operand" "=c"))] "TARGET_64BIT" "repz{\;| }cmpsb" [(set_attr "type" "str") (set_attr "mode" "QI") (set_attr "prefix_rep" "1")]) (define_expand "strlensi" [(set (match_operand:SI 0 "register_operand" "") (unspec:SI [(match_operand:BLK 1 "general_operand" "") (match_operand:QI 2 "immediate_operand" "") (match_operand 3 "immediate_operand" "")] UNSPEC_SCAS))] "" { if (ix86_expand_strlen (operands[0], operands[1], operands[2], operands[3])) DONE; else FAIL; }) (define_expand "strlendi" [(set (match_operand:DI 0 "register_operand" "") (unspec:DI [(match_operand:BLK 1 "general_operand" "") (match_operand:QI 2 "immediate_operand" "") (match_operand 3 "immediate_operand" "")] UNSPEC_SCAS))] "" { if (ix86_expand_strlen (operands[0], operands[1], operands[2], operands[3])) DONE; else FAIL; }) (define_expand "strlenqi_1" [(parallel [(set (match_operand 0 "register_operand" "") (match_operand 2 "" "")) (use (reg:SI 19)) (clobber (match_operand 1 "register_operand" "")) (clobber (reg:CC 17))])] "" "") (define_insn "*strlenqi_1" [(set (match_operand:SI 0 "register_operand" "=&c") (unspec:SI [(mem:BLK (match_operand:SI 5 "register_operand" "1")) (match_operand:QI 2 "register_operand" "a") (match_operand:SI 3 "immediate_operand" "i") (match_operand:SI 4 "register_operand" "0")] UNSPEC_SCAS)) (use (reg:SI 19)) (clobber (match_operand:SI 1 "register_operand" "=D")) (clobber (reg:CC 17))] "!TARGET_64BIT" "repnz{\;| }scasb" [(set_attr "type" "str") (set_attr "mode" "QI") (set_attr "prefix_rep" "1")]) (define_insn "*strlenqi_rex_1" [(set (match_operand:DI 0 "register_operand" "=&c") (unspec:DI [(mem:BLK (match_operand:DI 5 "register_operand" "1")) (match_operand:QI 2 "register_operand" "a") (match_operand:DI 3 "immediate_operand" "i") (match_operand:DI 4 "register_operand" "0")] UNSPEC_SCAS)) (use (reg:SI 19)) (clobber (match_operand:DI 1 "register_operand" "=D")) (clobber (reg:CC 17))] "TARGET_64BIT" "repnz{\;| }scasb" [(set_attr "type" "str") (set_attr "mode" "QI") (set_attr "prefix_rep" "1")]) ;; Peephole optimizations to clean up after cmpstr*. This should be ;; handled in combine, but it is not currently up to the task. ;; When used for their truth value, the cmpstr* expanders generate ;; code like this: ;; ;; repz cmpsb ;; seta %al ;; setb %dl ;; cmpb %al, %dl ;; jcc label ;; ;; The intermediate three instructions are unnecessary. ;; This one handles cmpstr*_nz_1... (define_peephole2 [(parallel[ (set (reg:CC 17) (compare:CC (mem:BLK (match_operand 4 "register_operand" "")) (mem:BLK (match_operand 5 "register_operand" "")))) (use (match_operand 6 "register_operand" "")) (use (match_operand:SI 3 "immediate_operand" "")) (use (reg:SI 19)) (clobber (match_operand 0 "register_operand" "")) (clobber (match_operand 1 "register_operand" "")) (clobber (match_operand 2 "register_operand" ""))]) (set (match_operand:QI 7 "register_operand" "") (gtu:QI (reg:CC 17) (const_int 0))) (set (match_operand:QI 8 "register_operand" "") (ltu:QI (reg:CC 17) (const_int 0))) (set (reg 17) (compare (match_dup 7) (match_dup 8))) ] "peep2_reg_dead_p (4, operands[7]) && peep2_reg_dead_p (4, operands[8])" [(parallel[ (set (reg:CC 17) (compare:CC (mem:BLK (match_dup 4)) (mem:BLK (match_dup 5)))) (use (match_dup 6)) (use (match_dup 3)) (use (reg:SI 19)) (clobber (match_dup 0)) (clobber (match_dup 1)) (clobber (match_dup 2))])] "") ;; ...and this one handles cmpstr*_1. (define_peephole2 [(parallel[ (set (reg:CC 17) (if_then_else:CC (ne (match_operand 6 "register_operand" "") (const_int 0)) (compare:CC (mem:BLK (match_operand 4 "register_operand" "")) (mem:BLK (match_operand 5 "register_operand" ""))) (const_int 0))) (use (match_operand:SI 3 "immediate_operand" "")) (use (reg:CC 17)) (use (reg:SI 19)) (clobber (match_operand 0 "register_operand" "")) (clobber (match_operand 1 "register_operand" "")) (clobber (match_operand 2 "register_operand" ""))]) (set (match_operand:QI 7 "register_operand" "") (gtu:QI (reg:CC 17) (const_int 0))) (set (match_operand:QI 8 "register_operand" "") (ltu:QI (reg:CC 17) (const_int 0))) (set (reg 17) (compare (match_dup 7) (match_dup 8))) ] "peep2_reg_dead_p (4, operands[7]) && peep2_reg_dead_p (4, operands[8])" [(parallel[ (set (reg:CC 17) (if_then_else:CC (ne (match_dup 6) (const_int 0)) (compare:CC (mem:BLK (match_dup 4)) (mem:BLK (match_dup 5))) (const_int 0))) (use (match_dup 3)) (use (reg:CC 17)) (use (reg:SI 19)) (clobber (match_dup 0)) (clobber (match_dup 1)) (clobber (match_dup 2))])] "") ;; Conditional move instructions. (define_expand "movdicc" [(set (match_operand:DI 0 "register_operand" "") (if_then_else:DI (match_operand 1 "comparison_operator" "") (match_operand:DI 2 "general_operand" "") (match_operand:DI 3 "general_operand" "")))] "TARGET_64BIT" "if (!ix86_expand_int_movcc (operands)) FAIL; DONE;") (define_insn "x86_movdicc_0_m1_rex64" [(set (match_operand:DI 0 "register_operand" "=r") (if_then_else:DI (match_operand 1 "ix86_carry_flag_operator" "") (const_int -1) (const_int 0))) (clobber (reg:CC 17))] "TARGET_64BIT" "sbb{q}\t%0, %0" ; Since we don't have the proper number of operands for an alu insn, ; fill in all the blanks. [(set_attr "type" "alu") (set_attr "pent_pair" "pu") (set_attr "memory" "none") (set_attr "imm_disp" "false") (set_attr "mode" "DI") (set_attr "length_immediate" "0")]) (define_insn "movdicc_c_rex64" [(set (match_operand:DI 0 "register_operand" "=r,r") (if_then_else:DI (match_operator 1 "ix86_comparison_operator" [(reg 17) (const_int 0)]) (match_operand:DI 2 "nonimmediate_operand" "rm,0") (match_operand:DI 3 "nonimmediate_operand" "0,rm")))] "TARGET_64BIT && TARGET_CMOVE && (GET_CODE (operands[2]) != MEM || GET_CODE (operands[3]) != MEM)" "@ cmov%O2%C1\t{%2, %0|%0, %2} cmov%O2%c1\t{%3, %0|%0, %3}" [(set_attr "type" "icmov") (set_attr "mode" "DI")]) (define_expand "movsicc" [(set (match_operand:SI 0 "register_operand" "") (if_then_else:SI (match_operand 1 "comparison_operator" "") (match_operand:SI 2 "general_operand" "") (match_operand:SI 3 "general_operand" "")))] "" "if (!ix86_expand_int_movcc (operands)) FAIL; DONE;") ;; Data flow gets confused by our desire for `sbbl reg,reg', and clearing ;; the register first winds up with `sbbl $0,reg', which is also weird. ;; So just document what we're doing explicitly. (define_insn "x86_movsicc_0_m1" [(set (match_operand:SI 0 "register_operand" "=r") (if_then_else:SI (match_operand 1 "ix86_carry_flag_operator" "") (const_int -1) (const_int 0))) (clobber (reg:CC 17))] "" "sbb{l}\t%0, %0" ; Since we don't have the proper number of operands for an alu insn, ; fill in all the blanks. [(set_attr "type" "alu") (set_attr "pent_pair" "pu") (set_attr "memory" "none") (set_attr "imm_disp" "false") (set_attr "mode" "SI") (set_attr "length_immediate" "0")]) (define_insn "*movsicc_noc" [(set (match_operand:SI 0 "register_operand" "=r,r") (if_then_else:SI (match_operator 1 "ix86_comparison_operator" [(reg 17) (const_int 0)]) (match_operand:SI 2 "nonimmediate_operand" "rm,0") (match_operand:SI 3 "nonimmediate_operand" "0,rm")))] "TARGET_CMOVE && (GET_CODE (operands[2]) != MEM || GET_CODE (operands[3]) != MEM)" "@ cmov%O2%C1\t{%2, %0|%0, %2} cmov%O2%c1\t{%3, %0|%0, %3}" [(set_attr "type" "icmov") (set_attr "mode" "SI")]) (define_expand "movhicc" [(set (match_operand:HI 0 "register_operand" "") (if_then_else:HI (match_operand 1 "comparison_operator" "") (match_operand:HI 2 "general_operand" "") (match_operand:HI 3 "general_operand" "")))] "TARGET_HIMODE_MATH" "if (!ix86_expand_int_movcc (operands)) FAIL; DONE;") (define_insn "*movhicc_noc" [(set (match_operand:HI 0 "register_operand" "=r,r") (if_then_else:HI (match_operator 1 "ix86_comparison_operator" [(reg 17) (const_int 0)]) (match_operand:HI 2 "nonimmediate_operand" "rm,0") (match_operand:HI 3 "nonimmediate_operand" "0,rm")))] "TARGET_CMOVE && (GET_CODE (operands[2]) != MEM || GET_CODE (operands[3]) != MEM)" "@ cmov%O2%C1\t{%2, %0|%0, %2} cmov%O2%c1\t{%3, %0|%0, %3}" [(set_attr "type" "icmov") (set_attr "mode" "HI")]) (define_expand "movqicc" [(set (match_operand:QI 0 "register_operand" "") (if_then_else:QI (match_operand 1 "comparison_operator" "") (match_operand:QI 2 "general_operand" "") (match_operand:QI 3 "general_operand" "")))] "TARGET_QIMODE_MATH" "if (!ix86_expand_int_movcc (operands)) FAIL; DONE;") (define_insn_and_split "*movqicc_noc" [(set (match_operand:QI 0 "register_operand" "=r,r") (if_then_else:QI (match_operator 1 "ix86_comparison_operator" [(match_operand 4 "flags_reg_operand" "") (const_int 0)]) (match_operand:QI 2 "register_operand" "r,0") (match_operand:QI 3 "register_operand" "0,r")))] "TARGET_CMOVE && !TARGET_PARTIAL_REG_STALL" "#" "&& reload_completed" [(set (match_dup 0) (if_then_else:SI (match_op_dup 1 [(match_dup 4) (const_int 0)]) (match_dup 2) (match_dup 3)))] "operands[0] = gen_lowpart (SImode, operands[0]); operands[2] = gen_lowpart (SImode, operands[2]); operands[3] = gen_lowpart (SImode, operands[3]);" [(set_attr "type" "icmov") (set_attr "mode" "SI")]) (define_expand "movsfcc" [(set (match_operand:SF 0 "register_operand" "") (if_then_else:SF (match_operand 1 "comparison_operator" "") (match_operand:SF 2 "register_operand" "") (match_operand:SF 3 "register_operand" "")))] "TARGET_CMOVE" "if (! ix86_expand_fp_movcc (operands)) FAIL; DONE;") (define_insn "*movsfcc_1" [(set (match_operand:SF 0 "register_operand" "=f#r,f#r,r#f,r#f") (if_then_else:SF (match_operator 1 "fcmov_comparison_operator" [(reg 17) (const_int 0)]) (match_operand:SF 2 "nonimmediate_operand" "f#r,0,rm#f,0") (match_operand:SF 3 "nonimmediate_operand" "0,f#r,0,rm#f")))] "TARGET_CMOVE && (GET_CODE (operands[2]) != MEM || GET_CODE (operands[3]) != MEM)" "@ fcmov%F1\t{%2, %0|%0, %2} fcmov%f1\t{%3, %0|%0, %3} cmov%O2%C1\t{%2, %0|%0, %2} cmov%O2%c1\t{%3, %0|%0, %3}" [(set_attr "type" "fcmov,fcmov,icmov,icmov") (set_attr "mode" "SF,SF,SI,SI")]) (define_expand "movdfcc" [(set (match_operand:DF 0 "register_operand" "") (if_then_else:DF (match_operand 1 "comparison_operator" "") (match_operand:DF 2 "register_operand" "") (match_operand:DF 3 "register_operand" "")))] "TARGET_CMOVE" "if (! ix86_expand_fp_movcc (operands)) FAIL; DONE;") (define_insn "*movdfcc_1" [(set (match_operand:DF 0 "register_operand" "=f#r,f#r,&r#f,&r#f") (if_then_else:DF (match_operator 1 "fcmov_comparison_operator" [(reg 17) (const_int 0)]) (match_operand:DF 2 "nonimmediate_operand" "f#r,0,rm#f,0") (match_operand:DF 3 "nonimmediate_operand" "0,f#r,0,rm#f")))] "!TARGET_64BIT && TARGET_CMOVE && (GET_CODE (operands[2]) != MEM || GET_CODE (operands[3]) != MEM)" "@ fcmov%F1\t{%2, %0|%0, %2} fcmov%f1\t{%3, %0|%0, %3} # #" [(set_attr "type" "fcmov,fcmov,multi,multi") (set_attr "mode" "DF")]) (define_insn "*movdfcc_1_rex64" [(set (match_operand:DF 0 "register_operand" "=f#r,f#r,r#f,r#f") (if_then_else:DF (match_operator 1 "fcmov_comparison_operator" [(reg 17) (const_int 0)]) (match_operand:DF 2 "nonimmediate_operand" "f#r,0#r,rm#f,0#f") (match_operand:DF 3 "nonimmediate_operand" "0#r,f#r,0#f,rm#f")))] "TARGET_64BIT && TARGET_CMOVE && (GET_CODE (operands[2]) != MEM || GET_CODE (operands[3]) != MEM)" "@ fcmov%F1\t{%2, %0|%0, %2} fcmov%f1\t{%3, %0|%0, %3} cmov%O2%C1\t{%2, %0|%0, %2} cmov%O2%c1\t{%3, %0|%0, %3}" [(set_attr "type" "fcmov,fcmov,icmov,icmov") (set_attr "mode" "DF")]) (define_split [(set (match_operand:DF 0 "register_and_not_any_fp_reg_operand" "") (if_then_else:DF (match_operator 1 "fcmov_comparison_operator" [(match_operand 4 "flags_reg_operand" "") (const_int 0)]) (match_operand:DF 2 "nonimmediate_operand" "") (match_operand:DF 3 "nonimmediate_operand" "")))] "!TARGET_64BIT && reload_completed" [(set (match_dup 2) (if_then_else:SI (match_op_dup 1 [(match_dup 4) (const_int 0)]) (match_dup 5) (match_dup 7))) (set (match_dup 3) (if_then_else:SI (match_op_dup 1 [(match_dup 4) (const_int 0)]) (match_dup 6) (match_dup 8)))] "split_di (operands+2, 1, operands+5, operands+6); split_di (operands+3, 1, operands+7, operands+8); split_di (operands, 1, operands+2, operands+3);") (define_expand "movxfcc" [(set (match_operand:XF 0 "register_operand" "") (if_then_else:XF (match_operand 1 "comparison_operator" "") (match_operand:XF 2 "register_operand" "") (match_operand:XF 3 "register_operand" "")))] "TARGET_CMOVE" "if (! ix86_expand_fp_movcc (operands)) FAIL; DONE;") (define_insn "*movxfcc_1" [(set (match_operand:XF 0 "register_operand" "=f,f") (if_then_else:XF (match_operator 1 "fcmov_comparison_operator" [(reg 17) (const_int 0)]) (match_operand:XF 2 "register_operand" "f,0") (match_operand:XF 3 "register_operand" "0,f")))] "TARGET_CMOVE" "@ fcmov%F1\t{%2, %0|%0, %2} fcmov%f1\t{%3, %0|%0, %3}" [(set_attr "type" "fcmov") (set_attr "mode" "XF")]) (define_expand "minsf3" [(parallel [ (set (match_operand:SF 0 "register_operand" "") (if_then_else:SF (lt (match_operand:SF 1 "register_operand" "") (match_operand:SF 2 "nonimmediate_operand" "")) (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))])] "TARGET_SSE" "") (define_insn "*minsf" [(set (match_operand:SF 0 "register_operand" "=x#f,f#x,f#x") (if_then_else:SF (lt (match_operand:SF 1 "register_operand" "0,0,f#x") (match_operand:SF 2 "nonimmediate_operand" "xm#f,f#x,0")) (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))] "TARGET_SSE && TARGET_IEEE_FP" "#") (define_insn "*minsf_nonieee" [(set (match_operand:SF 0 "register_operand" "=x#f,f#x") (if_then_else:SF (lt (match_operand:SF 1 "nonimmediate_operand" "%0,0") (match_operand:SF 2 "nonimmediate_operand" "xm#f,f#x")) (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))] "TARGET_SSE && !TARGET_IEEE_FP && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "#") (define_split [(set (match_operand:SF 0 "register_operand" "") (if_then_else:SF (lt (match_operand:SF 1 "register_operand" "") (match_operand:SF 2 "nonimmediate_operand" "")) (match_operand:SF 3 "register_operand" "") (match_operand:SF 4 "nonimmediate_operand" ""))) (clobber (reg:CC 17))] "SSE_REG_P (operands[0]) && reload_completed && ((operands_match_p (operands[1], operands[3]) && operands_match_p (operands[2], operands[4])) || (operands_match_p (operands[1], operands[4]) && operands_match_p (operands[2], operands[3])))" [(set (match_dup 0) (if_then_else:SF (lt (match_dup 1) (match_dup 2)) (match_dup 1) (match_dup 2)))]) ;; Conditional addition patterns (define_expand "addqicc" [(match_operand:QI 0 "register_operand" "") (match_operand 1 "comparison_operator" "") (match_operand:QI 2 "register_operand" "") (match_operand:QI 3 "const_int_operand" "")] "" "if (!ix86_expand_int_addcc (operands)) FAIL; DONE;") (define_expand "addhicc" [(match_operand:HI 0 "register_operand" "") (match_operand 1 "comparison_operator" "") (match_operand:HI 2 "register_operand" "") (match_operand:HI 3 "const_int_operand" "")] "" "if (!ix86_expand_int_addcc (operands)) FAIL; DONE;") (define_expand "addsicc" [(match_operand:SI 0 "register_operand" "") (match_operand 1 "comparison_operator" "") (match_operand:SI 2 "register_operand" "") (match_operand:SI 3 "const_int_operand" "")] "" "if (!ix86_expand_int_addcc (operands)) FAIL; DONE;") (define_expand "adddicc" [(match_operand:DI 0 "register_operand" "") (match_operand 1 "comparison_operator" "") (match_operand:DI 2 "register_operand" "") (match_operand:DI 3 "const_int_operand" "")] "TARGET_64BIT" "if (!ix86_expand_int_addcc (operands)) FAIL; DONE;") ;; We can't represent the LT test directly. Do this by swapping the operands. (define_split [(set (match_operand:SF 0 "fp_register_operand" "") (if_then_else:SF (lt (match_operand:SF 1 "register_operand" "") (match_operand:SF 2 "register_operand" "")) (match_operand:SF 3 "register_operand" "") (match_operand:SF 4 "register_operand" ""))) (clobber (reg:CC 17))] "reload_completed && ((operands_match_p (operands[1], operands[3]) && operands_match_p (operands[2], operands[4])) || (operands_match_p (operands[1], operands[4]) && operands_match_p (operands[2], operands[3])))" [(set (reg:CCFP 17) (compare:CCFP (match_dup 2) (match_dup 1))) (set (match_dup 0) (if_then_else:SF (ge (reg:CCFP 17) (const_int 0)) (match_dup 1) (match_dup 2)))]) (define_insn "*minsf_sse" [(set (match_operand:SF 0 "register_operand" "=x") (if_then_else:SF (lt (match_operand:SF 1 "register_operand" "0") (match_operand:SF 2 "nonimmediate_operand" "xm")) (match_dup 1) (match_dup 2)))] "TARGET_SSE && reload_completed" "minss\t{%2, %0|%0, %2}" [(set_attr "type" "sse") (set_attr "mode" "SF")]) (define_expand "mindf3" [(parallel [ (set (match_operand:DF 0 "register_operand" "") (if_then_else:DF (lt (match_operand:DF 1 "register_operand" "") (match_operand:DF 2 "nonimmediate_operand" "")) (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))])] "TARGET_SSE2 && TARGET_SSE_MATH" "#") (define_insn "*mindf" [(set (match_operand:DF 0 "register_operand" "=Y#f,f#Y,f#Y") (if_then_else:DF (lt (match_operand:DF 1 "register_operand" "0,0,f#Y") (match_operand:DF 2 "nonimmediate_operand" "Ym#f,f#Y,0")) (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))] "TARGET_SSE2 && TARGET_IEEE_FP && TARGET_SSE_MATH" "#") (define_insn "*mindf_nonieee" [(set (match_operand:DF 0 "register_operand" "=Y#f,f#Y") (if_then_else:DF (lt (match_operand:DF 1 "nonimmediate_operand" "%0,0") (match_operand:DF 2 "nonimmediate_operand" "Ym#f,f#Y")) (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))] "TARGET_SSE2 && TARGET_SSE_MATH && !TARGET_IEEE_FP && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "#") (define_split [(set (match_operand:DF 0 "register_operand" "") (if_then_else:DF (lt (match_operand:DF 1 "register_operand" "") (match_operand:DF 2 "nonimmediate_operand" "")) (match_operand:DF 3 "register_operand" "") (match_operand:DF 4 "nonimmediate_operand" ""))) (clobber (reg:CC 17))] "SSE_REG_P (operands[0]) && reload_completed && ((operands_match_p (operands[1], operands[3]) && operands_match_p (operands[2], operands[4])) || (operands_match_p (operands[1], operands[4]) && operands_match_p (operands[2], operands[3])))" [(set (match_dup 0) (if_then_else:DF (lt (match_dup 1) (match_dup 2)) (match_dup 1) (match_dup 2)))]) ;; We can't represent the LT test directly. Do this by swapping the operands. (define_split [(set (match_operand:DF 0 "fp_register_operand" "") (if_then_else:DF (lt (match_operand:DF 1 "register_operand" "") (match_operand:DF 2 "register_operand" "")) (match_operand:DF 3 "register_operand" "") (match_operand:DF 4 "register_operand" ""))) (clobber (reg:CC 17))] "reload_completed && ((operands_match_p (operands[1], operands[3]) && operands_match_p (operands[2], operands[4])) || (operands_match_p (operands[1], operands[4]) && operands_match_p (operands[2], operands[3])))" [(set (reg:CCFP 17) (compare:CCFP (match_dup 2) (match_dup 1))) (set (match_dup 0) (if_then_else:DF (ge (reg:CCFP 17) (const_int 0)) (match_dup 1) (match_dup 2)))]) (define_insn "*mindf_sse" [(set (match_operand:DF 0 "register_operand" "=Y") (if_then_else:DF (lt (match_operand:DF 1 "register_operand" "0") (match_operand:DF 2 "nonimmediate_operand" "Ym")) (match_dup 1) (match_dup 2)))] "TARGET_SSE2 && TARGET_SSE_MATH && reload_completed" "minsd\t{%2, %0|%0, %2}" [(set_attr "type" "sse") (set_attr "mode" "DF")]) (define_expand "maxsf3" [(parallel [ (set (match_operand:SF 0 "register_operand" "") (if_then_else:SF (gt (match_operand:SF 1 "register_operand" "") (match_operand:SF 2 "nonimmediate_operand" "")) (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))])] "TARGET_SSE" "#") (define_insn "*maxsf" [(set (match_operand:SF 0 "register_operand" "=x#f,f#x,f#x") (if_then_else:SF (gt (match_operand:SF 1 "register_operand" "0,0,f#x") (match_operand:SF 2 "nonimmediate_operand" "xm#f,f#x,0")) (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))] "TARGET_SSE && TARGET_IEEE_FP" "#") (define_insn "*maxsf_nonieee" [(set (match_operand:SF 0 "register_operand" "=x#f,f#x") (if_then_else:SF (gt (match_operand:SF 1 "nonimmediate_operand" "%0,0") (match_operand:SF 2 "nonimmediate_operand" "xm#f,f#x")) (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))] "TARGET_SSE && !TARGET_IEEE_FP && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "#") (define_split [(set (match_operand:SF 0 "register_operand" "") (if_then_else:SF (gt (match_operand:SF 1 "register_operand" "") (match_operand:SF 2 "nonimmediate_operand" "")) (match_operand:SF 3 "register_operand" "") (match_operand:SF 4 "nonimmediate_operand" ""))) (clobber (reg:CC 17))] "SSE_REG_P (operands[0]) && reload_completed && ((operands_match_p (operands[1], operands[3]) && operands_match_p (operands[2], operands[4])) || (operands_match_p (operands[1], operands[4]) && operands_match_p (operands[2], operands[3])))" [(set (match_dup 0) (if_then_else:SF (gt (match_dup 1) (match_dup 2)) (match_dup 1) (match_dup 2)))]) (define_split [(set (match_operand:SF 0 "fp_register_operand" "") (if_then_else:SF (gt (match_operand:SF 1 "register_operand" "") (match_operand:SF 2 "register_operand" "")) (match_operand:SF 3 "register_operand" "") (match_operand:SF 4 "register_operand" ""))) (clobber (reg:CC 17))] "reload_completed && ((operands_match_p (operands[1], operands[3]) && operands_match_p (operands[2], operands[4])) || (operands_match_p (operands[1], operands[4]) && operands_match_p (operands[2], operands[3])))" [(set (reg:CCFP 17) (compare:CCFP (match_dup 1) (match_dup 2))) (set (match_dup 0) (if_then_else:SF (gt (reg:CCFP 17) (const_int 0)) (match_dup 1) (match_dup 2)))]) (define_insn "*maxsf_sse" [(set (match_operand:SF 0 "register_operand" "=x") (if_then_else:SF (gt (match_operand:SF 1 "register_operand" "0") (match_operand:SF 2 "nonimmediate_operand" "xm")) (match_dup 1) (match_dup 2)))] "TARGET_SSE && reload_completed" "maxss\t{%2, %0|%0, %2}" [(set_attr "type" "sse") (set_attr "mode" "SF")]) (define_expand "maxdf3" [(parallel [ (set (match_operand:DF 0 "register_operand" "") (if_then_else:DF (gt (match_operand:DF 1 "register_operand" "") (match_operand:DF 2 "nonimmediate_operand" "")) (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))])] "TARGET_SSE2 && TARGET_SSE_MATH" "#") (define_insn "*maxdf" [(set (match_operand:DF 0 "register_operand" "=Y#f,f#Y,f#Y") (if_then_else:DF (gt (match_operand:DF 1 "register_operand" "0,0,f#Y") (match_operand:DF 2 "nonimmediate_operand" "Ym#f,f#Y,0")) (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))] "TARGET_SSE2 && TARGET_SSE_MATH && TARGET_IEEE_FP" "#") (define_insn "*maxdf_nonieee" [(set (match_operand:DF 0 "register_operand" "=Y#f,f#Y") (if_then_else:DF (gt (match_operand:DF 1 "nonimmediate_operand" "%0,0") (match_operand:DF 2 "nonimmediate_operand" "Ym#f,f#Y")) (match_dup 1) (match_dup 2))) (clobber (reg:CC 17))] "TARGET_SSE2 && TARGET_SSE_MATH && !TARGET_IEEE_FP && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "#") (define_split [(set (match_operand:DF 0 "register_operand" "") (if_then_else:DF (gt (match_operand:DF 1 "register_operand" "") (match_operand:DF 2 "nonimmediate_operand" "")) (match_operand:DF 3 "register_operand" "") (match_operand:DF 4 "nonimmediate_operand" ""))) (clobber (reg:CC 17))] "SSE_REG_P (operands[0]) && reload_completed && ((operands_match_p (operands[1], operands[3]) && operands_match_p (operands[2], operands[4])) || (operands_match_p (operands[1], operands[4]) && operands_match_p (operands[2], operands[3])))" [(set (match_dup 0) (if_then_else:DF (gt (match_dup 1) (match_dup 2)) (match_dup 1) (match_dup 2)))]) (define_split [(set (match_operand:DF 0 "fp_register_operand" "") (if_then_else:DF (gt (match_operand:DF 1 "register_operand" "") (match_operand:DF 2 "register_operand" "")) (match_operand:DF 3 "register_operand" "") (match_operand:DF 4 "register_operand" ""))) (clobber (reg:CC 17))] "reload_completed && ((operands_match_p (operands[1], operands[3]) && operands_match_p (operands[2], operands[4])) || (operands_match_p (operands[1], operands[4]) && operands_match_p (operands[2], operands[3])))" [(set (reg:CCFP 17) (compare:CCFP (match_dup 1) (match_dup 2))) (set (match_dup 0) (if_then_else:DF (gt (reg:CCFP 17) (const_int 0)) (match_dup 1) (match_dup 2)))]) (define_insn "*maxdf_sse" [(set (match_operand:DF 0 "register_operand" "=Y") (if_then_else:DF (gt (match_operand:DF 1 "register_operand" "0") (match_operand:DF 2 "nonimmediate_operand" "Ym")) (match_dup 1) (match_dup 2)))] "TARGET_SSE2 && TARGET_SSE_MATH && reload_completed" "maxsd\t{%2, %0|%0, %2}" [(set_attr "type" "sse") (set_attr "mode" "DF")]) ;; Misc patterns (?) ;; This pattern exists to put a dependency on all ebp-based memory accesses. ;; Otherwise there will be nothing to keep ;; ;; [(set (reg ebp) (reg esp))] ;; [(set (reg esp) (plus (reg esp) (const_int -160000))) ;; (clobber (eflags)] ;; [(set (mem (plus (reg ebp) (const_int -160000))) (const_int 0))] ;; ;; in proper program order. (define_insn "pro_epilogue_adjust_stack_1" [(set (match_operand:SI 0 "register_operand" "=r,r") (plus:SI (match_operand:SI 1 "register_operand" "0,r") (match_operand:SI 2 "immediate_operand" "i,i"))) (clobber (reg:CC 17)) (clobber (mem:BLK (scratch)))] "!TARGET_64BIT" { switch (get_attr_type (insn)) { case TYPE_IMOV: return "mov{l}\t{%1, %0|%0, %1}"; case TYPE_ALU: if (GET_CODE (operands[2]) == CONST_INT && (INTVAL (operands[2]) == 128 || (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) != -128))) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{l}\t{%2, %0|%0, %2}"; } return "add{l}\t{%2, %0|%0, %2}"; case TYPE_LEA: operands[2] = SET_SRC (XVECEXP (PATTERN (insn), 0, 0)); return "lea{l}\t{%a2, %0|%0, %a2}"; default: abort (); } } [(set (attr "type") (cond [(eq_attr "alternative" "0") (const_string "alu") (match_operand:SI 2 "const0_operand" "") (const_string "imov") ] (const_string "lea"))) (set_attr "mode" "SI")]) (define_insn "pro_epilogue_adjust_stack_rex64" [(set (match_operand:DI 0 "register_operand" "=r,r") (plus:DI (match_operand:DI 1 "register_operand" "0,r") (match_operand:DI 2 "x86_64_immediate_operand" "e,e"))) (clobber (reg:CC 17)) (clobber (mem:BLK (scratch)))] "TARGET_64BIT" { switch (get_attr_type (insn)) { case TYPE_IMOV: return "mov{q}\t{%1, %0|%0, %1}"; case TYPE_ALU: if (GET_CODE (operands[2]) == CONST_INT /* Avoid overflows. */ && ((INTVAL (operands[2]) & ((((unsigned int) 1) << 31) - 1))) && (INTVAL (operands[2]) == 128 || (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) != -128))) { operands[2] = GEN_INT (-INTVAL (operands[2])); return "sub{q}\t{%2, %0|%0, %2}"; } return "add{q}\t{%2, %0|%0, %2}"; case TYPE_LEA: operands[2] = SET_SRC (XVECEXP (PATTERN (insn), 0, 0)); return "lea{q}\t{%a2, %0|%0, %a2}"; default: abort (); } } [(set (attr "type") (cond [(eq_attr "alternative" "0") (const_string "alu") (match_operand:DI 2 "const0_operand" "") (const_string "imov") ] (const_string "lea"))) (set_attr "mode" "DI")]) (define_insn "pro_epilogue_adjust_stack_rex64_2" [(set (match_operand:DI 0 "register_operand" "=r,r") (plus:DI (match_operand:DI 1 "register_operand" "0,r") (match_operand:DI 3 "immediate_operand" "i,i"))) (use (match_operand:DI 2 "register_operand" "r,r")) (clobber (reg:CC 17)) (clobber (mem:BLK (scratch)))] "TARGET_64BIT" { switch (get_attr_type (insn)) { case TYPE_ALU: return "add{q}\t{%2, %0|%0, %2}"; case TYPE_LEA: operands[2] = gen_rtx_PLUS (DImode, operands[1], operands[2]); return "lea{q}\t{%a2, %0|%0, %a2}"; default: abort (); } } [(set_attr "type" "alu,lea") (set_attr "mode" "DI")]) ;; Placeholder for the conditional moves. This one is split either to SSE ;; based moves emulation or to usual cmove sequence. Little bit unfortunate ;; fact is that compares supported by the cmp??ss instructions are exactly ;; swapped of those supported by cmove sequence. ;; The EQ/NE comparisons also needs bit care, since they are not directly ;; supported by i387 comparisons and we do need to emit two conditional moves ;; in tandem. (define_insn "sse_movsfcc" [(set (match_operand:SF 0 "register_operand" "=&x#rf,x#rf,?f#xr,?f#xr,?f#xr,?f#xr,?r#xf,?r#xf,?r#xf,?r#xf") (if_then_else:SF (match_operator 1 "sse_comparison_operator" [(match_operand:SF 4 "nonimmediate_operand" "0#fx,x#fx,f#x,f#x,xm#f,xm#f,f#x,f#x,xm#f,xm#f") (match_operand:SF 5 "nonimmediate_operand" "xm#f,xm#f,f#x,f#x,x#f,x#f,f#x,f#x,x#f,x#f")]) (match_operand:SF 2 "nonimmediate_operand" "x#fr,0#fr,f#fx,0#fx,f#fx,0#fx,rm#rx,0#rx,rm#rx,0#rx") (match_operand:SF 3 "nonimmediate_operand" "x#fr,x#fr,0#fx,f#fx,0#fx,f#fx,0#fx,rm#rx,0#rx,rm#rx"))) (clobber (match_scratch:SF 6 "=2,&4,X,X,X,X,X,X,X,X")) (clobber (reg:CC 17))] "TARGET_SSE && (GET_CODE (operands[2]) != MEM || GET_CODE (operands[3]) != MEM) /* Avoid combine from being smart and converting min/max instruction patterns into conditional moves. */ && ((GET_CODE (operands[1]) != LT && GET_CODE (operands[1]) != GT && GET_CODE (operands[1]) != UNLE && GET_CODE (operands[1]) != UNGE) || !rtx_equal_p (operands[4], operands[2]) || !rtx_equal_p (operands[5], operands[3])) && (!TARGET_IEEE_FP || (GET_CODE (operands[1]) != EQ && GET_CODE (operands[1]) != NE))" "#") (define_insn "sse_movsfcc_eq" [(set (match_operand:SF 0 "register_operand" "=&x#rf,x#rf,?f#xr,?f#xr,?r#xf,?r#xf") (if_then_else:SF (eq (match_operand:SF 3 "nonimmediate_operand" "%0#fx,x#fx,f#x,xm#f,f#x,xm#f") (match_operand:SF 4 "nonimmediate_operand" "xm#f,xm#f,f#x,x#f,f#x,x#f")) (match_operand:SF 1 "nonimmediate_operand" "x#fr,0#fr,0#fx,0#fx,0#rx,0#rx") (match_operand:SF 2 "nonimmediate_operand" "x#fr,x#fr,f#fx,f#fx,rm#rx,rm#rx"))) (clobber (match_scratch:SF 5 "=1,&3,X,X,X,X")) (clobber (reg:CC 17))] "TARGET_SSE && (GET_CODE (operands[2]) != MEM || GET_CODE (operands[3]) != MEM)" "#") (define_insn "sse_movdfcc" [(set (match_operand:DF 0 "register_operand" "=&Y#rf,Y#rf,?f#Yr,?f#Yr,?f#Yr,?f#Yr,?r#Yf,?r#Yf,?r#Yf,?r#Yf") (if_then_else:DF (match_operator 1 "sse_comparison_operator" [(match_operand:DF 4 "nonimmediate_operand" "0#fY,Y#fY,f#Y,f#Y,Ym#f,Ym#f,f#Y,f#Y,Ym#f,Ym#f") (match_operand:DF 5 "nonimmediate_operand" "Ym#f,Ym#f,f#Y,f#Y,Y#f,Y#f,f#Y,f#Y,Y#f,Y#f")]) (match_operand:DF 2 "nonimmediate_operand" "Y#fr,0#fr,f#fY,0#fY,f#fY,0#fY,rm#rY,0#rY,rm#rY,0#rY") (match_operand:DF 3 "nonimmediate_operand" "Y#fr,Y#fr,0#fY,f#fY,0#fY,f#fY,0#fY,rm#rY,0#rY,rm#rY"))) (clobber (match_scratch:DF 6 "=2,&4,X,X,X,X,X,X,X,X")) (clobber (reg:CC 17))] "TARGET_SSE2 && (GET_CODE (operands[2]) != MEM || GET_CODE (operands[3]) != MEM) /* Avoid combine from being smart and converting min/max instruction patterns into conditional moves. */ && ((GET_CODE (operands[1]) != LT && GET_CODE (operands[1]) != GT && GET_CODE (operands[1]) != UNLE && GET_CODE (operands[1]) != UNGE) || !rtx_equal_p (operands[4], operands[2]) || !rtx_equal_p (operands[5], operands[3])) && (!TARGET_IEEE_FP || (GET_CODE (operands[1]) != EQ && GET_CODE (operands[1]) != NE))" "#") (define_insn "sse_movdfcc_eq" [(set (match_operand:DF 0 "register_operand" "=&Y#rf,Y#rf,?f#Yr,?f#Yr,?r#Yf,?r#Yf") (if_then_else:DF (eq (match_operand:DF 3 "nonimmediate_operand" "%0#fY,Y#fY,f#Y,Ym#f,f#Y,Ym#f") (match_operand:DF 4 "nonimmediate_operand" "Ym#f,Ym#f,f#Y,Y#f,f#Y,Y#f")) (match_operand:DF 1 "nonimmediate_operand" "Y#fr,0#fr,0#fY,0#fY,0#rY,0#rY") (match_operand:DF 2 "nonimmediate_operand" "Y#fr,Y#fr,f#fY,f#fY,rm#rY,rm#rY"))) (clobber (match_scratch:DF 5 "=1,&3,X,X,X,X")) (clobber (reg:CC 17))] "TARGET_SSE && (GET_CODE (operands[2]) != MEM || GET_CODE (operands[3]) != MEM)" "#") ;; For non-sse moves just expand the usual cmove sequence. (define_split [(set (match_operand 0 "register_operand" "") (if_then_else (match_operator 1 "comparison_operator" [(match_operand 4 "nonimmediate_operand" "") (match_operand 5 "register_operand" "")]) (match_operand 2 "nonimmediate_operand" "") (match_operand 3 "nonimmediate_operand" ""))) (clobber (match_operand 6 "" "")) (clobber (reg:CC 17))] "!SSE_REG_P (operands[0]) && reload_completed && (GET_MODE (operands[0]) == SFmode || (TARGET_SSE2 && GET_MODE (operands[0]) == DFmode))" [(const_int 0)] { ix86_compare_op0 = operands[5]; ix86_compare_op1 = operands[4]; operands[1] = gen_rtx_fmt_ee (swap_condition (GET_CODE (operands[1])), VOIDmode, operands[5], operands[4]); ix86_expand_fp_movcc (operands); DONE; }) ;; Split SSE based conditional move into sequence: ;; cmpCC op0, op4 - set op0 to 0 or ffffffff depending on the comparison ;; and op2, op0 - zero op2 if comparison was false ;; nand op0, op3 - load op3 to op0 if comparison was false ;; or op2, op0 - get the nonzero one into the result. (define_split [(set (match_operand:SF 0 "register_operand" "") (if_then_else:SF (match_operator 1 "sse_comparison_operator" [(match_operand:SF 4 "register_operand" "") (match_operand:SF 5 "nonimmediate_operand" "")]) (match_operand:SF 2 "register_operand" "") (match_operand:SF 3 "register_operand" ""))) (clobber (match_operand 6 "" "")) (clobber (reg:CC 17))] "SSE_REG_P (operands[0]) && reload_completed" [(set (match_dup 4) (match_op_dup 1 [(match_dup 4) (match_dup 5)])) (set (match_dup 2) (and:V4SF (match_dup 2) (match_dup 8))) (set (match_dup 8) (and:V4SF (not:V4SF (match_dup 8)) (match_dup 3))) (set (match_dup 0) (ior:V4SF (match_dup 6) (match_dup 7)))] { /* If op2 == op3, op3 would be clobbered before it is used. */ if (operands_match_p (operands[2], operands[3])) { emit_move_insn (operands[0], operands[2]); DONE; } PUT_MODE (operands[1], GET_MODE (operands[0])); if (operands_match_p (operands[0], operands[4])) operands[6] = operands[4], operands[7] = operands[2]; else operands[6] = operands[2], operands[7] = operands[4]; operands[0] = simplify_gen_subreg (V4SFmode, operands[0], SFmode, 0); operands[2] = simplify_gen_subreg (V4SFmode, operands[2], SFmode, 0); operands[3] = simplify_gen_subreg (V4SFmode, operands[3], SFmode, 0); operands[8] = simplify_gen_subreg (V4SFmode, operands[4], SFmode, 0); operands[6] = simplify_gen_subreg (V4SFmode, operands[6], SFmode, 0); operands[7] = simplify_gen_subreg (V4SFmode, operands[7], SFmode, 0); }) (define_split [(set (match_operand:DF 0 "register_operand" "") (if_then_else:DF (match_operator 1 "sse_comparison_operator" [(match_operand:DF 4 "register_operand" "") (match_operand:DF 5 "nonimmediate_operand" "")]) (match_operand:DF 2 "register_operand" "") (match_operand:DF 3 "register_operand" ""))) (clobber (match_operand 6 "" "")) (clobber (reg:CC 17))] "SSE_REG_P (operands[0]) && reload_completed" [(set (match_dup 4) (match_op_dup 1 [(match_dup 4) (match_dup 5)])) (set (match_dup 2) (and:V2DF (match_dup 2) (match_dup 8))) (set (match_dup 8) (and:V2DF (not:V2DF (match_dup 8)) (match_dup 3))) (set (match_dup 0) (ior:V2DF (match_dup 6) (match_dup 7)))] { if (GET_MODE (operands[2]) == DFmode && TARGET_SSE_PARTIAL_REGS && !optimize_size) { rtx op = simplify_gen_subreg (V2DFmode, operands[2], DFmode, 0); emit_insn (gen_sse2_unpcklpd (op, op, op)); op = simplify_gen_subreg (V2DFmode, operands[3], DFmode, 0); emit_insn (gen_sse2_unpcklpd (op, op, op)); } /* If op2 == op3, op3 would be clobbered before it is used. */ if (operands_match_p (operands[2], operands[3])) { emit_move_insn (operands[0], operands[2]); DONE; } PUT_MODE (operands[1], GET_MODE (operands[0])); if (operands_match_p (operands[0], operands[4])) operands[6] = operands[4], operands[7] = operands[2]; else operands[6] = operands[2], operands[7] = operands[4]; operands[0] = simplify_gen_subreg (V2DFmode, operands[0], DFmode, 0); operands[2] = simplify_gen_subreg (V2DFmode, operands[2], DFmode, 0); operands[3] = simplify_gen_subreg (V2DFmode, operands[3], DFmode, 0); operands[8] = simplify_gen_subreg (V2DFmode, operands[4], DFmode, 0); operands[6] = simplify_gen_subreg (V2DFmode, operands[6], DFmode, 0); operands[7] = simplify_gen_subreg (V2DFmode, operands[7], DFmode, 0); }) ;; Special case of conditional move we can handle effectively. ;; Do not brother with the integer/floating point case, since these are ;; bot considerably slower, unlike in the generic case. (define_insn "*sse_movsfcc_const0_1" [(set (match_operand:SF 0 "register_operand" "=&x") (if_then_else:SF (match_operator 1 "sse_comparison_operator" [(match_operand:SF 4 "register_operand" "0") (match_operand:SF 5 "nonimmediate_operand" "xm")]) (match_operand:SF 2 "register_operand" "x") (match_operand:SF 3 "const0_operand" "X")))] "TARGET_SSE" "#") (define_insn "*sse_movsfcc_const0_2" [(set (match_operand:SF 0 "register_operand" "=&x") (if_then_else:SF (match_operator 1 "sse_comparison_operator" [(match_operand:SF 4 "register_operand" "0") (match_operand:SF 5 "nonimmediate_operand" "xm")]) (match_operand:SF 2 "const0_operand" "X") (match_operand:SF 3 "register_operand" "x")))] "TARGET_SSE" "#") (define_insn "*sse_movsfcc_const0_3" [(set (match_operand:SF 0 "register_operand" "=&x") (if_then_else:SF (match_operator 1 "fcmov_comparison_operator" [(match_operand:SF 4 "nonimmediate_operand" "xm") (match_operand:SF 5 "register_operand" "0")]) (match_operand:SF 2 "register_operand" "x") (match_operand:SF 3 "const0_operand" "X")))] "TARGET_SSE" "#") (define_insn "*sse_movsfcc_const0_4" [(set (match_operand:SF 0 "register_operand" "=&x") (if_then_else:SF (match_operator 1 "fcmov_comparison_operator" [(match_operand:SF 4 "nonimmediate_operand" "xm") (match_operand:SF 5 "register_operand" "0")]) (match_operand:SF 2 "const0_operand" "X") (match_operand:SF 3 "register_operand" "x")))] "TARGET_SSE" "#") (define_insn "*sse_movdfcc_const0_1" [(set (match_operand:DF 0 "register_operand" "=&Y") (if_then_else:DF (match_operator 1 "sse_comparison_operator" [(match_operand:DF 4 "register_operand" "0") (match_operand:DF 5 "nonimmediate_operand" "Ym")]) (match_operand:DF 2 "register_operand" "Y") (match_operand:DF 3 "const0_operand" "X")))] "TARGET_SSE2" "#") (define_insn "*sse_movdfcc_const0_2" [(set (match_operand:DF 0 "register_operand" "=&Y") (if_then_else:DF (match_operator 1 "sse_comparison_operator" [(match_operand:DF 4 "register_operand" "0") (match_operand:DF 5 "nonimmediate_operand" "Ym")]) (match_operand:DF 2 "const0_operand" "X") (match_operand:DF 3 "register_operand" "Y")))] "TARGET_SSE2" "#") (define_insn "*sse_movdfcc_const0_3" [(set (match_operand:DF 0 "register_operand" "=&Y") (if_then_else:DF (match_operator 1 "fcmov_comparison_operator" [(match_operand:DF 4 "nonimmediate_operand" "Ym") (match_operand:DF 5 "register_operand" "0")]) (match_operand:DF 2 "register_operand" "Y") (match_operand:DF 3 "const0_operand" "X")))] "TARGET_SSE2" "#") (define_insn "*sse_movdfcc_const0_4" [(set (match_operand:DF 0 "register_operand" "=&Y") (if_then_else:DF (match_operator 1 "fcmov_comparison_operator" [(match_operand:DF 4 "nonimmediate_operand" "Ym") (match_operand:DF 5 "register_operand" "0")]) (match_operand:DF 2 "const0_operand" "X") (match_operand:DF 3 "register_operand" "Y")))] "TARGET_SSE2" "#") (define_split [(set (match_operand:SF 0 "register_operand" "") (if_then_else:SF (match_operator 1 "comparison_operator" [(match_operand:SF 4 "nonimmediate_operand" "") (match_operand:SF 5 "nonimmediate_operand" "")]) (match_operand:SF 2 "nonmemory_operand" "") (match_operand:SF 3 "nonmemory_operand" "")))] "SSE_REG_P (operands[0]) && reload_completed && (const0_operand (operands[2], GET_MODE (operands[0])) || const0_operand (operands[3], GET_MODE (operands[0])))" [(set (match_dup 0) (match_op_dup 1 [(match_dup 0) (match_dup 5)])) (set (match_dup 8) (and:V4SF (match_dup 6) (match_dup 7)))] { PUT_MODE (operands[1], GET_MODE (operands[0])); if (!sse_comparison_operator (operands[1], VOIDmode) || !rtx_equal_p (operands[0], operands[4])) { rtx tmp = operands[5]; operands[5] = operands[4]; operands[4] = tmp; PUT_CODE (operands[1], swap_condition (GET_CODE (operands[1]))); } if (!rtx_equal_p (operands[0], operands[4])) abort (); operands[8] = simplify_gen_subreg (V4SFmode, operands[0], SFmode, 0); if (const0_operand (operands[2], GET_MODE (operands[2]))) { operands[7] = operands[3]; operands[6] = gen_rtx_NOT (V4SFmode, operands[5]); } else { operands[7] = operands[2]; operands[6] = operands[8]; } operands[7] = simplify_gen_subreg (V4SFmode, operands[7], SFmode, 0); }) (define_split [(set (match_operand:DF 0 "register_operand" "") (if_then_else:DF (match_operator 1 "comparison_operator" [(match_operand:DF 4 "nonimmediate_operand" "") (match_operand:DF 5 "nonimmediate_operand" "")]) (match_operand:DF 2 "nonmemory_operand" "") (match_operand:DF 3 "nonmemory_operand" "")))] "SSE_REG_P (operands[0]) && reload_completed && (const0_operand (operands[2], GET_MODE (operands[0])) || const0_operand (operands[3], GET_MODE (operands[0])))" [(set (match_dup 0) (match_op_dup 1 [(match_dup 0) (match_dup 5)])) (set (match_dup 8) (and:V2DF (match_dup 6) (match_dup 7)))] { if (TARGET_SSE_PARTIAL_REGS && !optimize_size && GET_MODE (operands[2]) == DFmode) { if (REG_P (operands[2])) { rtx op = simplify_gen_subreg (V2DFmode, operands[2], DFmode, 0); emit_insn (gen_sse2_unpcklpd (op, op, op)); } if (REG_P (operands[3])) { rtx op = simplify_gen_subreg (V2DFmode, operands[3], DFmode, 0); emit_insn (gen_sse2_unpcklpd (op, op, op)); } } PUT_MODE (operands[1], GET_MODE (operands[0])); if (!sse_comparison_operator (operands[1], VOIDmode) || !rtx_equal_p (operands[0], operands[4])) { rtx tmp = operands[5]; operands[5] = operands[4]; operands[4] = tmp; PUT_CODE (operands[1], swap_condition (GET_CODE (operands[1]))); } if (!rtx_equal_p (operands[0], operands[4])) abort (); operands[8] = simplify_gen_subreg (V2DFmode, operands[0], DFmode, 0); if (const0_operand (operands[2], GET_MODE (operands[2]))) { operands[7] = operands[3]; operands[6] = gen_rtx_NOT (V2DFmode, operands[8]); } else { operands[7] = operands[2]; operands[6] = operands[8]; } operands[7] = simplify_gen_subreg (V2DFmode, operands[7], DFmode, 0); }) (define_expand "allocate_stack_worker" [(match_operand:SI 0 "register_operand" "")] "TARGET_STACK_PROBE" { if (reload_completed) { if (TARGET_64BIT) emit_insn (gen_allocate_stack_worker_rex64_postreload (operands[0])); else emit_insn (gen_allocate_stack_worker_postreload (operands[0])); } else { if (TARGET_64BIT) emit_insn (gen_allocate_stack_worker_rex64 (operands[0])); else emit_insn (gen_allocate_stack_worker_1 (operands[0])); } DONE; }) (define_insn "allocate_stack_worker_1" [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "a")] UNSPECV_STACK_PROBE) (set (reg:SI 7) (minus:SI (reg:SI 7) (match_dup 0))) (clobber (match_scratch:SI 1 "=0")) (clobber (reg:CC 17))] "!TARGET_64BIT && TARGET_STACK_PROBE" "call\t__alloca" [(set_attr "type" "multi") (set_attr "length" "5")]) (define_expand "allocate_stack_worker_postreload" [(parallel [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "a")] UNSPECV_STACK_PROBE) (set (reg:SI 7) (minus:SI (reg:SI 7) (match_dup 0))) (clobber (match_dup 0)) (clobber (reg:CC 17))])] "" "") (define_insn "allocate_stack_worker_rex64" [(unspec_volatile:DI [(match_operand:DI 0 "register_operand" "a")] UNSPECV_STACK_PROBE) (set (reg:DI 7) (minus:DI (reg:DI 7) (match_dup 0))) (clobber (match_scratch:DI 1 "=0")) (clobber (reg:CC 17))] "TARGET_64BIT && TARGET_STACK_PROBE" "call\t__alloca" [(set_attr "type" "multi") (set_attr "length" "5")]) (define_expand "allocate_stack_worker_rex64_postreload" [(parallel [(unspec_volatile:DI [(match_operand:DI 0 "register_operand" "a")] UNSPECV_STACK_PROBE) (set (reg:DI 7) (minus:DI (reg:DI 7) (match_dup 0))) (clobber (match_dup 0)) (clobber (reg:CC 17))])] "" "") (define_expand "allocate_stack" [(parallel [(set (match_operand:SI 0 "register_operand" "=r") (minus:SI (reg:SI 7) (match_operand:SI 1 "general_operand" ""))) (clobber (reg:CC 17))]) (parallel [(set (reg:SI 7) (minus:SI (reg:SI 7) (match_dup 1))) (clobber (reg:CC 17))])] "TARGET_STACK_PROBE" { #ifdef CHECK_STACK_LIMIT if (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) < CHECK_STACK_LIMIT) emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, operands[1])); else #endif emit_insn (gen_allocate_stack_worker (copy_to_mode_reg (SImode, operands[1]))); emit_move_insn (operands[0], virtual_stack_dynamic_rtx); DONE; }) (define_expand "builtin_setjmp_receiver" [(label_ref (match_operand 0 "" ""))] "!TARGET_64BIT && flag_pic" { emit_insn (gen_set_got (pic_offset_table_rtx)); DONE; }) ;; Avoid redundant prefixes by splitting HImode arithmetic to SImode. (define_split [(set (match_operand 0 "register_operand" "") (match_operator 3 "promotable_binary_operator" [(match_operand 1 "register_operand" "") (match_operand 2 "aligned_operand" "")])) (clobber (reg:CC 17))] "! TARGET_PARTIAL_REG_STALL && reload_completed && ((GET_MODE (operands[0]) == HImode && ((!optimize_size && !TARGET_FAST_PREFIX) || GET_CODE (operands[2]) != CONST_INT || CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'K'))) || (GET_MODE (operands[0]) == QImode && (TARGET_PROMOTE_QImode || optimize_size)))" [(parallel [(set (match_dup 0) (match_op_dup 3 [(match_dup 1) (match_dup 2)])) (clobber (reg:CC 17))])] "operands[0] = gen_lowpart (SImode, operands[0]); operands[1] = gen_lowpart (SImode, operands[1]); if (GET_CODE (operands[3]) != ASHIFT) operands[2] = gen_lowpart (SImode, operands[2]); PUT_MODE (operands[3], SImode);") ; Promote the QImode tests, as i386 has encoding of the AND ; instruction with 32-bit sign-extended immediate and thus the ; instruction size is unchanged, except in the %eax case for ; which it is increased by one byte, hence the ! optimize_size. (define_split [(set (match_operand 0 "flags_reg_operand" "") (match_operator 2 "compare_operator" [(and (match_operand 3 "aligned_operand" "") (match_operand 4 "const_int_operand" "")) (const_int 0)])) (set (match_operand 1 "register_operand" "") (and (match_dup 3) (match_dup 4)))] "! TARGET_PARTIAL_REG_STALL && reload_completed /* Ensure that the operand will remain sign-extended immediate. */ && ix86_match_ccmode (insn, INTVAL (operands[4]) >= 0 ? CCNOmode : CCZmode) && ! optimize_size && ((GET_MODE (operands[1]) == HImode && ! TARGET_FAST_PREFIX) || (GET_MODE (operands[1]) == QImode && TARGET_PROMOTE_QImode))" [(parallel [(set (match_dup 0) (match_op_dup 2 [(and:SI (match_dup 3) (match_dup 4)) (const_int 0)])) (set (match_dup 1) (and:SI (match_dup 3) (match_dup 4)))])] { operands[4] = gen_int_mode (INTVAL (operands[4]) & GET_MODE_MASK (GET_MODE (operands[1])), SImode); operands[1] = gen_lowpart (SImode, operands[1]); operands[3] = gen_lowpart (SImode, operands[3]); }) ; Don't promote the QImode tests, as i386 doesn't have encoding of ; the TEST instruction with 32-bit sign-extended immediate and thus ; the instruction size would at least double, which is not what we ; want even with ! optimize_size. (define_split [(set (match_operand 0 "flags_reg_operand" "") (match_operator 1 "compare_operator" [(and (match_operand:HI 2 "aligned_operand" "") (match_operand:HI 3 "const_int_operand" "")) (const_int 0)]))] "! TARGET_PARTIAL_REG_STALL && reload_completed /* Ensure that the operand will remain sign-extended immediate. */ && ix86_match_ccmode (insn, INTVAL (operands[3]) >= 0 ? CCNOmode : CCZmode) && ! TARGET_FAST_PREFIX && ! optimize_size" [(set (match_dup 0) (match_op_dup 1 [(and:SI (match_dup 2) (match_dup 3)) (const_int 0)]))] { operands[3] = gen_int_mode (INTVAL (operands[3]) & GET_MODE_MASK (GET_MODE (operands[2])), SImode); operands[2] = gen_lowpart (SImode, operands[2]); }) (define_split [(set (match_operand 0 "register_operand" "") (neg (match_operand 1 "register_operand" ""))) (clobber (reg:CC 17))] "! TARGET_PARTIAL_REG_STALL && reload_completed && (GET_MODE (operands[0]) == HImode || (GET_MODE (operands[0]) == QImode && (TARGET_PROMOTE_QImode || optimize_size)))" [(parallel [(set (match_dup 0) (neg:SI (match_dup 1))) (clobber (reg:CC 17))])] "operands[0] = gen_lowpart (SImode, operands[0]); operands[1] = gen_lowpart (SImode, operands[1]);") (define_split [(set (match_operand 0 "register_operand" "") (not (match_operand 1 "register_operand" "")))] "! TARGET_PARTIAL_REG_STALL && reload_completed && (GET_MODE (operands[0]) == HImode || (GET_MODE (operands[0]) == QImode && (TARGET_PROMOTE_QImode || optimize_size)))" [(set (match_dup 0) (not:SI (match_dup 1)))] "operands[0] = gen_lowpart (SImode, operands[0]); operands[1] = gen_lowpart (SImode, operands[1]);") (define_split [(set (match_operand 0 "register_operand" "") (if_then_else (match_operator 1 "comparison_operator" [(reg 17) (const_int 0)]) (match_operand 2 "register_operand" "") (match_operand 3 "register_operand" "")))] "! TARGET_PARTIAL_REG_STALL && TARGET_CMOVE && (GET_MODE (operands[0]) == HImode || (GET_MODE (operands[0]) == QImode && (TARGET_PROMOTE_QImode || optimize_size)))" [(set (match_dup 0) (if_then_else:SI (match_dup 1) (match_dup 2) (match_dup 3)))] "operands[0] = gen_lowpart (SImode, operands[0]); operands[2] = gen_lowpart (SImode, operands[2]); operands[3] = gen_lowpart (SImode, operands[3]);") ;; RTL Peephole optimizations, run before sched2. These primarily look to ;; transform a complex memory operation into two memory to register operations. ;; Don't push memory operands (define_peephole2 [(set (match_operand:SI 0 "push_operand" "") (match_operand:SI 1 "memory_operand" "")) (match_scratch:SI 2 "r")] "! optimize_size && ! TARGET_PUSH_MEMORY" [(set (match_dup 2) (match_dup 1)) (set (match_dup 0) (match_dup 2))] "") (define_peephole2 [(set (match_operand:DI 0 "push_operand" "") (match_operand:DI 1 "memory_operand" "")) (match_scratch:DI 2 "r")] "! optimize_size && ! TARGET_PUSH_MEMORY" [(set (match_dup 2) (match_dup 1)) (set (match_dup 0) (match_dup 2))] "") ;; We need to handle SFmode only, because DFmode and XFmode is split to ;; SImode pushes. (define_peephole2 [(set (match_operand:SF 0 "push_operand" "") (match_operand:SF 1 "memory_operand" "")) (match_scratch:SF 2 "r")] "! optimize_size && ! TARGET_PUSH_MEMORY" [(set (match_dup 2) (match_dup 1)) (set (match_dup 0) (match_dup 2))] "") (define_peephole2 [(set (match_operand:HI 0 "push_operand" "") (match_operand:HI 1 "memory_operand" "")) (match_scratch:HI 2 "r")] "! optimize_size && ! TARGET_PUSH_MEMORY" [(set (match_dup 2) (match_dup 1)) (set (match_dup 0) (match_dup 2))] "") (define_peephole2 [(set (match_operand:QI 0 "push_operand" "") (match_operand:QI 1 "memory_operand" "")) (match_scratch:QI 2 "q")] "! optimize_size && ! TARGET_PUSH_MEMORY" [(set (match_dup 2) (match_dup 1)) (set (match_dup 0) (match_dup 2))] "") ;; Don't move an immediate directly to memory when the instruction ;; gets too big. (define_peephole2 [(match_scratch:SI 1 "r") (set (match_operand:SI 0 "memory_operand" "") (const_int 0))] "! optimize_size && ! TARGET_USE_MOV0 && TARGET_SPLIT_LONG_MOVES && get_attr_length (insn) >= ix86_cost->large_insn && peep2_regno_dead_p (0, FLAGS_REG)" [(parallel [(set (match_dup 1) (const_int 0)) (clobber (reg:CC 17))]) (set (match_dup 0) (match_dup 1))] "") (define_peephole2 [(match_scratch:HI 1 "r") (set (match_operand:HI 0 "memory_operand" "") (const_int 0))] "! optimize_size && ! TARGET_USE_MOV0 && TARGET_SPLIT_LONG_MOVES && get_attr_length (insn) >= ix86_cost->large_insn && peep2_regno_dead_p (0, FLAGS_REG)" [(parallel [(set (match_dup 2) (const_int 0)) (clobber (reg:CC 17))]) (set (match_dup 0) (match_dup 1))] "operands[2] = gen_lowpart (SImode, operands[1]);") (define_peephole2 [(match_scratch:QI 1 "q") (set (match_operand:QI 0 "memory_operand" "") (const_int 0))] "! optimize_size && ! TARGET_USE_MOV0 && TARGET_SPLIT_LONG_MOVES && get_attr_length (insn) >= ix86_cost->large_insn && peep2_regno_dead_p (0, FLAGS_REG)" [(parallel [(set (match_dup 2) (const_int 0)) (clobber (reg:CC 17))]) (set (match_dup 0) (match_dup 1))] "operands[2] = gen_lowpart (SImode, operands[1]);") (define_peephole2 [(match_scratch:SI 2 "r") (set (match_operand:SI 0 "memory_operand" "") (match_operand:SI 1 "immediate_operand" ""))] "! optimize_size && get_attr_length (insn) >= ix86_cost->large_insn && TARGET_SPLIT_LONG_MOVES" [(set (match_dup 2) (match_dup 1)) (set (match_dup 0) (match_dup 2))] "") (define_peephole2 [(match_scratch:HI 2 "r") (set (match_operand:HI 0 "memory_operand" "") (match_operand:HI 1 "immediate_operand" ""))] "! optimize_size && get_attr_length (insn) >= ix86_cost->large_insn && TARGET_SPLIT_LONG_MOVES" [(set (match_dup 2) (match_dup 1)) (set (match_dup 0) (match_dup 2))] "") (define_peephole2 [(match_scratch:QI 2 "q") (set (match_operand:QI 0 "memory_operand" "") (match_operand:QI 1 "immediate_operand" ""))] "! optimize_size && get_attr_length (insn) >= ix86_cost->large_insn && TARGET_SPLIT_LONG_MOVES" [(set (match_dup 2) (match_dup 1)) (set (match_dup 0) (match_dup 2))] "") ;; Don't compare memory with zero, load and use a test instead. (define_peephole2 [(set (match_operand 0 "flags_reg_operand" "") (match_operator 1 "compare_operator" [(match_operand:SI 2 "memory_operand" "") (const_int 0)])) (match_scratch:SI 3 "r")] "ix86_match_ccmode (insn, CCNOmode) && ! optimize_size" [(set (match_dup 3) (match_dup 2)) (set (match_dup 0) (match_op_dup 1 [(match_dup 3) (const_int 0)]))] "") ;; NOT is not pairable on Pentium, while XOR is, but one byte longer. ;; Don't split NOTs with a displacement operand, because resulting XOR ;; will not be pairable anyway. ;; ;; On AMD K6, NOT is vector decoded with memory operand that can not be ;; represented using a modRM byte. The XOR replacement is long decoded, ;; so this split helps here as well. ;; ;; Note: Can't do this as a regular split because we can't get proper ;; lifetime information then. (define_peephole2 [(set (match_operand:SI 0 "nonimmediate_operand" "") (not:SI (match_operand:SI 1 "nonimmediate_operand" "")))] "!optimize_size && peep2_regno_dead_p (0, FLAGS_REG) && ((TARGET_PENTIUM && (GET_CODE (operands[0]) != MEM || !memory_displacement_operand (operands[0], SImode))) || (TARGET_K6 && long_memory_operand (operands[0], SImode)))" [(parallel [(set (match_dup 0) (xor:SI (match_dup 1) (const_int -1))) (clobber (reg:CC 17))])] "") (define_peephole2 [(set (match_operand:HI 0 "nonimmediate_operand" "") (not:HI (match_operand:HI 1 "nonimmediate_operand" "")))] "!optimize_size && peep2_regno_dead_p (0, FLAGS_REG) && ((TARGET_PENTIUM && (GET_CODE (operands[0]) != MEM || !memory_displacement_operand (operands[0], HImode))) || (TARGET_K6 && long_memory_operand (operands[0], HImode)))" [(parallel [(set (match_dup 0) (xor:HI (match_dup 1) (const_int -1))) (clobber (reg:CC 17))])] "") (define_peephole2 [(set (match_operand:QI 0 "nonimmediate_operand" "") (not:QI (match_operand:QI 1 "nonimmediate_operand" "")))] "!optimize_size && peep2_regno_dead_p (0, FLAGS_REG) && ((TARGET_PENTIUM && (GET_CODE (operands[0]) != MEM || !memory_displacement_operand (operands[0], QImode))) || (TARGET_K6 && long_memory_operand (operands[0], QImode)))" [(parallel [(set (match_dup 0) (xor:QI (match_dup 1) (const_int -1))) (clobber (reg:CC 17))])] "") ;; Non pairable "test imm, reg" instructions can be translated to ;; "and imm, reg" if reg dies. The "and" form is also shorter (one ;; byte opcode instead of two, have a short form for byte operands), ;; so do it for other CPUs as well. Given that the value was dead, ;; this should not create any new dependencies. Pass on the sub-word ;; versions if we're concerned about partial register stalls. (define_peephole2 [(set (match_operand 0 "flags_reg_operand" "") (match_operator 1 "compare_operator" [(and:SI (match_operand:SI 2 "register_operand" "") (match_operand:SI 3 "immediate_operand" "")) (const_int 0)]))] "ix86_match_ccmode (insn, CCNOmode) && (true_regnum (operands[2]) != 0 || (GET_CODE (operands[3]) == CONST_INT && CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'K'))) && peep2_reg_dead_p (1, operands[2])" [(parallel [(set (match_dup 0) (match_op_dup 1 [(and:SI (match_dup 2) (match_dup 3)) (const_int 0)])) (set (match_dup 2) (and:SI (match_dup 2) (match_dup 3)))])] "") ;; We don't need to handle HImode case, because it will be promoted to SImode ;; on ! TARGET_PARTIAL_REG_STALL (define_peephole2 [(set (match_operand 0 "flags_reg_operand" "") (match_operator 1 "compare_operator" [(and:QI (match_operand:QI 2 "register_operand" "") (match_operand:QI 3 "immediate_operand" "")) (const_int 0)]))] "! TARGET_PARTIAL_REG_STALL && ix86_match_ccmode (insn, CCNOmode) && true_regnum (operands[2]) != 0 && peep2_reg_dead_p (1, operands[2])" [(parallel [(set (match_dup 0) (match_op_dup 1 [(and:QI (match_dup 2) (match_dup 3)) (const_int 0)])) (set (match_dup 2) (and:QI (match_dup 2) (match_dup 3)))])] "") (define_peephole2 [(set (match_operand 0 "flags_reg_operand" "") (match_operator 1 "compare_operator" [(and:SI (zero_extract:SI (match_operand 2 "ext_register_operand" "") (const_int 8) (const_int 8)) (match_operand 3 "const_int_operand" "")) (const_int 0)]))] "! TARGET_PARTIAL_REG_STALL && ix86_match_ccmode (insn, CCNOmode) && true_regnum (operands[2]) != 0 && peep2_reg_dead_p (1, operands[2])" [(parallel [(set (match_dup 0) (match_op_dup 1 [(and:SI (zero_extract:SI (match_dup 2) (const_int 8) (const_int 8)) (match_dup 3)) (const_int 0)])) (set (zero_extract:SI (match_dup 2) (const_int 8) (const_int 8)) (and:SI (zero_extract:SI (match_dup 2) (const_int 8) (const_int 8)) (match_dup 3)))])] "") ;; Don't do logical operations with memory inputs. (define_peephole2 [(match_scratch:SI 2 "r") (parallel [(set (match_operand:SI 0 "register_operand" "") (match_operator:SI 3 "arith_or_logical_operator" [(match_dup 0) (match_operand:SI 1 "memory_operand" "")])) (clobber (reg:CC 17))])] "! optimize_size && ! TARGET_READ_MODIFY" [(set (match_dup 2) (match_dup 1)) (parallel [(set (match_dup 0) (match_op_dup 3 [(match_dup 0) (match_dup 2)])) (clobber (reg:CC 17))])] "") (define_peephole2 [(match_scratch:SI 2 "r") (parallel [(set (match_operand:SI 0 "register_operand" "") (match_operator:SI 3 "arith_or_logical_operator" [(match_operand:SI 1 "memory_operand" "") (match_dup 0)])) (clobber (reg:CC 17))])] "! optimize_size && ! TARGET_READ_MODIFY" [(set (match_dup 2) (match_dup 1)) (parallel [(set (match_dup 0) (match_op_dup 3 [(match_dup 2) (match_dup 0)])) (clobber (reg:CC 17))])] "") ; Don't do logical operations with memory outputs ; ; These two don't make sense for PPro/PII -- we're expanding a 4-uop ; instruction into two 1-uop insns plus a 2-uop insn. That last has ; the same decoder scheduling characteristics as the original. (define_peephole2 [(match_scratch:SI 2 "r") (parallel [(set (match_operand:SI 0 "memory_operand" "") (match_operator:SI 3 "arith_or_logical_operator" [(match_dup 0) (match_operand:SI 1 "nonmemory_operand" "")])) (clobber (reg:CC 17))])] "! optimize_size && ! TARGET_READ_MODIFY_WRITE" [(set (match_dup 2) (match_dup 0)) (parallel [(set (match_dup 2) (match_op_dup 3 [(match_dup 2) (match_dup 1)])) (clobber (reg:CC 17))]) (set (match_dup 0) (match_dup 2))] "") (define_peephole2 [(match_scratch:SI 2 "r") (parallel [(set (match_operand:SI 0 "memory_operand" "") (match_operator:SI 3 "arith_or_logical_operator" [(match_operand:SI 1 "nonmemory_operand" "") (match_dup 0)])) (clobber (reg:CC 17))])] "! optimize_size && ! TARGET_READ_MODIFY_WRITE" [(set (match_dup 2) (match_dup 0)) (parallel [(set (match_dup 2) (match_op_dup 3 [(match_dup 1) (match_dup 2)])) (clobber (reg:CC 17))]) (set (match_dup 0) (match_dup 2))] "") ;; Attempt to always use XOR for zeroing registers. (define_peephole2 [(set (match_operand 0 "register_operand" "") (const_int 0))] "(GET_MODE (operands[0]) == QImode || GET_MODE (operands[0]) == HImode || GET_MODE (operands[0]) == SImode || (GET_MODE (operands[0]) == DImode && TARGET_64BIT)) && (! TARGET_USE_MOV0 || optimize_size) && peep2_regno_dead_p (0, FLAGS_REG)" [(parallel [(set (match_dup 0) (const_int 0)) (clobber (reg:CC 17))])] "operands[0] = gen_lowpart (GET_MODE (operands[0]) == DImode ? DImode : SImode, operands[0]);") (define_peephole2 [(set (strict_low_part (match_operand 0 "register_operand" "")) (const_int 0))] "(GET_MODE (operands[0]) == QImode || GET_MODE (operands[0]) == HImode) && (! TARGET_USE_MOV0 || optimize_size) && peep2_regno_dead_p (0, FLAGS_REG)" [(parallel [(set (strict_low_part (match_dup 0)) (const_int 0)) (clobber (reg:CC 17))])]) ;; For HI and SI modes, or $-1,reg is smaller than mov $-1,reg. (define_peephole2 [(set (match_operand 0 "register_operand" "") (const_int -1))] "(GET_MODE (operands[0]) == HImode || GET_MODE (operands[0]) == SImode || (GET_MODE (operands[0]) == DImode && TARGET_64BIT)) && (optimize_size || TARGET_PENTIUM) && peep2_regno_dead_p (0, FLAGS_REG)" [(parallel [(set (match_dup 0) (const_int -1)) (clobber (reg:CC 17))])] "operands[0] = gen_lowpart (GET_MODE (operands[0]) == DImode ? DImode : SImode, operands[0]);") ;; Attempt to convert simple leas to adds. These can be created by ;; move expanders. (define_peephole2 [(set (match_operand:SI 0 "register_operand" "") (plus:SI (match_dup 0) (match_operand:SI 1 "nonmemory_operand" "")))] "peep2_regno_dead_p (0, FLAGS_REG)" [(parallel [(set (match_dup 0) (plus:SI (match_dup 0) (match_dup 1))) (clobber (reg:CC 17))])] "") (define_peephole2 [(set (match_operand:SI 0 "register_operand" "") (subreg:SI (plus:DI (match_operand:DI 1 "register_operand" "") (match_operand:DI 2 "nonmemory_operand" "")) 0))] "peep2_regno_dead_p (0, FLAGS_REG) && REGNO (operands[0]) == REGNO (operands[1])" [(parallel [(set (match_dup 0) (plus:SI (match_dup 0) (match_dup 2))) (clobber (reg:CC 17))])] "operands[2] = gen_lowpart (SImode, operands[2]);") (define_peephole2 [(set (match_operand:DI 0 "register_operand" "") (plus:DI (match_dup 0) (match_operand:DI 1 "x86_64_general_operand" "")))] "peep2_regno_dead_p (0, FLAGS_REG)" [(parallel [(set (match_dup 0) (plus:DI (match_dup 0) (match_dup 1))) (clobber (reg:CC 17))])] "") (define_peephole2 [(set (match_operand:SI 0 "register_operand" "") (mult:SI (match_dup 0) (match_operand:SI 1 "const_int_operand" "")))] "exact_log2 (INTVAL (operands[1])) >= 0 && peep2_regno_dead_p (0, FLAGS_REG)" [(parallel [(set (match_dup 0) (ashift:SI (match_dup 0) (match_dup 2))) (clobber (reg:CC 17))])] "operands[2] = GEN_INT (exact_log2 (INTVAL (operands[1])));") (define_peephole2 [(set (match_operand:DI 0 "register_operand" "") (mult:DI (match_dup 0) (match_operand:DI 1 "const_int_operand" "")))] "exact_log2 (INTVAL (operands[1])) >= 0 && peep2_regno_dead_p (0, FLAGS_REG)" [(parallel [(set (match_dup 0) (ashift:DI (match_dup 0) (match_dup 2))) (clobber (reg:CC 17))])] "operands[2] = GEN_INT (exact_log2 (INTVAL (operands[1])));") (define_peephole2 [(set (match_operand:SI 0 "register_operand" "") (subreg:SI (mult:DI (match_operand:DI 1 "register_operand" "") (match_operand:DI 2 "const_int_operand" "")) 0))] "exact_log2 (INTVAL (operands[2])) >= 0 && REGNO (operands[0]) == REGNO (operands[1]) && peep2_regno_dead_p (0, FLAGS_REG)" [(parallel [(set (match_dup 0) (ashift:SI (match_dup 0) (match_dup 2))) (clobber (reg:CC 17))])] "operands[2] = GEN_INT (exact_log2 (INTVAL (operands[2])));") ;; The ESP adjustments can be done by the push and pop instructions. Resulting ;; code is shorter, since push is only 1 byte, while add imm, %esp 3 bytes. On ;; many CPUs it is also faster, since special hardware to avoid esp ;; dependencies is present. ;; While some of these conversions may be done using splitters, we use peepholes ;; in order to allow combine_stack_adjustments pass to see nonobfuscated RTL. ;; Convert prologue esp subtractions to push. ;; We need register to push. In order to keep verify_flow_info happy we have ;; two choices ;; - use scratch and clobber it in order to avoid dependencies ;; - use already live register ;; We can't use the second way right now, since there is no reliable way how to ;; verify that given register is live. First choice will also most likely in ;; fewer dependencies. On the place of esp adjustments it is very likely that ;; call clobbered registers are dead. We may want to use base pointer as an ;; alternative when no register is available later. (define_peephole2 [(match_scratch:SI 0 "r") (parallel [(set (reg:SI 7) (plus:SI (reg:SI 7) (const_int -4))) (clobber (reg:CC 17)) (clobber (mem:BLK (scratch)))])] "optimize_size || !TARGET_SUB_ESP_4" [(clobber (match_dup 0)) (parallel [(set (mem:SI (pre_dec:SI (reg:SI 7))) (match_dup 0)) (clobber (mem:BLK (scratch)))])]) (define_peephole2 [(match_scratch:SI 0 "r") (parallel [(set (reg:SI 7) (plus:SI (reg:SI 7) (const_int -8))) (clobber (reg:CC 17)) (clobber (mem:BLK (scratch)))])] "optimize_size || !TARGET_SUB_ESP_8" [(clobber (match_dup 0)) (set (mem:SI (pre_dec:SI (reg:SI 7))) (match_dup 0)) (parallel [(set (mem:SI (pre_dec:SI (reg:SI 7))) (match_dup 0)) (clobber (mem:BLK (scratch)))])]) ;; Convert esp subtractions to push. (define_peephole2 [(match_scratch:SI 0 "r") (parallel [(set (reg:SI 7) (plus:SI (reg:SI 7) (const_int -4))) (clobber (reg:CC 17))])] "optimize_size || !TARGET_SUB_ESP_4" [(clobber (match_dup 0)) (set (mem:SI (pre_dec:SI (reg:SI 7))) (match_dup 0))]) (define_peephole2 [(match_scratch:SI 0 "r") (parallel [(set (reg:SI 7) (plus:SI (reg:SI 7) (const_int -8))) (clobber (reg:CC 17))])] "optimize_size || !TARGET_SUB_ESP_8" [(clobber (match_dup 0)) (set (mem:SI (pre_dec:SI (reg:SI 7))) (match_dup 0)) (set (mem:SI (pre_dec:SI (reg:SI 7))) (match_dup 0))]) ;; Convert epilogue deallocator to pop. (define_peephole2 [(match_scratch:SI 0 "r") (parallel [(set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4))) (clobber (reg:CC 17)) (clobber (mem:BLK (scratch)))])] "optimize_size || !TARGET_ADD_ESP_4" [(parallel [(set (match_dup 0) (mem:SI (reg:SI 7))) (set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4))) (clobber (mem:BLK (scratch)))])] "") ;; Two pops case is tricky, since pop causes dependency on destination register. ;; We use two registers if available. (define_peephole2 [(match_scratch:SI 0 "r") (match_scratch:SI 1 "r") (parallel [(set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 8))) (clobber (reg:CC 17)) (clobber (mem:BLK (scratch)))])] "optimize_size || !TARGET_ADD_ESP_8" [(parallel [(set (match_dup 0) (mem:SI (reg:SI 7))) (set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4))) (clobber (mem:BLK (scratch)))]) (parallel [(set (match_dup 1) (mem:SI (reg:SI 7))) (set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4)))])] "") (define_peephole2 [(match_scratch:SI 0 "r") (parallel [(set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 8))) (clobber (reg:CC 17)) (clobber (mem:BLK (scratch)))])] "optimize_size" [(parallel [(set (match_dup 0) (mem:SI (reg:SI 7))) (set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4))) (clobber (mem:BLK (scratch)))]) (parallel [(set (match_dup 0) (mem:SI (reg:SI 7))) (set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4)))])] "") ;; Convert esp additions to pop. (define_peephole2 [(match_scratch:SI 0 "r") (parallel [(set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4))) (clobber (reg:CC 17))])] "" [(parallel [(set (match_dup 0) (mem:SI (reg:SI 7))) (set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4)))])] "") ;; Two pops case is tricky, since pop causes dependency on destination register. ;; We use two registers if available. (define_peephole2 [(match_scratch:SI 0 "r") (match_scratch:SI 1 "r") (parallel [(set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 8))) (clobber (reg:CC 17))])] "" [(parallel [(set (match_dup 0) (mem:SI (reg:SI 7))) (set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4)))]) (parallel [(set (match_dup 1) (mem:SI (reg:SI 7))) (set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4)))])] "") (define_peephole2 [(match_scratch:SI 0 "r") (parallel [(set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 8))) (clobber (reg:CC 17))])] "optimize_size" [(parallel [(set (match_dup 0) (mem:SI (reg:SI 7))) (set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4)))]) (parallel [(set (match_dup 0) (mem:SI (reg:SI 7))) (set (reg:SI 7) (plus:SI (reg:SI 7) (const_int 4)))])] "") ;; Convert compares with 1 to shorter inc/dec operations when CF is not ;; required and register dies. Similarly for 128 to plus -128. (define_peephole2 [(set (match_operand 0 "flags_reg_operand" "") (match_operator 1 "compare_operator" [(match_operand 2 "register_operand" "") (match_operand 3 "const_int_operand" "")]))] "(INTVAL (operands[3]) == -1 || INTVAL (operands[3]) == 1 || INTVAL (operands[3]) == 128) && ix86_match_ccmode (insn, CCGCmode) && peep2_reg_dead_p (1, operands[2])" [(parallel [(set (match_dup 0) (match_op_dup 1 [(match_dup 2) (match_dup 3)])) (clobber (match_dup 2))])] "") (define_peephole2 [(match_scratch:DI 0 "r") (parallel [(set (reg:DI 7) (plus:DI (reg:DI 7) (const_int -8))) (clobber (reg:CC 17)) (clobber (mem:BLK (scratch)))])] "optimize_size || !TARGET_SUB_ESP_4" [(clobber (match_dup 0)) (parallel [(set (mem:DI (pre_dec:DI (reg:DI 7))) (match_dup 0)) (clobber (mem:BLK (scratch)))])]) (define_peephole2 [(match_scratch:DI 0 "r") (parallel [(set (reg:DI 7) (plus:DI (reg:DI 7) (const_int -16))) (clobber (reg:CC 17)) (clobber (mem:BLK (scratch)))])] "optimize_size || !TARGET_SUB_ESP_8" [(clobber (match_dup 0)) (set (mem:DI (pre_dec:DI (reg:DI 7))) (match_dup 0)) (parallel [(set (mem:DI (pre_dec:DI (reg:DI 7))) (match_dup 0)) (clobber (mem:BLK (scratch)))])]) ;; Convert esp subtractions to push. (define_peephole2 [(match_scratch:DI 0 "r") (parallel [(set (reg:DI 7) (plus:DI (reg:DI 7) (const_int -8))) (clobber (reg:CC 17))])] "optimize_size || !TARGET_SUB_ESP_4" [(clobber (match_dup 0)) (set (mem:DI (pre_dec:DI (reg:DI 7))) (match_dup 0))]) (define_peephole2 [(match_scratch:DI 0 "r") (parallel [(set (reg:DI 7) (plus:DI (reg:DI 7) (const_int -16))) (clobber (reg:CC 17))])] "optimize_size || !TARGET_SUB_ESP_8" [(clobber (match_dup 0)) (set (mem:DI (pre_dec:DI (reg:DI 7))) (match_dup 0)) (set (mem:DI (pre_dec:DI (reg:DI 7))) (match_dup 0))]) ;; Convert epilogue deallocator to pop. (define_peephole2 [(match_scratch:DI 0 "r") (parallel [(set (reg:DI 7) (plus:DI (reg:DI 7) (const_int 8))) (clobber (reg:CC 17)) (clobber (mem:BLK (scratch)))])] "optimize_size || !TARGET_ADD_ESP_4" [(parallel [(set (match_dup 0) (mem:DI (reg:DI 7))) (set (reg:DI 7) (plus:DI (reg:DI 7) (const_int 8))) (clobber (mem:BLK (scratch)))])] "") ;; Two pops case is tricky, since pop causes dependency on destination register. ;; We use two registers if available. (define_peephole2 [(match_scratch:DI 0 "r") (match_scratch:DI 1 "r") (parallel [(set (reg:DI 7) (plus:DI (reg:DI 7) (const_int 16))) (clobber (reg:CC 17)) (clobber (mem:BLK (scratch)))])] "optimize_size || !TARGET_ADD_ESP_8" [(parallel [(set (match_dup 0) (mem:DI (reg:DI 7))) (set (reg:DI 7) (plus:DI (reg:DI 7) (const_int 8))) (clobber (mem:BLK (scratch)))]) (parallel [(set (match_dup 1) (mem:DI (reg:DI 7))) (set (reg:DI 7) (plus:DI (reg:DI 7) (const_int 8)))])] "") (define_peephole2 [(match_scratch:DI 0 "r") (parallel [(set (reg:DI 7) (plus:DI (reg:DI 7) (const_int 16))) (clobber (reg:CC 17)) (clobber (mem:BLK (scratch)))])] "optimize_size" [(parallel [(set (match_dup 0) (mem:DI (reg:DI 7))) (set (reg:DI 7) (plus:DI (reg:DI 7) (const_int 8))) (clobber (mem:BLK (scratch)))]) (parallel [(set (match_dup 0) (mem:DI (reg:DI 7))) (set (reg:DI 7) (plus:DI (reg:DI 7) (const_int 8)))])] "") ;; Convert esp additions to pop. (define_peephole2 [(match_scratch:DI 0 "r") (parallel [(set (reg:DI 7) (plus:DI (reg:DI 7) (const_int 8))) (clobber (reg:CC 17))])] "" [(parallel [(set (match_dup 0) (mem:DI (reg:DI 7))) (set (reg:DI 7) (plus:DI (reg:DI 7) (const_int 8)))])] "") ;; Two pops case is tricky, since pop causes dependency on destination register. ;; We use two registers if available. (define_peephole2 [(match_scratch:DI 0 "r") (match_scratch:DI 1 "r") (parallel [(set (reg:DI 7) (plus:DI (reg:DI 7) (const_int 16))) (clobber (reg:CC 17))])] "" [(parallel [(set (match_dup 0) (mem:DI (reg:DI 7))) (set (reg:DI 7) (plus:DI (reg:DI 7) (const_int 8)))]) (parallel [(set (match_dup 1) (mem:DI (reg:DI 7))) (set (reg:DI 7) (plus:DI (reg:DI 7) (const_int 8)))])] "") (define_peephole2 [(match_scratch:DI 0 "r") (parallel [(set (reg:DI 7) (plus:DI (reg:DI 7) (const_int 16))) (clobber (reg:CC 17))])] "optimize_size" [(parallel [(set (match_dup 0) (mem:DI (reg:DI 7))) (set (reg:DI 7) (plus:DI (reg:DI 7) (const_int 8)))]) (parallel [(set (match_dup 0) (mem:DI (reg:DI 7))) (set (reg:DI 7) (plus:DI (reg:DI 7) (const_int 8)))])] "") ;; Imul $32bit_imm, mem, reg is vector decoded, while ;; imul $32bit_imm, reg, reg is direct decoded. (define_peephole2 [(match_scratch:DI 3 "r") (parallel [(set (match_operand:DI 0 "register_operand" "") (mult:DI (match_operand:DI 1 "memory_operand" "") (match_operand:DI 2 "immediate_operand" ""))) (clobber (reg:CC 17))])] "TARGET_K8 && !optimize_size && (GET_CODE (operands[2]) != CONST_INT || !CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'K'))" [(set (match_dup 3) (match_dup 1)) (parallel [(set (match_dup 0) (mult:DI (match_dup 3) (match_dup 2))) (clobber (reg:CC 17))])] "") (define_peephole2 [(match_scratch:SI 3 "r") (parallel [(set (match_operand:SI 0 "register_operand" "") (mult:SI (match_operand:SI 1 "memory_operand" "") (match_operand:SI 2 "immediate_operand" ""))) (clobber (reg:CC 17))])] "TARGET_K8 && !optimize_size && (GET_CODE (operands[2]) != CONST_INT || !CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'K'))" [(set (match_dup 3) (match_dup 1)) (parallel [(set (match_dup 0) (mult:SI (match_dup 3) (match_dup 2))) (clobber (reg:CC 17))])] "") (define_peephole2 [(match_scratch:SI 3 "r") (parallel [(set (match_operand:DI 0 "register_operand" "") (zero_extend:DI (mult:SI (match_operand:SI 1 "memory_operand" "") (match_operand:SI 2 "immediate_operand" "")))) (clobber (reg:CC 17))])] "TARGET_K8 && !optimize_size && (GET_CODE (operands[2]) != CONST_INT || !CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'K'))" [(set (match_dup 3) (match_dup 1)) (parallel [(set (match_dup 0) (zero_extend:DI (mult:SI (match_dup 3) (match_dup 2)))) (clobber (reg:CC 17))])] "") ;; imul $8/16bit_imm, regmem, reg is vector decoded. ;; Convert it into imul reg, reg ;; It would be better to force assembler to encode instruction using long ;; immediate, but there is apparently no way to do so. (define_peephole2 [(parallel [(set (match_operand:DI 0 "register_operand" "") (mult:DI (match_operand:DI 1 "nonimmediate_operand" "") (match_operand:DI 2 "const_int_operand" ""))) (clobber (reg:CC 17))]) (match_scratch:DI 3 "r")] "TARGET_K8 && !optimize_size && CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'K')" [(set (match_dup 3) (match_dup 2)) (parallel [(set (match_dup 0) (mult:DI (match_dup 0) (match_dup 3))) (clobber (reg:CC 17))])] { if (!rtx_equal_p (operands[0], operands[1])) emit_move_insn (operands[0], operands[1]); }) (define_peephole2 [(parallel [(set (match_operand:SI 0 "register_operand" "") (mult:SI (match_operand:SI 1 "nonimmediate_operand" "") (match_operand:SI 2 "const_int_operand" ""))) (clobber (reg:CC 17))]) (match_scratch:SI 3 "r")] "TARGET_K8 && !optimize_size && CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'K')" [(set (match_dup 3) (match_dup 2)) (parallel [(set (match_dup 0) (mult:SI (match_dup 0) (match_dup 3))) (clobber (reg:CC 17))])] { if (!rtx_equal_p (operands[0], operands[1])) emit_move_insn (operands[0], operands[1]); }) (define_peephole2 [(parallel [(set (match_operand:HI 0 "register_operand" "") (mult:HI (match_operand:HI 1 "nonimmediate_operand" "") (match_operand:HI 2 "immediate_operand" ""))) (clobber (reg:CC 17))]) (match_scratch:HI 3 "r")] "TARGET_K8 && !optimize_size" [(set (match_dup 3) (match_dup 2)) (parallel [(set (match_dup 0) (mult:HI (match_dup 0) (match_dup 3))) (clobber (reg:CC 17))])] { if (!rtx_equal_p (operands[0], operands[1])) emit_move_insn (operands[0], operands[1]); }) ;; Call-value patterns last so that the wildcard operand does not ;; disrupt insn-recog's switch tables. (define_insn "*call_value_pop_0" [(set (match_operand 0 "" "") (call (mem:QI (match_operand:SI 1 "constant_call_address_operand" "")) (match_operand:SI 2 "" ""))) (set (reg:SI 7) (plus:SI (reg:SI 7) (match_operand:SI 3 "immediate_operand" "")))] "!TARGET_64BIT" { if (SIBLING_CALL_P (insn)) return "jmp\t%P1"; else return "call\t%P1"; } [(set_attr "type" "callv")]) (define_insn "*call_value_pop_1" [(set (match_operand 0 "" "") (call (mem:QI (match_operand:SI 1 "call_insn_operand" "rsm")) (match_operand:SI 2 "" ""))) (set (reg:SI 7) (plus:SI (reg:SI 7) (match_operand:SI 3 "immediate_operand" "i")))] "!TARGET_64BIT" { if (constant_call_address_operand (operands[1], QImode)) { if (SIBLING_CALL_P (insn)) return "jmp\t%P1"; else return "call\t%P1"; } if (SIBLING_CALL_P (insn)) return "jmp\t%A1"; else return "call\t%A1"; } [(set_attr "type" "callv")]) (define_insn "*call_value_0" [(set (match_operand 0 "" "") (call (mem:QI (match_operand:SI 1 "constant_call_address_operand" "")) (match_operand:SI 2 "" "")))] "!TARGET_64BIT" { if (SIBLING_CALL_P (insn)) return "jmp\t%P1"; else return "call\t%P1"; } [(set_attr "type" "callv")]) (define_insn "*call_value_0_rex64" [(set (match_operand 0 "" "") (call (mem:QI (match_operand:DI 1 "constant_call_address_operand" "")) (match_operand:DI 2 "const_int_operand" "")))] "TARGET_64BIT" { if (SIBLING_CALL_P (insn)) return "jmp\t%P1"; else return "call\t%P1"; } [(set_attr "type" "callv")]) (define_insn "*call_value_1" [(set (match_operand 0 "" "") (call (mem:QI (match_operand:SI 1 "call_insn_operand" "rsm")) (match_operand:SI 2 "" "")))] "!SIBLING_CALL_P (insn) && !TARGET_64BIT" { if (constant_call_address_operand (operands[1], QImode)) return "call\t%P1"; return "call\t%A1"; } [(set_attr "type" "callv")]) (define_insn "*sibcall_value_1" [(set (match_operand 0 "" "") (call (mem:QI (match_operand:SI 1 "sibcall_insn_operand" "s,c,d,a")) (match_operand:SI 2 "" "")))] "SIBLING_CALL_P (insn) && !TARGET_64BIT" { if (constant_call_address_operand (operands[1], QImode)) return "jmp\t%P1"; return "jmp\t%A1"; } [(set_attr "type" "callv")]) (define_insn "*call_value_1_rex64" [(set (match_operand 0 "" "") (call (mem:QI (match_operand:DI 1 "call_insn_operand" "rsm")) (match_operand:DI 2 "" "")))] "!SIBLING_CALL_P (insn) && TARGET_64BIT" { if (constant_call_address_operand (operands[1], QImode)) return "call\t%P1"; return "call\t%A1"; } [(set_attr "type" "callv")]) (define_insn "*sibcall_value_1_rex64" [(set (match_operand 0 "" "") (call (mem:QI (match_operand:DI 1 "constant_call_address_operand" "")) (match_operand:DI 2 "" "")))] "SIBLING_CALL_P (insn) && TARGET_64BIT" "jmp\t%P1" [(set_attr "type" "callv")]) (define_insn "*sibcall_value_1_rex64_v" [(set (match_operand 0 "" "") (call (mem:QI (reg:DI 40)) (match_operand:DI 1 "" "")))] "SIBLING_CALL_P (insn) && TARGET_64BIT" "jmp\t*%%r11" [(set_attr "type" "callv")]) (define_insn "trap" [(trap_if (const_int 1) (const_int 5))] "" "int\t$5") ;;; ix86 doesn't have conditional trap instructions, but we fake them ;;; for the sake of bounds checking. By emitting bounds checks as ;;; conditional traps rather than as conditional jumps around ;;; unconditional traps we avoid introducing spurious basic-block ;;; boundaries and facilitate elimination of redundant checks. In ;;; honor of the too-inflexible-for-BPs `bound' instruction, we use ;;; interrupt 5. ;;; ;;; FIXME: Static branch prediction rules for ix86 are such that ;;; forward conditional branches predict as untaken. As implemented ;;; below, pseudo conditional traps violate that rule. We should use ;;; .pushsection/.popsection to place all of the `int 5's in a special ;;; section loaded at the end of the text segment and branch forward ;;; there on bounds-failure, and then jump back immediately (in case ;;; the system chooses to ignore bounds violations, or to report ;;; violations and continue execution). (define_expand "conditional_trap" [(trap_if (match_operator 0 "comparison_operator" [(match_dup 2) (const_int 0)]) (match_operand 1 "const_int_operand" ""))] "" { emit_insn (gen_rtx_TRAP_IF (VOIDmode, ix86_expand_compare (GET_CODE (operands[0]), NULL, NULL), operands[1])); DONE; }) (define_insn "*conditional_trap_1" [(trap_if (match_operator 0 "comparison_operator" [(reg 17) (const_int 0)]) (match_operand 1 "const_int_operand" ""))] "" { operands[2] = gen_label_rtx (); output_asm_insn ("j%c0\t%l2\; int\t%1", operands); (*targetm.asm_out.internal_label) (asm_out_file, "L", CODE_LABEL_NUMBER (operands[2])); RET; }) ;; Pentium III SIMD instructions. ;; Moves for SSE/MMX regs. (define_insn "*movv4sf_internal" [(set (match_operand:V4SF 0 "nonimmediate_operand" "=x,x,m") (match_operand:V4SF 1 "vector_move_operand" "C,xm,x"))] "TARGET_SSE && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "@ xorps\t%0, %0 movaps\t{%1, %0|%0, %1} movaps\t{%1, %0|%0, %1}" [(set_attr "type" "ssemov") (set_attr "mode" "V4SF")]) (define_split [(set (match_operand:V4SF 0 "register_operand" "") (match_operand:V4SF 1 "zero_extended_scalar_load_operand" ""))] "TARGET_SSE && reload_completed" [(set (match_dup 0) (vec_merge:V4SF (vec_duplicate:V4SF (match_dup 1)) (match_dup 2) (const_int 1)))] { operands[1] = simplify_gen_subreg (SFmode, operands[1], V4SFmode, 0); operands[2] = CONST0_RTX (V4SFmode); }) (define_insn "*movv4si_internal" [(set (match_operand:V4SI 0 "nonimmediate_operand" "=x,x,m") (match_operand:V4SI 1 "vector_move_operand" "C,xm,x"))] "TARGET_SSE && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" { switch (which_alternative) { case 0: if (get_attr_mode (insn) == MODE_V4SF) return "xorps\t%0, %0"; else return "pxor\t%0, %0"; case 1: case 2: if (get_attr_mode (insn) == MODE_V4SF) return "movaps\t{%1, %0|%0, %1}"; else return "movdqa\t{%1, %0|%0, %1}"; default: abort (); } } [(set_attr "type" "ssemov") (set (attr "mode") (cond [(eq_attr "alternative" "0,1") (if_then_else (ne (symbol_ref "optimize_size") (const_int 0)) (const_string "V4SF") (const_string "TI")) (eq_attr "alternative" "2") (if_then_else (ior (ne (symbol_ref "TARGET_SSE_TYPELESS_STORES") (const_int 0)) (ne (symbol_ref "optimize_size") (const_int 0))) (const_string "V4SF") (const_string "TI"))] (const_string "TI")))]) (define_insn "*movv2di_internal" [(set (match_operand:V2DI 0 "nonimmediate_operand" "=x,x,m") (match_operand:V2DI 1 "vector_move_operand" "C,xm,x"))] "TARGET_SSE && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" { switch (which_alternative) { case 0: if (get_attr_mode (insn) == MODE_V4SF) return "xorps\t%0, %0"; else return "pxor\t%0, %0"; case 1: case 2: if (get_attr_mode (insn) == MODE_V4SF) return "movaps\t{%1, %0|%0, %1}"; else return "movdqa\t{%1, %0|%0, %1}"; default: abort (); } } [(set_attr "type" "ssemov") (set (attr "mode") (cond [(eq_attr "alternative" "0,1") (if_then_else (ne (symbol_ref "optimize_size") (const_int 0)) (const_string "V4SF") (const_string "TI")) (eq_attr "alternative" "2") (if_then_else (ior (ne (symbol_ref "TARGET_SSE_TYPELESS_STORES") (const_int 0)) (ne (symbol_ref "optimize_size") (const_int 0))) (const_string "V4SF") (const_string "TI"))] (const_string "TI")))]) (define_split [(set (match_operand:V2DF 0 "register_operand" "") (match_operand:V2DF 1 "zero_extended_scalar_load_operand" ""))] "TARGET_SSE2 && reload_completed" [(set (match_dup 0) (vec_merge:V2DF (vec_duplicate:V2DF (match_dup 1)) (match_dup 2) (const_int 1)))] { operands[1] = simplify_gen_subreg (DFmode, operands[1], V2DFmode, 0); operands[2] = CONST0_RTX (V2DFmode); }) (define_insn "*movv2si_internal" [(set (match_operand:V2SI 0 "nonimmediate_operand" "=y,y ,m,!y,!*Y,*x,?*x,?m") (match_operand:V2SI 1 "vector_move_operand" "C ,ym,y,*Y,y ,C ,*xm,*x"))] "TARGET_MMX && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "@ pxor\t%0, %0 movq\t{%1, %0|%0, %1} movq\t{%1, %0|%0, %1} movdq2q\t{%1, %0|%0, %1} movq2dq\t{%1, %0|%0, %1} pxor\t%0, %0 movq\t{%1, %0|%0, %1} movq\t{%1, %0|%0, %1}" [(set_attr "type" "mmxmov,mmxmov,mmxmov,ssecvt,ssecvt,ssemov,ssemov,ssemov") (set_attr "mode" "DI")]) (define_insn "*movv4hi_internal" [(set (match_operand:V4HI 0 "nonimmediate_operand" "=y,y ,m,!y,!*Y,*x,?*x,?m") (match_operand:V4HI 1 "vector_move_operand" "C ,ym,y,*Y,y ,C ,*xm,*x"))] "TARGET_MMX && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "@ pxor\t%0, %0 movq\t{%1, %0|%0, %1} movq\t{%1, %0|%0, %1} movdq2q\t{%1, %0|%0, %1} movq2dq\t{%1, %0|%0, %1} pxor\t%0, %0 movq\t{%1, %0|%0, %1} movq\t{%1, %0|%0, %1}" [(set_attr "type" "mmxmov,mmxmov,mmxmov,ssecvt,ssecvt,ssemov,ssemov,ssemov") (set_attr "mode" "DI")]) (define_insn "*movv8qi_internal" [(set (match_operand:V8QI 0 "nonimmediate_operand" "=y,y ,m,!y,!*Y,*x,?*x,?m") (match_operand:V8QI 1 "vector_move_operand" "C ,ym,y,*Y,y ,C ,*xm,*x"))] "TARGET_MMX && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "@ pxor\t%0, %0 movq\t{%1, %0|%0, %1} movq\t{%1, %0|%0, %1} movdq2q\t{%1, %0|%0, %1} movq2dq\t{%1, %0|%0, %1} pxor\t%0, %0 movq\t{%1, %0|%0, %1} movq\t{%1, %0|%0, %1}" [(set_attr "type" "mmxmov,mmxmov,mmxmov,ssecvt,ssecvt,ssemov,ssemov,ssemov") (set_attr "mode" "DI")]) (define_insn "*movv2sf_internal" [(set (match_operand:V2SF 0 "nonimmediate_operand" "=y,y ,m,!y,!*Y,*x,?*x,?m") (match_operand:V2SF 1 "vector_move_operand" "C ,ym,y,*Y,y ,C ,*xm,*x"))] "TARGET_MMX && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "@ pxor\t%0, %0 movq\t{%1, %0|%0, %1} movq\t{%1, %0|%0, %1} movdq2q\t{%1, %0|%0, %1} movq2dq\t{%1, %0|%0, %1} xorps\t%0, %0 movq\t{%1, %0|%0, %1} movq\t{%1, %0|%0, %1}" [(set_attr "type" "mmxmov,mmxmov,mmxmov,ssecvt,ssecvt,ssemov,ssemov,ssemov") (set_attr "mode" "DI")]) (define_expand "movti" [(set (match_operand:TI 0 "nonimmediate_operand" "") (match_operand:TI 1 "nonimmediate_operand" ""))] "TARGET_SSE || TARGET_64BIT" { if (TARGET_64BIT) ix86_expand_move (TImode, operands); else ix86_expand_vector_move (TImode, operands); DONE; }) (define_expand "movtf" [(set (match_operand:TF 0 "nonimmediate_operand" "") (match_operand:TF 1 "nonimmediate_operand" ""))] "TARGET_64BIT" { ix86_expand_move (TFmode, operands); DONE; }) (define_insn "*movv2df_internal" [(set (match_operand:V2DF 0 "nonimmediate_operand" "=x,x,m") (match_operand:V2DF 1 "vector_move_operand" "C,xm,x"))] "TARGET_SSE && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" { switch (which_alternative) { case 0: if (get_attr_mode (insn) == MODE_V4SF) return "xorps\t%0, %0"; else return "xorpd\t%0, %0"; case 1: case 2: if (get_attr_mode (insn) == MODE_V4SF) return "movaps\t{%1, %0|%0, %1}"; else return "movapd\t{%1, %0|%0, %1}"; default: abort (); } } [(set_attr "type" "ssemov") (set (attr "mode") (cond [(eq (symbol_ref "TARGET_SSE2") (const_int 0)) (const_string "V4SF") (eq_attr "alternative" "0,1") (if_then_else (ne (symbol_ref "optimize_size") (const_int 0)) (const_string "V4SF") (const_string "V2DF")) (eq_attr "alternative" "2") (if_then_else (ior (ne (symbol_ref "TARGET_SSE_TYPELESS_STORES") (const_int 0)) (ne (symbol_ref "optimize_size") (const_int 0))) (const_string "V4SF") (const_string "V2DF"))] (const_string "V2DF")))]) (define_insn "*movv8hi_internal" [(set (match_operand:V8HI 0 "nonimmediate_operand" "=x,x,m") (match_operand:V8HI 1 "vector_move_operand" "C,xm,x"))] "TARGET_SSE && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" { switch (which_alternative) { case 0: if (get_attr_mode (insn) == MODE_V4SF) return "xorps\t%0, %0"; else return "pxor\t%0, %0"; case 1: case 2: if (get_attr_mode (insn) == MODE_V4SF) return "movaps\t{%1, %0|%0, %1}"; else return "movdqa\t{%1, %0|%0, %1}"; default: abort (); } } [(set_attr "type" "ssemov") (set (attr "mode") (cond [(eq_attr "alternative" "0,1") (if_then_else (ne (symbol_ref "optimize_size") (const_int 0)) (const_string "V4SF") (const_string "TI")) (eq_attr "alternative" "2") (if_then_else (ior (ne (symbol_ref "TARGET_SSE_TYPELESS_STORES") (const_int 0)) (ne (symbol_ref "optimize_size") (const_int 0))) (const_string "V4SF") (const_string "TI"))] (const_string "TI")))]) (define_insn "*movv16qi_internal" [(set (match_operand:V16QI 0 "nonimmediate_operand" "=x,x,m") (match_operand:V16QI 1 "vector_move_operand" "C,xm,x"))] "TARGET_SSE && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" { switch (which_alternative) { case 0: if (get_attr_mode (insn) == MODE_V4SF) return "xorps\t%0, %0"; else return "pxor\t%0, %0"; case 1: case 2: if (get_attr_mode (insn) == MODE_V4SF) return "movaps\t{%1, %0|%0, %1}"; else return "movdqa\t{%1, %0|%0, %1}"; default: abort (); } } [(set_attr "type" "ssemov") (set (attr "mode") (cond [(eq_attr "alternative" "0,1") (if_then_else (ne (symbol_ref "optimize_size") (const_int 0)) (const_string "V4SF") (const_string "TI")) (eq_attr "alternative" "2") (if_then_else (ior (ne (symbol_ref "TARGET_SSE_TYPELESS_STORES") (const_int 0)) (ne (symbol_ref "optimize_size") (const_int 0))) (const_string "V4SF") (const_string "TI"))] (const_string "TI")))]) (define_expand "movv2df" [(set (match_operand:V2DF 0 "nonimmediate_operand" "") (match_operand:V2DF 1 "nonimmediate_operand" ""))] "TARGET_SSE" { ix86_expand_vector_move (V2DFmode, operands); DONE; }) (define_expand "movv8hi" [(set (match_operand:V8HI 0 "nonimmediate_operand" "") (match_operand:V8HI 1 "nonimmediate_operand" ""))] "TARGET_SSE" { ix86_expand_vector_move (V8HImode, operands); DONE; }) (define_expand "movv16qi" [(set (match_operand:V16QI 0 "nonimmediate_operand" "") (match_operand:V16QI 1 "nonimmediate_operand" ""))] "TARGET_SSE" { ix86_expand_vector_move (V16QImode, operands); DONE; }) (define_expand "movv4sf" [(set (match_operand:V4SF 0 "nonimmediate_operand" "") (match_operand:V4SF 1 "nonimmediate_operand" ""))] "TARGET_SSE" { ix86_expand_vector_move (V4SFmode, operands); DONE; }) (define_expand "movv4si" [(set (match_operand:V4SI 0 "nonimmediate_operand" "") (match_operand:V4SI 1 "nonimmediate_operand" ""))] "TARGET_SSE" { ix86_expand_vector_move (V4SImode, operands); DONE; }) (define_expand "movv2di" [(set (match_operand:V2DI 0 "nonimmediate_operand" "") (match_operand:V2DI 1 "nonimmediate_operand" ""))] "TARGET_SSE" { ix86_expand_vector_move (V2DImode, operands); DONE; }) (define_expand "movv2si" [(set (match_operand:V2SI 0 "nonimmediate_operand" "") (match_operand:V2SI 1 "nonimmediate_operand" ""))] "TARGET_MMX" { ix86_expand_vector_move (V2SImode, operands); DONE; }) (define_expand "movv4hi" [(set (match_operand:V4HI 0 "nonimmediate_operand" "") (match_operand:V4HI 1 "nonimmediate_operand" ""))] "TARGET_MMX" { ix86_expand_vector_move (V4HImode, operands); DONE; }) (define_expand "movv8qi" [(set (match_operand:V8QI 0 "nonimmediate_operand" "") (match_operand:V8QI 1 "nonimmediate_operand" ""))] "TARGET_MMX" { ix86_expand_vector_move (V8QImode, operands); DONE; }) (define_expand "movv2sf" [(set (match_operand:V2SF 0 "nonimmediate_operand" "") (match_operand:V2SF 1 "nonimmediate_operand" ""))] "TARGET_MMX" { ix86_expand_vector_move (V2SFmode, operands); DONE; }) (define_insn "*pushti" [(set (match_operand:TI 0 "push_operand" "=<") (match_operand:TI 1 "register_operand" "x"))] "TARGET_SSE" "#") (define_insn "*pushv2df" [(set (match_operand:V2DF 0 "push_operand" "=<") (match_operand:V2DF 1 "register_operand" "x"))] "TARGET_SSE" "#") (define_insn "*pushv2di" [(set (match_operand:V2DI 0 "push_operand" "=<") (match_operand:V2DI 1 "register_operand" "x"))] "TARGET_SSE" "#") (define_insn "*pushv8hi" [(set (match_operand:V8HI 0 "push_operand" "=<") (match_operand:V8HI 1 "register_operand" "x"))] "TARGET_SSE" "#") (define_insn "*pushv16qi" [(set (match_operand:V16QI 0 "push_operand" "=<") (match_operand:V16QI 1 "register_operand" "x"))] "TARGET_SSE" "#") (define_insn "*pushv4sf" [(set (match_operand:V4SF 0 "push_operand" "=<") (match_operand:V4SF 1 "register_operand" "x"))] "TARGET_SSE" "#") (define_insn "*pushv4si" [(set (match_operand:V4SI 0 "push_operand" "=<") (match_operand:V4SI 1 "register_operand" "x"))] "TARGET_SSE" "#") (define_insn "*pushv2si" [(set (match_operand:V2SI 0 "push_operand" "=<") (match_operand:V2SI 1 "register_operand" "y"))] "TARGET_MMX" "#") (define_insn "*pushv4hi" [(set (match_operand:V4HI 0 "push_operand" "=<") (match_operand:V4HI 1 "register_operand" "y"))] "TARGET_MMX" "#") (define_insn "*pushv8qi" [(set (match_operand:V8QI 0 "push_operand" "=<") (match_operand:V8QI 1 "register_operand" "y"))] "TARGET_MMX" "#") (define_insn "*pushv2sf" [(set (match_operand:V2SF 0 "push_operand" "=<") (match_operand:V2SF 1 "register_operand" "y"))] "TARGET_MMX" "#") (define_split [(set (match_operand 0 "push_operand" "") (match_operand 1 "register_operand" ""))] "!TARGET_64BIT && reload_completed && (SSE_REG_P (operands[1]) || MMX_REG_P (operands[1]))" [(set (reg:SI 7) (plus:SI (reg:SI 7) (match_dup 3))) (set (match_dup 2) (match_dup 1))] "operands[2] = change_address (operands[0], GET_MODE (operands[0]), stack_pointer_rtx); operands[3] = GEN_INT (-GET_MODE_SIZE (GET_MODE (operands[0])));") (define_split [(set (match_operand 0 "push_operand" "") (match_operand 1 "register_operand" ""))] "TARGET_64BIT && reload_completed && (SSE_REG_P (operands[1]) || MMX_REG_P (operands[1]))" [(set (reg:DI 7) (plus:DI (reg:DI 7) (match_dup 3))) (set (match_dup 2) (match_dup 1))] "operands[2] = change_address (operands[0], GET_MODE (operands[0]), stack_pointer_rtx); operands[3] = GEN_INT (-GET_MODE_SIZE (GET_MODE (operands[0])));") (define_insn "*movti_internal" [(set (match_operand:TI 0 "nonimmediate_operand" "=x,x,m") (match_operand:TI 1 "vector_move_operand" "C,xm,x"))] "TARGET_SSE && !TARGET_64BIT && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" { switch (which_alternative) { case 0: if (get_attr_mode (insn) == MODE_V4SF) return "xorps\t%0, %0"; else return "pxor\t%0, %0"; case 1: case 2: if (get_attr_mode (insn) == MODE_V4SF) return "movaps\t{%1, %0|%0, %1}"; else return "movdqa\t{%1, %0|%0, %1}"; default: abort (); } } [(set_attr "type" "ssemov,ssemov,ssemov") (set (attr "mode") (cond [(eq_attr "alternative" "0,1") (if_then_else (ne (symbol_ref "optimize_size") (const_int 0)) (const_string "V4SF") (const_string "TI")) (eq_attr "alternative" "2") (if_then_else (ne (symbol_ref "optimize_size") (const_int 0)) (const_string "V4SF") (const_string "TI"))] (const_string "TI")))]) (define_insn "*movti_rex64" [(set (match_operand:TI 0 "nonimmediate_operand" "=r,o,x,x,xm") (match_operand:TI 1 "general_operand" "riFo,riF,C,xm,x"))] "TARGET_64BIT && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" { switch (which_alternative) { case 0: case 1: return "#"; case 2: if (get_attr_mode (insn) == MODE_V4SF) return "xorps\t%0, %0"; else return "pxor\t%0, %0"; case 3: case 4: if (get_attr_mode (insn) == MODE_V4SF) return "movaps\t{%1, %0|%0, %1}"; else return "movdqa\t{%1, %0|%0, %1}"; default: abort (); } } [(set_attr "type" "*,*,ssemov,ssemov,ssemov") (set (attr "mode") (cond [(eq_attr "alternative" "2,3") (if_then_else (ne (symbol_ref "optimize_size") (const_int 0)) (const_string "V4SF") (const_string "TI")) (eq_attr "alternative" "4") (if_then_else (ior (ne (symbol_ref "TARGET_SSE_TYPELESS_STORES") (const_int 0)) (ne (symbol_ref "optimize_size") (const_int 0))) (const_string "V4SF") (const_string "TI"))] (const_string "DI")))]) (define_insn "*movtf_rex64" [(set (match_operand:TF 0 "nonimmediate_operand" "=r,o,x,x,xm") (match_operand:TF 1 "general_operand" "riFo,riF,C,xm,x"))] "TARGET_64BIT && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" { switch (which_alternative) { case 0: case 1: return "#"; case 2: if (get_attr_mode (insn) == MODE_V4SF) return "xorps\t%0, %0"; else return "pxor\t%0, %0"; case 3: case 4: if (get_attr_mode (insn) == MODE_V4SF) return "movaps\t{%1, %0|%0, %1}"; else return "movdqa\t{%1, %0|%0, %1}"; default: abort (); } } [(set_attr "type" "*,*,ssemov,ssemov,ssemov") (set (attr "mode") (cond [(eq_attr "alternative" "2,3") (if_then_else (ne (symbol_ref "optimize_size") (const_int 0)) (const_string "V4SF") (const_string "TI")) (eq_attr "alternative" "4") (if_then_else (ior (ne (symbol_ref "TARGET_SSE_TYPELESS_STORES") (const_int 0)) (ne (symbol_ref "optimize_size") (const_int 0))) (const_string "V4SF") (const_string "TI"))] (const_string "DI")))]) (define_split [(set (match_operand:TI 0 "nonimmediate_operand" "") (match_operand:TI 1 "general_operand" ""))] "reload_completed && !SSE_REG_P (operands[0]) && !SSE_REG_P (operands[1])" [(const_int 0)] "ix86_split_long_move (operands); DONE;") (define_split [(set (match_operand:TF 0 "nonimmediate_operand" "") (match_operand:TF 1 "general_operand" ""))] "reload_completed && !SSE_REG_P (operands[0]) && !SSE_REG_P (operands[1])" [(const_int 0)] "ix86_split_long_move (operands); DONE;") ;; These two patterns are useful for specifying exactly whether to use ;; movaps or movups (define_expand "sse_movaps" [(set (match_operand:V4SF 0 "nonimmediate_operand" "") (unspec:V4SF [(match_operand:V4SF 1 "nonimmediate_operand" "")] UNSPEC_MOVA))] "TARGET_SSE" { if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) { rtx tmp = gen_reg_rtx (V4SFmode); emit_insn (gen_sse_movaps (tmp, operands[1])); emit_move_insn (operands[0], tmp); DONE; } }) (define_insn "*sse_movaps_1" [(set (match_operand:V4SF 0 "nonimmediate_operand" "=x,m") (unspec:V4SF [(match_operand:V4SF 1 "nonimmediate_operand" "xm,x")] UNSPEC_MOVA))] "TARGET_SSE && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "movaps\t{%1, %0|%0, %1}" [(set_attr "type" "ssemov,ssemov") (set_attr "mode" "V4SF")]) (define_expand "sse_movups" [(set (match_operand:V4SF 0 "nonimmediate_operand" "") (unspec:V4SF [(match_operand:V4SF 1 "nonimmediate_operand" "")] UNSPEC_MOVU))] "TARGET_SSE" { if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) { rtx tmp = gen_reg_rtx (V4SFmode); emit_insn (gen_sse_movups (tmp, operands[1])); emit_move_insn (operands[0], tmp); DONE; } }) (define_insn "*sse_movups_1" [(set (match_operand:V4SF 0 "nonimmediate_operand" "=x,m") (unspec:V4SF [(match_operand:V4SF 1 "nonimmediate_operand" "xm,x")] UNSPEC_MOVU))] "TARGET_SSE && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "movups\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt,ssecvt") (set_attr "mode" "V4SF")]) ;; SSE Strange Moves. (define_insn "sse_movmskps" [(set (match_operand:SI 0 "register_operand" "=r") (unspec:SI [(match_operand:V4SF 1 "register_operand" "x")] UNSPEC_MOVMSK))] "TARGET_SSE" "movmskps\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "V4SF")]) (define_insn "mmx_pmovmskb" [(set (match_operand:SI 0 "register_operand" "=r") (unspec:SI [(match_operand:V8QI 1 "register_operand" "y")] UNSPEC_MOVMSK))] "TARGET_SSE || TARGET_3DNOW_A" "pmovmskb\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "V4SF")]) (define_insn "mmx_maskmovq" [(set (mem:V8QI (match_operand:SI 0 "register_operand" "D")) (unspec:V8QI [(match_operand:V8QI 1 "register_operand" "y") (match_operand:V8QI 2 "register_operand" "y")] UNSPEC_MASKMOV))] "(TARGET_SSE || TARGET_3DNOW_A) && !TARGET_64BIT" ;; @@@ check ordering of operands in intel/nonintel syntax "maskmovq\t{%2, %1|%1, %2}" [(set_attr "type" "mmxcvt") (set_attr "mode" "DI")]) (define_insn "mmx_maskmovq_rex" [(set (mem:V8QI (match_operand:DI 0 "register_operand" "D")) (unspec:V8QI [(match_operand:V8QI 1 "register_operand" "y") (match_operand:V8QI 2 "register_operand" "y")] UNSPEC_MASKMOV))] "(TARGET_SSE || TARGET_3DNOW_A) && TARGET_64BIT" ;; @@@ check ordering of operands in intel/nonintel syntax "maskmovq\t{%2, %1|%1, %2}" [(set_attr "type" "mmxcvt") (set_attr "mode" "DI")]) (define_insn "sse_movntv4sf" [(set (match_operand:V4SF 0 "memory_operand" "=m") (unspec:V4SF [(match_operand:V4SF 1 "register_operand" "x")] UNSPEC_MOVNT))] "TARGET_SSE" "movntps\t{%1, %0|%0, %1}" [(set_attr "type" "ssemov") (set_attr "mode" "V4SF")]) (define_insn "sse_movntdi" [(set (match_operand:DI 0 "memory_operand" "=m") (unspec:DI [(match_operand:DI 1 "register_operand" "y")] UNSPEC_MOVNT))] "TARGET_SSE || TARGET_3DNOW_A" "movntq\t{%1, %0|%0, %1}" [(set_attr "type" "mmxmov") (set_attr "mode" "DI")]) (define_insn "sse_movhlps" [(set (match_operand:V4SF 0 "register_operand" "=x") (vec_merge:V4SF (match_operand:V4SF 1 "register_operand" "0") (vec_select:V4SF (match_operand:V4SF 2 "register_operand" "x") (parallel [(const_int 2) (const_int 3) (const_int 0) (const_int 1)])) (const_int 3)))] "TARGET_SSE" "movhlps\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "V4SF")]) (define_insn "sse_movlhps" [(set (match_operand:V4SF 0 "register_operand" "=x") (vec_merge:V4SF (match_operand:V4SF 1 "register_operand" "0") (vec_select:V4SF (match_operand:V4SF 2 "register_operand" "x") (parallel [(const_int 2) (const_int 3) (const_int 0) (const_int 1)])) (const_int 12)))] "TARGET_SSE" "movlhps\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "V4SF")]) (define_insn "sse_movhps" [(set (match_operand:V4SF 0 "nonimmediate_operand" "=x,m") (vec_merge:V4SF (match_operand:V4SF 1 "nonimmediate_operand" "0,0") (match_operand:V4SF 2 "nonimmediate_operand" "m,x") (const_int 12)))] "TARGET_SSE && (GET_CODE (operands[1]) == MEM || GET_CODE (operands[2]) == MEM)" "movhps\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "V4SF")]) (define_insn "sse_movlps" [(set (match_operand:V4SF 0 "nonimmediate_operand" "=x,m") (vec_merge:V4SF (match_operand:V4SF 1 "nonimmediate_operand" "0,0") (match_operand:V4SF 2 "nonimmediate_operand" "m,x") (const_int 3)))] "TARGET_SSE && (GET_CODE (operands[1]) == MEM || GET_CODE (operands[2]) == MEM)" "movlps\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "V4SF")]) (define_expand "sse_loadss" [(match_operand:V4SF 0 "register_operand" "") (match_operand:SF 1 "memory_operand" "")] "TARGET_SSE" { emit_insn (gen_sse_loadss_1 (operands[0], operands[1], CONST0_RTX (V4SFmode))); DONE; }) (define_insn "sse_loadss_1" [(set (match_operand:V4SF 0 "register_operand" "=x") (vec_merge:V4SF (vec_duplicate:V4SF (match_operand:SF 1 "memory_operand" "m")) (match_operand:V4SF 2 "const0_operand" "X") (const_int 1)))] "TARGET_SSE" "movss\t{%1, %0|%0, %1}" [(set_attr "type" "ssemov") (set_attr "mode" "SF")]) (define_insn "sse_movss" [(set (match_operand:V4SF 0 "register_operand" "=x") (vec_merge:V4SF (match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "register_operand" "x") (const_int 1)))] "TARGET_SSE" "movss\t{%2, %0|%0, %2}" [(set_attr "type" "ssemov") (set_attr "mode" "SF")]) (define_insn "sse_storess" [(set (match_operand:SF 0 "memory_operand" "=m") (vec_select:SF (match_operand:V4SF 1 "register_operand" "x") (parallel [(const_int 0)])))] "TARGET_SSE" "movss\t{%1, %0|%0, %1}" [(set_attr "type" "ssemov") (set_attr "mode" "SF")]) (define_insn "sse_shufps" [(set (match_operand:V4SF 0 "register_operand" "=x") (unspec:V4SF [(match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "nonimmediate_operand" "xm") (match_operand:SI 3 "immediate_operand" "i")] UNSPEC_SHUFFLE))] "TARGET_SSE" ;; @@@ check operand order for intel/nonintel syntax "shufps\t{%3, %2, %0|%0, %2, %3}" [(set_attr "type" "ssecvt") (set_attr "mode" "V4SF")]) ;; SSE arithmetic (define_insn "addv4sf3" [(set (match_operand:V4SF 0 "register_operand" "=x") (plus:V4SF (match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "nonimmediate_operand" "xm")))] "TARGET_SSE" "addps\t{%2, %0|%0, %2}" [(set_attr "type" "sseadd") (set_attr "mode" "V4SF")]) (define_insn "vmaddv4sf3" [(set (match_operand:V4SF 0 "register_operand" "=x") (vec_merge:V4SF (plus:V4SF (match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "nonimmediate_operand" "xm")) (match_dup 1) (const_int 1)))] "TARGET_SSE" "addss\t{%2, %0|%0, %2}" [(set_attr "type" "sseadd") (set_attr "mode" "SF")]) (define_insn "subv4sf3" [(set (match_operand:V4SF 0 "register_operand" "=x") (minus:V4SF (match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "nonimmediate_operand" "xm")))] "TARGET_SSE" "subps\t{%2, %0|%0, %2}" [(set_attr "type" "sseadd") (set_attr "mode" "V4SF")]) (define_insn "vmsubv4sf3" [(set (match_operand:V4SF 0 "register_operand" "=x") (vec_merge:V4SF (minus:V4SF (match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "nonimmediate_operand" "xm")) (match_dup 1) (const_int 1)))] "TARGET_SSE" "subss\t{%2, %0|%0, %2}" [(set_attr "type" "sseadd") (set_attr "mode" "SF")]) (define_insn "mulv4sf3" [(set (match_operand:V4SF 0 "register_operand" "=x") (mult:V4SF (match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "nonimmediate_operand" "xm")))] "TARGET_SSE" "mulps\t{%2, %0|%0, %2}" [(set_attr "type" "ssemul") (set_attr "mode" "V4SF")]) (define_insn "vmmulv4sf3" [(set (match_operand:V4SF 0 "register_operand" "=x") (vec_merge:V4SF (mult:V4SF (match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "nonimmediate_operand" "xm")) (match_dup 1) (const_int 1)))] "TARGET_SSE" "mulss\t{%2, %0|%0, %2}" [(set_attr "type" "ssemul") (set_attr "mode" "SF")]) (define_insn "divv4sf3" [(set (match_operand:V4SF 0 "register_operand" "=x") (div:V4SF (match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "nonimmediate_operand" "xm")))] "TARGET_SSE" "divps\t{%2, %0|%0, %2}" [(set_attr "type" "ssediv") (set_attr "mode" "V4SF")]) (define_insn "vmdivv4sf3" [(set (match_operand:V4SF 0 "register_operand" "=x") (vec_merge:V4SF (div:V4SF (match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "nonimmediate_operand" "xm")) (match_dup 1) (const_int 1)))] "TARGET_SSE" "divss\t{%2, %0|%0, %2}" [(set_attr "type" "ssediv") (set_attr "mode" "SF")]) ;; SSE square root/reciprocal (define_insn "rcpv4sf2" [(set (match_operand:V4SF 0 "register_operand" "=x") (unspec:V4SF [(match_operand:V4SF 1 "nonimmediate_operand" "xm")] UNSPEC_RCP))] "TARGET_SSE" "rcpps\t{%1, %0|%0, %1}" [(set_attr "type" "sse") (set_attr "mode" "V4SF")]) (define_insn "vmrcpv4sf2" [(set (match_operand:V4SF 0 "register_operand" "=x") (vec_merge:V4SF (unspec:V4SF [(match_operand:V4SF 1 "nonimmediate_operand" "xm")] UNSPEC_RCP) (match_operand:V4SF 2 "register_operand" "0") (const_int 1)))] "TARGET_SSE" "rcpss\t{%1, %0|%0, %1}" [(set_attr "type" "sse") (set_attr "mode" "SF")]) (define_insn "rsqrtv4sf2" [(set (match_operand:V4SF 0 "register_operand" "=x") (unspec:V4SF [(match_operand:V4SF 1 "nonimmediate_operand" "xm")] UNSPEC_RSQRT))] "TARGET_SSE" "rsqrtps\t{%1, %0|%0, %1}" [(set_attr "type" "sse") (set_attr "mode" "V4SF")]) (define_insn "vmrsqrtv4sf2" [(set (match_operand:V4SF 0 "register_operand" "=x") (vec_merge:V4SF (unspec:V4SF [(match_operand:V4SF 1 "nonimmediate_operand" "xm")] UNSPEC_RSQRT) (match_operand:V4SF 2 "register_operand" "0") (const_int 1)))] "TARGET_SSE" "rsqrtss\t{%1, %0|%0, %1}" [(set_attr "type" "sse") (set_attr "mode" "SF")]) (define_insn "sqrtv4sf2" [(set (match_operand:V4SF 0 "register_operand" "=x") (sqrt:V4SF (match_operand:V4SF 1 "nonimmediate_operand" "xm")))] "TARGET_SSE" "sqrtps\t{%1, %0|%0, %1}" [(set_attr "type" "sse") (set_attr "mode" "V4SF")]) (define_insn "vmsqrtv4sf2" [(set (match_operand:V4SF 0 "register_operand" "=x") (vec_merge:V4SF (sqrt:V4SF (match_operand:V4SF 1 "nonimmediate_operand" "xm")) (match_operand:V4SF 2 "register_operand" "0") (const_int 1)))] "TARGET_SSE" "sqrtss\t{%1, %0|%0, %1}" [(set_attr "type" "sse") (set_attr "mode" "SF")]) ;; SSE logical operations. ;; SSE defines logical operations on floating point values. This brings ;; interesting challenge to RTL representation where logicals are only valid ;; on integral types. We deal with this by representing the floating point ;; logical as logical on arguments casted to TImode as this is what hardware ;; really does. Unfortunately hardware requires the type information to be ;; present and thus we must avoid subregs from being simplified and eliminated ;; in later compilation phases. ;; ;; We have following variants from each instruction: ;; sse_andsf3 - the operation taking V4SF vector operands ;; and doing TImode cast on them ;; *sse_andsf3_memory - the operation taking one memory operand casted to ;; TImode, since backend insist on eliminating casts ;; on memory operands ;; sse_andti3_sf_1 - the operation taking SF scalar operands. ;; We can not accept memory operand here as instruction reads ;; whole scalar. This is generated only post reload by GCC ;; scalar float operations that expands to logicals (fabs) ;; sse_andti3_sf_2 - the operation taking SF scalar input and TImode ;; memory operand. Eventually combine can be able ;; to synthesize these using splitter. ;; sse2_anddf3, *sse2_anddf3_memory ;; ;; ;; These are not called andti3 etc. because we really really don't want ;; the compiler to widen DImode ands to TImode ands and then try to move ;; into DImode subregs of SSE registers, and them together, and move out ;; of DImode subregs again! ;; SSE1 single precision floating point logical operation (define_expand "sse_andv4sf3" [(set (match_operand:V4SF 0 "register_operand" "") (and:V4SF (match_operand:V4SF 1 "register_operand" "") (match_operand:V4SF 2 "nonimmediate_operand" "")))] "TARGET_SSE" "") (define_insn "*sse_andv4sf3" [(set (match_operand:V4SF 0 "register_operand" "=x") (and:V4SF (match_operand:V4SF 1 "nonimmediate_operand" "%0") (match_operand:V4SF 2 "nonimmediate_operand" "xm")))] "TARGET_SSE && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "andps\t{%2, %0|%0, %2}" [(set_attr "type" "sselog") (set_attr "mode" "V4SF")]) (define_expand "sse_nandv4sf3" [(set (match_operand:V4SF 0 "register_operand" "") (and:V4SF (not:V4SF (match_operand:V4SF 1 "register_operand" "")) (match_operand:V4SF 2 "nonimmediate_operand" "")))] "TARGET_SSE" "") (define_insn "*sse_nandv4sf3" [(set (match_operand:V4SF 0 "register_operand" "=x") (and:V4SF (not:V4SF (match_operand:V4SF 1 "register_operand" "0")) (match_operand:V4SF 2 "nonimmediate_operand" "xm")))] "TARGET_SSE" "andnps\t{%2, %0|%0, %2}" [(set_attr "type" "sselog") (set_attr "mode" "V4SF")]) (define_expand "sse_iorv4sf3" [(set (match_operand:V4SF 0 "register_operand" "") (ior:V4SF (match_operand:V4SF 1 "register_operand" "") (match_operand:V4SF 2 "nonimmediate_operand" "")))] "TARGET_SSE" "") (define_insn "*sse_iorv4sf3" [(set (match_operand:V4SF 0 "register_operand" "=x") (ior:V4SF (match_operand:V4SF 1 "nonimmediate_operand" "%0") (match_operand:V4SF 2 "nonimmediate_operand" "xm")))] "TARGET_SSE && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "orps\t{%2, %0|%0, %2}" [(set_attr "type" "sselog") (set_attr "mode" "V4SF")]) (define_expand "sse_xorv4sf3" [(set (match_operand:V4SF 0 "register_operand" "") (xor:V4SF (match_operand:V4SF 1 "register_operand" "") (match_operand:V4SF 2 "nonimmediate_operand" "")))] "TARGET_SSE" "") (define_insn "*sse_xorv4sf3" [(set (match_operand:V4SF 0 "register_operand" "=x") (xor:V4SF (match_operand:V4SF 1 "nonimmediate_operand" "%0") (match_operand:V4SF 2 "nonimmediate_operand" "xm")))] "TARGET_SSE && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "xorps\t{%2, %0|%0, %2}" [(set_attr "type" "sselog") (set_attr "mode" "V4SF")]) ;; SSE2 double precision floating point logical operation (define_expand "sse2_andv2df3" [(set (match_operand:V2DF 0 "register_operand" "") (and:V2DF (match_operand:V2DF 1 "register_operand" "") (match_operand:V2DF 2 "nonimmediate_operand" "")))] "TARGET_SSE2" "") (define_insn "*sse2_andv2df3" [(set (match_operand:V2DF 0 "register_operand" "=x") (and:V2DF (match_operand:V2DF 1 "nonimmediate_operand" "%0") (match_operand:V2DF 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2 && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "andpd\t{%2, %0|%0, %2}" [(set_attr "type" "sselog") (set_attr "mode" "V2DF")]) (define_expand "sse2_nandv2df3" [(set (match_operand:V2DF 0 "register_operand" "") (and:V2DF (not:V2DF (match_operand:V2DF 1 "register_operand" "")) (match_operand:V2DF 2 "nonimmediate_operand" "")))] "TARGET_SSE2" "") (define_insn "*sse2_nandv2df3" [(set (match_operand:V2DF 0 "register_operand" "=x") (and:V2DF (not:V2DF (match_operand:V2DF 1 "register_operand" "0")) (match_operand:V2DF 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "andnpd\t{%2, %0|%0, %2}" [(set_attr "type" "sselog") (set_attr "mode" "V2DF")]) (define_expand "sse2_iorv2df3" [(set (match_operand:V2DF 0 "register_operand" "") (ior:V2DF (match_operand:V2DF 1 "register_operand" "") (match_operand:V2DF 2 "nonimmediate_operand" "")))] "TARGET_SSE2" "") (define_insn "*sse2_iorv2df3" [(set (match_operand:V2DF 0 "register_operand" "=x") (ior:V2DF (match_operand:V2DF 1 "nonimmediate_operand" "%0") (match_operand:V2DF 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2 && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "orpd\t{%2, %0|%0, %2}" [(set_attr "type" "sselog") (set_attr "mode" "V2DF")]) (define_expand "sse2_xorv2df3" [(set (match_operand:V2DF 0 "register_operand" "") (xor:V2DF (match_operand:V2DF 1 "nonimmediate_operand" "") (match_operand:V2DF 2 "nonimmediate_operand" "")))] "TARGET_SSE2" "") (define_insn "*sse2_xorv2df3" [(set (match_operand:V2DF 0 "register_operand" "=x") (xor:V2DF (match_operand:V2DF 1 "nonimmediate_operand" "%0") (match_operand:V2DF 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2 && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "xorpd\t{%2, %0|%0, %2}" [(set_attr "type" "sselog") (set_attr "mode" "V2DF")]) ;; SSE2 integral logicals. These patterns must always come after floating ;; point ones since we don't want compiler to use integer opcodes on floating ;; point SSE values to avoid matching of subregs in the match_operand. (define_insn "*sse2_andti3" [(set (match_operand:TI 0 "register_operand" "=x") (and:TI (match_operand:TI 1 "nonimmediate_operand" "%0") (match_operand:TI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2 && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "pand\t{%2, %0|%0, %2}" [(set_attr "type" "sselog") (set_attr "mode" "TI")]) (define_insn "sse2_andv2di3" [(set (match_operand:V2DI 0 "register_operand" "=x") (and:V2DI (match_operand:V2DI 1 "nonimmediate_operand" "%0") (match_operand:V2DI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2 && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "pand\t{%2, %0|%0, %2}" [(set_attr "type" "sselog") (set_attr "mode" "TI")]) (define_insn "*sse2_nandti3" [(set (match_operand:TI 0 "register_operand" "=x") (and:TI (not:TI (match_operand:TI 1 "register_operand" "0")) (match_operand:TI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "pandn\t{%2, %0|%0, %2}" [(set_attr "type" "sselog") (set_attr "mode" "TI")]) (define_insn "sse2_nandv2di3" [(set (match_operand:V2DI 0 "register_operand" "=x") (and:V2DI (not:V2DI (match_operand:V2DI 1 "register_operand" "0")) (match_operand:V2DI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2 && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "pandn\t{%2, %0|%0, %2}" [(set_attr "type" "sselog") (set_attr "mode" "TI")]) (define_insn "*sse2_iorti3" [(set (match_operand:TI 0 "register_operand" "=x") (ior:TI (match_operand:TI 1 "nonimmediate_operand" "%0") (match_operand:TI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2 && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "por\t{%2, %0|%0, %2}" [(set_attr "type" "sselog") (set_attr "mode" "TI")]) (define_insn "sse2_iorv2di3" [(set (match_operand:V2DI 0 "register_operand" "=x") (ior:V2DI (match_operand:V2DI 1 "nonimmediate_operand" "%0") (match_operand:V2DI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2 && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "por\t{%2, %0|%0, %2}" [(set_attr "type" "sselog") (set_attr "mode" "TI")]) (define_insn "*sse2_xorti3" [(set (match_operand:TI 0 "register_operand" "=x") (xor:TI (match_operand:TI 1 "nonimmediate_operand" "%0") (match_operand:TI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2 && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "pxor\t{%2, %0|%0, %2}" [(set_attr "type" "sselog") (set_attr "mode" "TI")]) (define_insn "sse2_xorv2di3" [(set (match_operand:V2DI 0 "register_operand" "=x") (xor:V2DI (match_operand:V2DI 1 "nonimmediate_operand" "%0") (match_operand:V2DI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2 && (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != MEM)" "pxor\t{%2, %0|%0, %2}" [(set_attr "type" "sselog") (set_attr "mode" "TI")]) ;; Use xor, but don't show input operands so they aren't live before ;; this insn. (define_insn "sse_clrv4sf" [(set (match_operand:V4SF 0 "register_operand" "=x") (match_operand:V4SF 1 "const0_operand" "X"))] "TARGET_SSE" { if (get_attr_mode (insn) == MODE_TI) return "pxor\t{%0, %0|%0, %0}"; else return "xorps\t{%0, %0|%0, %0}"; } [(set_attr "type" "sselog") (set_attr "memory" "none") (set (attr "mode") (if_then_else (and (and (ne (symbol_ref "TARGET_SSE_LOAD0_BY_PXOR") (const_int 0)) (ne (symbol_ref "TARGET_SSE2") (const_int 0))) (eq (symbol_ref "optimize_size") (const_int 0))) (const_string "TI") (const_string "V4SF")))]) ;; Use xor, but don't show input operands so they aren't live before ;; this insn. (define_insn "sse_clrv2df" [(set (match_operand:V2DF 0 "register_operand" "=x") (unspec:V2DF [(const_int 0)] UNSPEC_NOP))] "TARGET_SSE2" "xorpd\t{%0, %0|%0, %0}" [(set_attr "type" "sselog") (set_attr "memory" "none") (set_attr "mode" "V4SF")]) ;; SSE mask-generating compares (define_insn "maskcmpv4sf3" [(set (match_operand:V4SI 0 "register_operand" "=x") (match_operator:V4SI 3 "sse_comparison_operator" [(match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "register_operand" "x")]))] "TARGET_SSE" "cmp%D3ps\t{%2, %0|%0, %2}" [(set_attr "type" "ssecmp") (set_attr "mode" "V4SF")]) (define_insn "maskncmpv4sf3" [(set (match_operand:V4SI 0 "register_operand" "=x") (not:V4SI (match_operator:V4SI 3 "sse_comparison_operator" [(match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "register_operand" "x")])))] "TARGET_SSE" { if (GET_CODE (operands[3]) == UNORDERED) return "cmpordps\t{%2, %0|%0, %2}"; else return "cmpn%D3ps\t{%2, %0|%0, %2}"; } [(set_attr "type" "ssecmp") (set_attr "mode" "V4SF")]) (define_insn "vmmaskcmpv4sf3" [(set (match_operand:V4SI 0 "register_operand" "=x") (vec_merge:V4SI (match_operator:V4SI 3 "sse_comparison_operator" [(match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "register_operand" "x")]) (subreg:V4SI (match_dup 1) 0) (const_int 1)))] "TARGET_SSE" "cmp%D3ss\t{%2, %0|%0, %2}" [(set_attr "type" "ssecmp") (set_attr "mode" "SF")]) (define_insn "vmmaskncmpv4sf3" [(set (match_operand:V4SI 0 "register_operand" "=x") (vec_merge:V4SI (not:V4SI (match_operator:V4SI 3 "sse_comparison_operator" [(match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "register_operand" "x")])) (subreg:V4SI (match_dup 1) 0) (const_int 1)))] "TARGET_SSE" { if (GET_CODE (operands[3]) == UNORDERED) return "cmpordss\t{%2, %0|%0, %2}"; else return "cmpn%D3ss\t{%2, %0|%0, %2}"; } [(set_attr "type" "ssecmp") (set_attr "mode" "SF")]) (define_insn "sse_comi" [(set (reg:CCFP 17) (compare:CCFP (vec_select:SF (match_operand:V4SF 0 "register_operand" "x") (parallel [(const_int 0)])) (vec_select:SF (match_operand:V4SF 1 "register_operand" "x") (parallel [(const_int 0)]))))] "TARGET_SSE" "comiss\t{%1, %0|%0, %1}" [(set_attr "type" "ssecomi") (set_attr "mode" "SF")]) (define_insn "sse_ucomi" [(set (reg:CCFPU 17) (compare:CCFPU (vec_select:SF (match_operand:V4SF 0 "register_operand" "x") (parallel [(const_int 0)])) (vec_select:SF (match_operand:V4SF 1 "register_operand" "x") (parallel [(const_int 0)]))))] "TARGET_SSE" "ucomiss\t{%1, %0|%0, %1}" [(set_attr "type" "ssecomi") (set_attr "mode" "SF")]) ;; SSE unpack (define_insn "sse_unpckhps" [(set (match_operand:V4SF 0 "register_operand" "=x") (vec_merge:V4SF (vec_select:V4SF (match_operand:V4SF 1 "register_operand" "0") (parallel [(const_int 2) (const_int 0) (const_int 3) (const_int 1)])) (vec_select:V4SF (match_operand:V4SF 2 "register_operand" "x") (parallel [(const_int 0) (const_int 2) (const_int 1) (const_int 3)])) (const_int 5)))] "TARGET_SSE" "unpckhps\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "V4SF")]) (define_insn "sse_unpcklps" [(set (match_operand:V4SF 0 "register_operand" "=x") (vec_merge:V4SF (vec_select:V4SF (match_operand:V4SF 1 "register_operand" "0") (parallel [(const_int 0) (const_int 2) (const_int 1) (const_int 3)])) (vec_select:V4SF (match_operand:V4SF 2 "register_operand" "x") (parallel [(const_int 2) (const_int 0) (const_int 3) (const_int 1)])) (const_int 5)))] "TARGET_SSE" "unpcklps\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "V4SF")]) ;; SSE min/max (define_insn "smaxv4sf3" [(set (match_operand:V4SF 0 "register_operand" "=x") (smax:V4SF (match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "nonimmediate_operand" "xm")))] "TARGET_SSE" "maxps\t{%2, %0|%0, %2}" [(set_attr "type" "sse") (set_attr "mode" "V4SF")]) (define_insn "vmsmaxv4sf3" [(set (match_operand:V4SF 0 "register_operand" "=x") (vec_merge:V4SF (smax:V4SF (match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "nonimmediate_operand" "xm")) (match_dup 1) (const_int 1)))] "TARGET_SSE" "maxss\t{%2, %0|%0, %2}" [(set_attr "type" "sse") (set_attr "mode" "SF")]) (define_insn "sminv4sf3" [(set (match_operand:V4SF 0 "register_operand" "=x") (smin:V4SF (match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "nonimmediate_operand" "xm")))] "TARGET_SSE" "minps\t{%2, %0|%0, %2}" [(set_attr "type" "sse") (set_attr "mode" "V4SF")]) (define_insn "vmsminv4sf3" [(set (match_operand:V4SF 0 "register_operand" "=x") (vec_merge:V4SF (smin:V4SF (match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "nonimmediate_operand" "xm")) (match_dup 1) (const_int 1)))] "TARGET_SSE" "minss\t{%2, %0|%0, %2}" [(set_attr "type" "sse") (set_attr "mode" "SF")]) ;; SSE <-> integer/MMX conversions (define_insn "cvtpi2ps" [(set (match_operand:V4SF 0 "register_operand" "=x") (vec_merge:V4SF (match_operand:V4SF 1 "register_operand" "0") (vec_duplicate:V4SF (float:V2SF (match_operand:V2SI 2 "nonimmediate_operand" "ym"))) (const_int 12)))] "TARGET_SSE" "cvtpi2ps\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "V4SF")]) (define_insn "cvtps2pi" [(set (match_operand:V2SI 0 "register_operand" "=y") (vec_select:V2SI (fix:V4SI (match_operand:V4SF 1 "nonimmediate_operand" "xm")) (parallel [(const_int 0) (const_int 1)])))] "TARGET_SSE" "cvtps2pi\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "V4SF")]) (define_insn "cvttps2pi" [(set (match_operand:V2SI 0 "register_operand" "=y") (vec_select:V2SI (unspec:V4SI [(match_operand:V4SF 1 "nonimmediate_operand" "xm")] UNSPEC_FIX) (parallel [(const_int 0) (const_int 1)])))] "TARGET_SSE" "cvttps2pi\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "SF")]) (define_insn "cvtsi2ss" [(set (match_operand:V4SF 0 "register_operand" "=x,x") (vec_merge:V4SF (match_operand:V4SF 1 "register_operand" "0,0") (vec_duplicate:V4SF (float:SF (match_operand:SI 2 "nonimmediate_operand" "r,rm"))) (const_int 14)))] "TARGET_SSE" "cvtsi2ss\t{%2, %0|%0, %2}" [(set_attr "type" "sseicvt") (set_attr "athlon_decode" "vector,double") (set_attr "mode" "SF")]) (define_insn "cvtsi2ssq" [(set (match_operand:V4SF 0 "register_operand" "=x,x") (vec_merge:V4SF (match_operand:V4SF 1 "register_operand" "0,0") (vec_duplicate:V4SF (float:SF (match_operand:DI 2 "nonimmediate_operand" "r,rm"))) (const_int 14)))] "TARGET_SSE && TARGET_64BIT" "cvtsi2ssq\t{%2, %0|%0, %2}" [(set_attr "type" "sseicvt") (set_attr "athlon_decode" "vector,double") (set_attr "mode" "SF")]) (define_insn "cvtss2si" [(set (match_operand:SI 0 "register_operand" "=r,r") (vec_select:SI (fix:V4SI (match_operand:V4SF 1 "nonimmediate_operand" "x,m")) (parallel [(const_int 0)])))] "TARGET_SSE" "cvtss2si\t{%1, %0|%0, %1}" [(set_attr "type" "sseicvt") (set_attr "athlon_decode" "double,vector") (set_attr "mode" "SI")]) (define_insn "cvtss2siq" [(set (match_operand:DI 0 "register_operand" "=r,r") (vec_select:DI (fix:V4DI (match_operand:V4SF 1 "nonimmediate_operand" "x,m")) (parallel [(const_int 0)])))] "TARGET_SSE" "cvtss2siq\t{%1, %0|%0, %1}" [(set_attr "type" "sseicvt") (set_attr "athlon_decode" "double,vector") (set_attr "mode" "DI")]) (define_insn "cvttss2si" [(set (match_operand:SI 0 "register_operand" "=r,r") (vec_select:SI (unspec:V4SI [(match_operand:V4SF 1 "nonimmediate_operand" "x,xm")] UNSPEC_FIX) (parallel [(const_int 0)])))] "TARGET_SSE" "cvttss2si\t{%1, %0|%0, %1}" [(set_attr "type" "sseicvt") (set_attr "mode" "SF") (set_attr "athlon_decode" "double,vector")]) (define_insn "cvttss2siq" [(set (match_operand:DI 0 "register_operand" "=r,r") (vec_select:DI (unspec:V4DI [(match_operand:V4SF 1 "nonimmediate_operand" "x,xm")] UNSPEC_FIX) (parallel [(const_int 0)])))] "TARGET_SSE && TARGET_64BIT" "cvttss2siq\t{%1, %0|%0, %1}" [(set_attr "type" "sseicvt") (set_attr "mode" "SF") (set_attr "athlon_decode" "double,vector")]) ;; MMX insns ;; MMX arithmetic (define_insn "addv8qi3" [(set (match_operand:V8QI 0 "register_operand" "=y") (plus:V8QI (match_operand:V8QI 1 "register_operand" "%0") (match_operand:V8QI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "paddb\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "addv4hi3" [(set (match_operand:V4HI 0 "register_operand" "=y") (plus:V4HI (match_operand:V4HI 1 "register_operand" "%0") (match_operand:V4HI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "paddw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "addv2si3" [(set (match_operand:V2SI 0 "register_operand" "=y") (plus:V2SI (match_operand:V2SI 1 "register_operand" "%0") (match_operand:V2SI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "paddd\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "mmx_adddi3" [(set (match_operand:DI 0 "register_operand" "=y") (unspec:DI [(plus:DI (match_operand:DI 1 "register_operand" "%0") (match_operand:DI 2 "nonimmediate_operand" "ym"))] UNSPEC_NOP))] "TARGET_MMX" "paddq\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "ssaddv8qi3" [(set (match_operand:V8QI 0 "register_operand" "=y") (ss_plus:V8QI (match_operand:V8QI 1 "register_operand" "%0") (match_operand:V8QI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "paddsb\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "ssaddv4hi3" [(set (match_operand:V4HI 0 "register_operand" "=y") (ss_plus:V4HI (match_operand:V4HI 1 "register_operand" "%0") (match_operand:V4HI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "paddsw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "usaddv8qi3" [(set (match_operand:V8QI 0 "register_operand" "=y") (us_plus:V8QI (match_operand:V8QI 1 "register_operand" "%0") (match_operand:V8QI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "paddusb\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "usaddv4hi3" [(set (match_operand:V4HI 0 "register_operand" "=y") (us_plus:V4HI (match_operand:V4HI 1 "register_operand" "%0") (match_operand:V4HI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "paddusw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "subv8qi3" [(set (match_operand:V8QI 0 "register_operand" "=y") (minus:V8QI (match_operand:V8QI 1 "register_operand" "0") (match_operand:V8QI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "psubb\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "subv4hi3" [(set (match_operand:V4HI 0 "register_operand" "=y") (minus:V4HI (match_operand:V4HI 1 "register_operand" "0") (match_operand:V4HI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "psubw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "subv2si3" [(set (match_operand:V2SI 0 "register_operand" "=y") (minus:V2SI (match_operand:V2SI 1 "register_operand" "0") (match_operand:V2SI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "psubd\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "mmx_subdi3" [(set (match_operand:DI 0 "register_operand" "=y") (unspec:DI [(minus:DI (match_operand:DI 1 "register_operand" "0") (match_operand:DI 2 "nonimmediate_operand" "ym"))] UNSPEC_NOP))] "TARGET_MMX" "psubq\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "sssubv8qi3" [(set (match_operand:V8QI 0 "register_operand" "=y") (ss_minus:V8QI (match_operand:V8QI 1 "register_operand" "0") (match_operand:V8QI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "psubsb\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "sssubv4hi3" [(set (match_operand:V4HI 0 "register_operand" "=y") (ss_minus:V4HI (match_operand:V4HI 1 "register_operand" "0") (match_operand:V4HI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "psubsw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "ussubv8qi3" [(set (match_operand:V8QI 0 "register_operand" "=y") (us_minus:V8QI (match_operand:V8QI 1 "register_operand" "0") (match_operand:V8QI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "psubusb\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "ussubv4hi3" [(set (match_operand:V4HI 0 "register_operand" "=y") (us_minus:V4HI (match_operand:V4HI 1 "register_operand" "0") (match_operand:V4HI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "psubusw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "mulv4hi3" [(set (match_operand:V4HI 0 "register_operand" "=y") (mult:V4HI (match_operand:V4HI 1 "register_operand" "0") (match_operand:V4HI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "pmullw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxmul") (set_attr "mode" "DI")]) (define_insn "smulv4hi3_highpart" [(set (match_operand:V4HI 0 "register_operand" "=y") (truncate:V4HI (lshiftrt:V4SI (mult:V4SI (sign_extend:V4SI (match_operand:V4HI 1 "register_operand" "0")) (sign_extend:V4SI (match_operand:V4HI 2 "nonimmediate_operand" "ym"))) (const_int 16))))] "TARGET_MMX" "pmulhw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxmul") (set_attr "mode" "DI")]) (define_insn "umulv4hi3_highpart" [(set (match_operand:V4HI 0 "register_operand" "=y") (truncate:V4HI (lshiftrt:V4SI (mult:V4SI (zero_extend:V4SI (match_operand:V4HI 1 "register_operand" "0")) (zero_extend:V4SI (match_operand:V4HI 2 "nonimmediate_operand" "ym"))) (const_int 16))))] "TARGET_SSE || TARGET_3DNOW_A" "pmulhuw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxmul") (set_attr "mode" "DI")]) (define_insn "mmx_pmaddwd" [(set (match_operand:V2SI 0 "register_operand" "=y") (plus:V2SI (mult:V2SI (sign_extend:V2SI (vec_select:V2HI (match_operand:V4HI 1 "register_operand" "0") (parallel [(const_int 0) (const_int 2)]))) (sign_extend:V2SI (vec_select:V2HI (match_operand:V4HI 2 "nonimmediate_operand" "ym") (parallel [(const_int 0) (const_int 2)])))) (mult:V2SI (sign_extend:V2SI (vec_select:V2HI (match_dup 1) (parallel [(const_int 1) (const_int 3)]))) (sign_extend:V2SI (vec_select:V2HI (match_dup 2) (parallel [(const_int 1) (const_int 3)]))))))] "TARGET_MMX" "pmaddwd\t{%2, %0|%0, %2}" [(set_attr "type" "mmxmul") (set_attr "mode" "DI")]) ;; MMX logical operations ;; Note we don't want to declare these as regular iordi3 insns to prevent ;; normal code that also wants to use the FPU from getting broken. ;; The UNSPECs are there to prevent the combiner from getting overly clever. (define_insn "mmx_iordi3" [(set (match_operand:DI 0 "register_operand" "=y") (unspec:DI [(ior:DI (match_operand:DI 1 "register_operand" "%0") (match_operand:DI 2 "nonimmediate_operand" "ym"))] UNSPEC_NOP))] "TARGET_MMX" "por\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "mmx_xordi3" [(set (match_operand:DI 0 "register_operand" "=y") (unspec:DI [(xor:DI (match_operand:DI 1 "register_operand" "%0") (match_operand:DI 2 "nonimmediate_operand" "ym"))] UNSPEC_NOP))] "TARGET_MMX" "pxor\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI") (set_attr "memory" "none")]) ;; Same as pxor, but don't show input operands so that we don't think ;; they are live. (define_insn "mmx_clrdi" [(set (match_operand:DI 0 "register_operand" "=y") (unspec:DI [(const_int 0)] UNSPEC_NOP))] "TARGET_MMX" "pxor\t{%0, %0|%0, %0}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI") (set_attr "memory" "none")]) (define_insn "mmx_anddi3" [(set (match_operand:DI 0 "register_operand" "=y") (unspec:DI [(and:DI (match_operand:DI 1 "register_operand" "%0") (match_operand:DI 2 "nonimmediate_operand" "ym"))] UNSPEC_NOP))] "TARGET_MMX" "pand\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "mmx_nanddi3" [(set (match_operand:DI 0 "register_operand" "=y") (unspec:DI [(and:DI (not:DI (match_operand:DI 1 "register_operand" "0")) (match_operand:DI 2 "nonimmediate_operand" "ym"))] UNSPEC_NOP))] "TARGET_MMX" "pandn\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) ;; MMX unsigned averages/sum of absolute differences (define_insn "mmx_uavgv8qi3" [(set (match_operand:V8QI 0 "register_operand" "=y") (ashiftrt:V8QI (plus:V8QI (plus:V8QI (match_operand:V8QI 1 "register_operand" "0") (match_operand:V8QI 2 "nonimmediate_operand" "ym")) (const_vector:V8QI [(const_int 1) (const_int 1) (const_int 1) (const_int 1) (const_int 1) (const_int 1) (const_int 1) (const_int 1)])) (const_int 1)))] "TARGET_SSE || TARGET_3DNOW_A" "pavgb\t{%2, %0|%0, %2}" [(set_attr "type" "mmxshft") (set_attr "mode" "DI")]) (define_insn "mmx_uavgv4hi3" [(set (match_operand:V4HI 0 "register_operand" "=y") (ashiftrt:V4HI (plus:V4HI (plus:V4HI (match_operand:V4HI 1 "register_operand" "0") (match_operand:V4HI 2 "nonimmediate_operand" "ym")) (const_vector:V4HI [(const_int 1) (const_int 1) (const_int 1) (const_int 1)])) (const_int 1)))] "TARGET_SSE || TARGET_3DNOW_A" "pavgw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxshft") (set_attr "mode" "DI")]) (define_insn "mmx_psadbw" [(set (match_operand:DI 0 "register_operand" "=y") (unspec:DI [(match_operand:V8QI 1 "register_operand" "0") (match_operand:V8QI 2 "nonimmediate_operand" "ym")] UNSPEC_PSADBW))] "TARGET_SSE || TARGET_3DNOW_A" "psadbw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxshft") (set_attr "mode" "DI")]) ;; MMX insert/extract/shuffle (define_insn "mmx_pinsrw" [(set (match_operand:V4HI 0 "register_operand" "=y") (vec_merge:V4HI (match_operand:V4HI 1 "register_operand" "0") (vec_duplicate:V4HI (truncate:HI (match_operand:SI 2 "nonimmediate_operand" "rm"))) (match_operand:SI 3 "const_0_to_15_operand" "N")))] "TARGET_SSE || TARGET_3DNOW_A" "pinsrw\t{%3, %2, %0|%0, %2, %3}" [(set_attr "type" "mmxcvt") (set_attr "mode" "DI")]) (define_insn "mmx_pextrw" [(set (match_operand:SI 0 "register_operand" "=r") (zero_extend:SI (vec_select:HI (match_operand:V4HI 1 "register_operand" "y") (parallel [(match_operand:SI 2 "const_0_to_3_operand" "N")]))))] "TARGET_SSE || TARGET_3DNOW_A" "pextrw\t{%2, %1, %0|%0, %1, %2}" [(set_attr "type" "mmxcvt") (set_attr "mode" "DI")]) (define_insn "mmx_pshufw" [(set (match_operand:V4HI 0 "register_operand" "=y") (unspec:V4HI [(match_operand:V4HI 1 "register_operand" "0") (match_operand:SI 2 "immediate_operand" "i")] UNSPEC_SHUFFLE))] "TARGET_SSE || TARGET_3DNOW_A" "pshufw\t{%2, %1, %0|%0, %1, %2}" [(set_attr "type" "mmxcvt") (set_attr "mode" "DI")]) ;; MMX mask-generating comparisons (define_insn "eqv8qi3" [(set (match_operand:V8QI 0 "register_operand" "=y") (eq:V8QI (match_operand:V8QI 1 "register_operand" "0") (match_operand:V8QI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "pcmpeqb\t{%2, %0|%0, %2}" [(set_attr "type" "mmxcmp") (set_attr "mode" "DI")]) (define_insn "eqv4hi3" [(set (match_operand:V4HI 0 "register_operand" "=y") (eq:V4HI (match_operand:V4HI 1 "register_operand" "0") (match_operand:V4HI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "pcmpeqw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxcmp") (set_attr "mode" "DI")]) (define_insn "eqv2si3" [(set (match_operand:V2SI 0 "register_operand" "=y") (eq:V2SI (match_operand:V2SI 1 "register_operand" "0") (match_operand:V2SI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "pcmpeqd\t{%2, %0|%0, %2}" [(set_attr "type" "mmxcmp") (set_attr "mode" "DI")]) (define_insn "gtv8qi3" [(set (match_operand:V8QI 0 "register_operand" "=y") (gt:V8QI (match_operand:V8QI 1 "register_operand" "0") (match_operand:V8QI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "pcmpgtb\t{%2, %0|%0, %2}" [(set_attr "type" "mmxcmp") (set_attr "mode" "DI")]) (define_insn "gtv4hi3" [(set (match_operand:V4HI 0 "register_operand" "=y") (gt:V4HI (match_operand:V4HI 1 "register_operand" "0") (match_operand:V4HI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "pcmpgtw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxcmp") (set_attr "mode" "DI")]) (define_insn "gtv2si3" [(set (match_operand:V2SI 0 "register_operand" "=y") (gt:V2SI (match_operand:V2SI 1 "register_operand" "0") (match_operand:V2SI 2 "nonimmediate_operand" "ym")))] "TARGET_MMX" "pcmpgtd\t{%2, %0|%0, %2}" [(set_attr "type" "mmxcmp") (set_attr "mode" "DI")]) ;; MMX max/min insns (define_insn "umaxv8qi3" [(set (match_operand:V8QI 0 "register_operand" "=y") (umax:V8QI (match_operand:V8QI 1 "register_operand" "0") (match_operand:V8QI 2 "nonimmediate_operand" "ym")))] "TARGET_SSE || TARGET_3DNOW_A" "pmaxub\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "smaxv4hi3" [(set (match_operand:V4HI 0 "register_operand" "=y") (smax:V4HI (match_operand:V4HI 1 "register_operand" "0") (match_operand:V4HI 2 "nonimmediate_operand" "ym")))] "TARGET_SSE || TARGET_3DNOW_A" "pmaxsw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "uminv8qi3" [(set (match_operand:V8QI 0 "register_operand" "=y") (umin:V8QI (match_operand:V8QI 1 "register_operand" "0") (match_operand:V8QI 2 "nonimmediate_operand" "ym")))] "TARGET_SSE || TARGET_3DNOW_A" "pminub\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) (define_insn "sminv4hi3" [(set (match_operand:V4HI 0 "register_operand" "=y") (smin:V4HI (match_operand:V4HI 1 "register_operand" "0") (match_operand:V4HI 2 "nonimmediate_operand" "ym")))] "TARGET_SSE || TARGET_3DNOW_A" "pminsw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "DI")]) ;; MMX shifts (define_insn "ashrv4hi3" [(set (match_operand:V4HI 0 "register_operand" "=y") (ashiftrt:V4HI (match_operand:V4HI 1 "register_operand" "0") (match_operand:DI 2 "nonmemory_operand" "yi")))] "TARGET_MMX" "psraw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxshft") (set_attr "mode" "DI")]) (define_insn "ashrv2si3" [(set (match_operand:V2SI 0 "register_operand" "=y") (ashiftrt:V2SI (match_operand:V2SI 1 "register_operand" "0") (match_operand:DI 2 "nonmemory_operand" "yi")))] "TARGET_MMX" "psrad\t{%2, %0|%0, %2}" [(set_attr "type" "mmxshft") (set_attr "mode" "DI")]) (define_insn "lshrv4hi3" [(set (match_operand:V4HI 0 "register_operand" "=y") (lshiftrt:V4HI (match_operand:V4HI 1 "register_operand" "0") (match_operand:DI 2 "nonmemory_operand" "yi")))] "TARGET_MMX" "psrlw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxshft") (set_attr "mode" "DI")]) (define_insn "lshrv2si3" [(set (match_operand:V2SI 0 "register_operand" "=y") (lshiftrt:V2SI (match_operand:V2SI 1 "register_operand" "0") (match_operand:DI 2 "nonmemory_operand" "yi")))] "TARGET_MMX" "psrld\t{%2, %0|%0, %2}" [(set_attr "type" "mmxshft") (set_attr "mode" "DI")]) ;; See logical MMX insns. (define_insn "mmx_lshrdi3" [(set (match_operand:DI 0 "register_operand" "=y") (unspec:DI [(lshiftrt:DI (match_operand:DI 1 "register_operand" "0") (match_operand:DI 2 "nonmemory_operand" "yi"))] UNSPEC_NOP))] "TARGET_MMX" "psrlq\t{%2, %0|%0, %2}" [(set_attr "type" "mmxshft") (set_attr "mode" "DI")]) (define_insn "ashlv4hi3" [(set (match_operand:V4HI 0 "register_operand" "=y") (ashift:V4HI (match_operand:V4HI 1 "register_operand" "0") (match_operand:DI 2 "nonmemory_operand" "yi")))] "TARGET_MMX" "psllw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxshft") (set_attr "mode" "DI")]) (define_insn "ashlv2si3" [(set (match_operand:V2SI 0 "register_operand" "=y") (ashift:V2SI (match_operand:V2SI 1 "register_operand" "0") (match_operand:DI 2 "nonmemory_operand" "yi")))] "TARGET_MMX" "pslld\t{%2, %0|%0, %2}" [(set_attr "type" "mmxshft") (set_attr "mode" "DI")]) ;; See logical MMX insns. (define_insn "mmx_ashldi3" [(set (match_operand:DI 0 "register_operand" "=y") (unspec:DI [(ashift:DI (match_operand:DI 1 "register_operand" "0") (match_operand:DI 2 "nonmemory_operand" "yi"))] UNSPEC_NOP))] "TARGET_MMX" "psllq\t{%2, %0|%0, %2}" [(set_attr "type" "mmxshft") (set_attr "mode" "DI")]) ;; MMX pack/unpack insns. (define_insn "mmx_packsswb" [(set (match_operand:V8QI 0 "register_operand" "=y") (vec_concat:V8QI (ss_truncate:V4QI (match_operand:V4HI 1 "register_operand" "0")) (ss_truncate:V4QI (match_operand:V4HI 2 "register_operand" "y"))))] "TARGET_MMX" "packsswb\t{%2, %0|%0, %2}" [(set_attr "type" "mmxshft") (set_attr "mode" "DI")]) (define_insn "mmx_packssdw" [(set (match_operand:V4HI 0 "register_operand" "=y") (vec_concat:V4HI (ss_truncate:V2HI (match_operand:V2SI 1 "register_operand" "0")) (ss_truncate:V2HI (match_operand:V2SI 2 "register_operand" "y"))))] "TARGET_MMX" "packssdw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxshft") (set_attr "mode" "DI")]) (define_insn "mmx_packuswb" [(set (match_operand:V8QI 0 "register_operand" "=y") (vec_concat:V8QI (us_truncate:V4QI (match_operand:V4HI 1 "register_operand" "0")) (us_truncate:V4QI (match_operand:V4HI 2 "register_operand" "y"))))] "TARGET_MMX" "packuswb\t{%2, %0|%0, %2}" [(set_attr "type" "mmxshft") (set_attr "mode" "DI")]) (define_insn "mmx_punpckhbw" [(set (match_operand:V8QI 0 "register_operand" "=y") (vec_merge:V8QI (vec_select:V8QI (match_operand:V8QI 1 "register_operand" "0") (parallel [(const_int 4) (const_int 0) (const_int 5) (const_int 1) (const_int 6) (const_int 2) (const_int 7) (const_int 3)])) (vec_select:V8QI (match_operand:V8QI 2 "register_operand" "y") (parallel [(const_int 0) (const_int 4) (const_int 1) (const_int 5) (const_int 2) (const_int 6) (const_int 3) (const_int 7)])) (const_int 85)))] "TARGET_MMX" "punpckhbw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxcvt") (set_attr "mode" "DI")]) (define_insn "mmx_punpckhwd" [(set (match_operand:V4HI 0 "register_operand" "=y") (vec_merge:V4HI (vec_select:V4HI (match_operand:V4HI 1 "register_operand" "0") (parallel [(const_int 0) (const_int 2) (const_int 1) (const_int 3)])) (vec_select:V4HI (match_operand:V4HI 2 "register_operand" "y") (parallel [(const_int 2) (const_int 0) (const_int 3) (const_int 1)])) (const_int 5)))] "TARGET_MMX" "punpckhwd\t{%2, %0|%0, %2}" [(set_attr "type" "mmxcvt") (set_attr "mode" "DI")]) (define_insn "mmx_punpckhdq" [(set (match_operand:V2SI 0 "register_operand" "=y") (vec_merge:V2SI (match_operand:V2SI 1 "register_operand" "0") (vec_select:V2SI (match_operand:V2SI 2 "register_operand" "y") (parallel [(const_int 1) (const_int 0)])) (const_int 1)))] "TARGET_MMX" "punpckhdq\t{%2, %0|%0, %2}" [(set_attr "type" "mmxcvt") (set_attr "mode" "DI")]) (define_insn "mmx_punpcklbw" [(set (match_operand:V8QI 0 "register_operand" "=y") (vec_merge:V8QI (vec_select:V8QI (match_operand:V8QI 1 "register_operand" "0") (parallel [(const_int 0) (const_int 4) (const_int 1) (const_int 5) (const_int 2) (const_int 6) (const_int 3) (const_int 7)])) (vec_select:V8QI (match_operand:V8QI 2 "register_operand" "y") (parallel [(const_int 4) (const_int 0) (const_int 5) (const_int 1) (const_int 6) (const_int 2) (const_int 7) (const_int 3)])) (const_int 85)))] "TARGET_MMX" "punpcklbw\t{%2, %0|%0, %2}" [(set_attr "type" "mmxcvt") (set_attr "mode" "DI")]) (define_insn "mmx_punpcklwd" [(set (match_operand:V4HI 0 "register_operand" "=y") (vec_merge:V4HI (vec_select:V4HI (match_operand:V4HI 1 "register_operand" "0") (parallel [(const_int 2) (const_int 0) (const_int 3) (const_int 1)])) (vec_select:V4HI (match_operand:V4HI 2 "register_operand" "y") (parallel [(const_int 0) (const_int 2) (const_int 1) (const_int 3)])) (const_int 5)))] "TARGET_MMX" "punpcklwd\t{%2, %0|%0, %2}" [(set_attr "type" "mmxcvt") (set_attr "mode" "DI")]) (define_insn "mmx_punpckldq" [(set (match_operand:V2SI 0 "register_operand" "=y") (vec_merge:V2SI (vec_select:V2SI (match_operand:V2SI 1 "register_operand" "0") (parallel [(const_int 1) (const_int 0)])) (match_operand:V2SI 2 "register_operand" "y") (const_int 1)))] "TARGET_MMX" "punpckldq\t{%2, %0|%0, %2}" [(set_attr "type" "mmxcvt") (set_attr "mode" "DI")]) ;; Miscellaneous stuff (define_insn "emms" [(unspec_volatile [(const_int 0)] UNSPECV_EMMS) (clobber (reg:XF 8)) (clobber (reg:XF 9)) (clobber (reg:XF 10)) (clobber (reg:XF 11)) (clobber (reg:XF 12)) (clobber (reg:XF 13)) (clobber (reg:XF 14)) (clobber (reg:XF 15)) (clobber (reg:DI 29)) (clobber (reg:DI 30)) (clobber (reg:DI 31)) (clobber (reg:DI 32)) (clobber (reg:DI 33)) (clobber (reg:DI 34)) (clobber (reg:DI 35)) (clobber (reg:DI 36))] "TARGET_MMX" "emms" [(set_attr "type" "mmx") (set_attr "memory" "unknown")]) (define_insn "ldmxcsr" [(unspec_volatile [(match_operand:SI 0 "memory_operand" "m")] UNSPECV_LDMXCSR)] "TARGET_SSE" "ldmxcsr\t%0" [(set_attr "type" "sse") (set_attr "memory" "load")]) (define_insn "stmxcsr" [(set (match_operand:SI 0 "memory_operand" "=m") (unspec_volatile:SI [(const_int 0)] UNSPECV_STMXCSR))] "TARGET_SSE" "stmxcsr\t%0" [(set_attr "type" "sse") (set_attr "memory" "store")]) (define_expand "sfence" [(set (match_dup 0) (unspec:BLK [(match_dup 0)] UNSPEC_SFENCE))] "TARGET_SSE || TARGET_3DNOW_A" { operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); MEM_VOLATILE_P (operands[0]) = 1; }) (define_insn "*sfence_insn" [(set (match_operand:BLK 0 "" "") (unspec:BLK [(match_dup 0)] UNSPEC_SFENCE))] "TARGET_SSE || TARGET_3DNOW_A" "sfence" [(set_attr "type" "sse") (set_attr "memory" "unknown")]) (define_expand "sse_prologue_save" [(parallel [(set (match_operand:BLK 0 "" "") (unspec:BLK [(reg:DI 21) (reg:DI 22) (reg:DI 23) (reg:DI 24) (reg:DI 25) (reg:DI 26) (reg:DI 27) (reg:DI 28)] UNSPEC_SSE_PROLOGUE_SAVE)) (use (match_operand:DI 1 "register_operand" "")) (use (match_operand:DI 2 "immediate_operand" "")) (use (label_ref:DI (match_operand 3 "" "")))])] "TARGET_64BIT" "") (define_insn "*sse_prologue_save_insn" [(set (mem:BLK (plus:DI (match_operand:DI 0 "register_operand" "R") (match_operand:DI 4 "const_int_operand" "n"))) (unspec:BLK [(reg:DI 21) (reg:DI 22) (reg:DI 23) (reg:DI 24) (reg:DI 25) (reg:DI 26) (reg:DI 27) (reg:DI 28)] UNSPEC_SSE_PROLOGUE_SAVE)) (use (match_operand:DI 1 "register_operand" "r")) (use (match_operand:DI 2 "const_int_operand" "i")) (use (label_ref:DI (match_operand 3 "" "X")))] "TARGET_64BIT && INTVAL (operands[4]) + SSE_REGPARM_MAX * 16 - 16 < 128 && INTVAL (operands[4]) + INTVAL (operands[2]) * 16 >= -128" "* { int i; operands[0] = gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, operands[0], operands[4])); output_asm_insn (\"jmp\\t%A1\", operands); for (i = SSE_REGPARM_MAX - 1; i >= INTVAL (operands[2]); i--) { operands[4] = adjust_address (operands[0], DImode, i*16); operands[5] = gen_rtx_REG (TImode, SSE_REGNO (i)); PUT_MODE (operands[4], TImode); if (GET_CODE (XEXP (operands[0], 0)) != PLUS) output_asm_insn (\"rex\", operands); output_asm_insn (\"movaps\\t{%5, %4|%4, %5}\", operands); } (*targetm.asm_out.internal_label) (asm_out_file, \"L\", CODE_LABEL_NUMBER (operands[3])); RET; } " [(set_attr "type" "other") (set_attr "length_immediate" "0") (set_attr "length_address" "0") (set_attr "length" "135") (set_attr "memory" "store") (set_attr "modrm" "0") (set_attr "mode" "DI")]) ;; 3Dnow! instructions (define_insn "addv2sf3" [(set (match_operand:V2SF 0 "register_operand" "=y") (plus:V2SF (match_operand:V2SF 1 "register_operand" "0") (match_operand:V2SF 2 "nonimmediate_operand" "ym")))] "TARGET_3DNOW" "pfadd\\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "V2SF")]) (define_insn "subv2sf3" [(set (match_operand:V2SF 0 "register_operand" "=y") (minus:V2SF (match_operand:V2SF 1 "register_operand" "0") (match_operand:V2SF 2 "nonimmediate_operand" "ym")))] "TARGET_3DNOW" "pfsub\\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "V2SF")]) (define_insn "subrv2sf3" [(set (match_operand:V2SF 0 "register_operand" "=y") (minus:V2SF (match_operand:V2SF 2 "nonimmediate_operand" "ym") (match_operand:V2SF 1 "register_operand" "0")))] "TARGET_3DNOW" "pfsubr\\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "V2SF")]) (define_insn "gtv2sf3" [(set (match_operand:V2SI 0 "register_operand" "=y") (gt:V2SI (match_operand:V2SF 1 "register_operand" "0") (match_operand:V2SF 2 "nonimmediate_operand" "ym")))] "TARGET_3DNOW" "pfcmpgt\\t{%2, %0|%0, %2}" [(set_attr "type" "mmxcmp") (set_attr "mode" "V2SF")]) (define_insn "gev2sf3" [(set (match_operand:V2SI 0 "register_operand" "=y") (ge:V2SI (match_operand:V2SF 1 "register_operand" "0") (match_operand:V2SF 2 "nonimmediate_operand" "ym")))] "TARGET_3DNOW" "pfcmpge\\t{%2, %0|%0, %2}" [(set_attr "type" "mmxcmp") (set_attr "mode" "V2SF")]) (define_insn "eqv2sf3" [(set (match_operand:V2SI 0 "register_operand" "=y") (eq:V2SI (match_operand:V2SF 1 "register_operand" "0") (match_operand:V2SF 2 "nonimmediate_operand" "ym")))] "TARGET_3DNOW" "pfcmpeq\\t{%2, %0|%0, %2}" [(set_attr "type" "mmxcmp") (set_attr "mode" "V2SF")]) (define_insn "pfmaxv2sf3" [(set (match_operand:V2SF 0 "register_operand" "=y") (smax:V2SF (match_operand:V2SF 1 "register_operand" "0") (match_operand:V2SF 2 "nonimmediate_operand" "ym")))] "TARGET_3DNOW" "pfmax\\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "V2SF")]) (define_insn "pfminv2sf3" [(set (match_operand:V2SF 0 "register_operand" "=y") (smin:V2SF (match_operand:V2SF 1 "register_operand" "0") (match_operand:V2SF 2 "nonimmediate_operand" "ym")))] "TARGET_3DNOW" "pfmin\\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "V2SF")]) (define_insn "mulv2sf3" [(set (match_operand:V2SF 0 "register_operand" "=y") (mult:V2SF (match_operand:V2SF 1 "register_operand" "0") (match_operand:V2SF 2 "nonimmediate_operand" "ym")))] "TARGET_3DNOW" "pfmul\\t{%2, %0|%0, %2}" [(set_attr "type" "mmxmul") (set_attr "mode" "V2SF")]) (define_insn "femms" [(unspec_volatile [(const_int 0)] UNSPECV_FEMMS) (clobber (reg:XF 8)) (clobber (reg:XF 9)) (clobber (reg:XF 10)) (clobber (reg:XF 11)) (clobber (reg:XF 12)) (clobber (reg:XF 13)) (clobber (reg:XF 14)) (clobber (reg:XF 15)) (clobber (reg:DI 29)) (clobber (reg:DI 30)) (clobber (reg:DI 31)) (clobber (reg:DI 32)) (clobber (reg:DI 33)) (clobber (reg:DI 34)) (clobber (reg:DI 35)) (clobber (reg:DI 36))] "TARGET_3DNOW" "femms" [(set_attr "type" "mmx") (set_attr "memory" "none")]) (define_insn "pf2id" [(set (match_operand:V2SI 0 "register_operand" "=y") (fix:V2SI (match_operand:V2SF 1 "nonimmediate_operand" "ym")))] "TARGET_3DNOW" "pf2id\\t{%1, %0|%0, %1}" [(set_attr "type" "mmxcvt") (set_attr "mode" "V2SF")]) (define_insn "pf2iw" [(set (match_operand:V2SI 0 "register_operand" "=y") (sign_extend:V2SI (ss_truncate:V2HI (fix:V2SI (match_operand:V2SF 1 "nonimmediate_operand" "ym")))))] "TARGET_3DNOW_A" "pf2iw\\t{%1, %0|%0, %1}" [(set_attr "type" "mmxcvt") (set_attr "mode" "V2SF")]) (define_insn "pfacc" [(set (match_operand:V2SF 0 "register_operand" "=y") (vec_concat:V2SF (plus:SF (vec_select:SF (match_operand:V2SF 1 "register_operand" "0") (parallel [(const_int 0)])) (vec_select:SF (match_dup 1) (parallel [(const_int 1)]))) (plus:SF (vec_select:SF (match_operand:V2SF 2 "nonimmediate_operand" "y") (parallel [(const_int 0)])) (vec_select:SF (match_dup 2) (parallel [(const_int 1)])))))] "TARGET_3DNOW" "pfacc\\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "V2SF")]) (define_insn "pfnacc" [(set (match_operand:V2SF 0 "register_operand" "=y") (vec_concat:V2SF (minus:SF (vec_select:SF (match_operand:V2SF 1 "register_operand" "0") (parallel [(const_int 0)])) (vec_select:SF (match_dup 1) (parallel [(const_int 1)]))) (minus:SF (vec_select:SF (match_operand:V2SF 2 "nonimmediate_operand" "y") (parallel [(const_int 0)])) (vec_select:SF (match_dup 2) (parallel [(const_int 1)])))))] "TARGET_3DNOW_A" "pfnacc\\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "V2SF")]) (define_insn "pfpnacc" [(set (match_operand:V2SF 0 "register_operand" "=y") (vec_concat:V2SF (minus:SF (vec_select:SF (match_operand:V2SF 1 "register_operand" "0") (parallel [(const_int 0)])) (vec_select:SF (match_dup 1) (parallel [(const_int 1)]))) (plus:SF (vec_select:SF (match_operand:V2SF 2 "nonimmediate_operand" "y") (parallel [(const_int 0)])) (vec_select:SF (match_dup 2) (parallel [(const_int 1)])))))] "TARGET_3DNOW_A" "pfpnacc\\t{%2, %0|%0, %2}" [(set_attr "type" "mmxadd") (set_attr "mode" "V2SF")]) (define_insn "pi2fw" [(set (match_operand:V2SF 0 "register_operand" "=y") (float:V2SF (vec_concat:V2SI (sign_extend:SI (truncate:HI (vec_select:SI (match_operand:V2SI 1 "nonimmediate_operand" "ym") (parallel [(const_int 0)])))) (sign_extend:SI (truncate:HI (vec_select:SI (match_dup 1) (parallel [(const_int 1)])))))))] "TARGET_3DNOW_A" "pi2fw\\t{%1, %0|%0, %1}" [(set_attr "type" "mmxcvt") (set_attr "mode" "V2SF")]) (define_insn "floatv2si2" [(set (match_operand:V2SF 0 "register_operand" "=y") (float:V2SF (match_operand:V2SI 1 "nonimmediate_operand" "ym")))] "TARGET_3DNOW" "pi2fd\\t{%1, %0|%0, %1}" [(set_attr "type" "mmxcvt") (set_attr "mode" "V2SF")]) ;; This insn is identical to pavgb in operation, but the opcode is ;; different. To avoid accidentally matching pavgb, use an unspec. (define_insn "pavgusb" [(set (match_operand:V8QI 0 "register_operand" "=y") (unspec:V8QI [(match_operand:V8QI 1 "register_operand" "0") (match_operand:V8QI 2 "nonimmediate_operand" "ym")] UNSPEC_PAVGUSB))] "TARGET_3DNOW" "pavgusb\\t{%2, %0|%0, %2}" [(set_attr "type" "mmxshft") (set_attr "mode" "TI")]) ;; 3DNow reciprocal and sqrt (define_insn "pfrcpv2sf2" [(set (match_operand:V2SF 0 "register_operand" "=y") (unspec:V2SF [(match_operand:V2SF 1 "nonimmediate_operand" "ym")] UNSPEC_PFRCP))] "TARGET_3DNOW" "pfrcp\\t{%1, %0|%0, %1}" [(set_attr "type" "mmx") (set_attr "mode" "TI")]) (define_insn "pfrcpit1v2sf3" [(set (match_operand:V2SF 0 "register_operand" "=y") (unspec:V2SF [(match_operand:V2SF 1 "register_operand" "0") (match_operand:V2SF 2 "nonimmediate_operand" "ym")] UNSPEC_PFRCPIT1))] "TARGET_3DNOW" "pfrcpit1\\t{%2, %0|%0, %2}" [(set_attr "type" "mmx") (set_attr "mode" "TI")]) (define_insn "pfrcpit2v2sf3" [(set (match_operand:V2SF 0 "register_operand" "=y") (unspec:V2SF [(match_operand:V2SF 1 "register_operand" "0") (match_operand:V2SF 2 "nonimmediate_operand" "ym")] UNSPEC_PFRCPIT2))] "TARGET_3DNOW" "pfrcpit2\\t{%2, %0|%0, %2}" [(set_attr "type" "mmx") (set_attr "mode" "TI")]) (define_insn "pfrsqrtv2sf2" [(set (match_operand:V2SF 0 "register_operand" "=y") (unspec:V2SF [(match_operand:V2SF 1 "nonimmediate_operand" "ym")] UNSPEC_PFRSQRT))] "TARGET_3DNOW" "pfrsqrt\\t{%1, %0|%0, %1}" [(set_attr "type" "mmx") (set_attr "mode" "TI")]) (define_insn "pfrsqit1v2sf3" [(set (match_operand:V2SF 0 "register_operand" "=y") (unspec:V2SF [(match_operand:V2SF 1 "register_operand" "0") (match_operand:V2SF 2 "nonimmediate_operand" "ym")] UNSPEC_PFRSQIT1))] "TARGET_3DNOW" "pfrsqit1\\t{%2, %0|%0, %2}" [(set_attr "type" "mmx") (set_attr "mode" "TI")]) (define_insn "pmulhrwv4hi3" [(set (match_operand:V4HI 0 "register_operand" "=y") (truncate:V4HI (lshiftrt:V4SI (plus:V4SI (mult:V4SI (sign_extend:V4SI (match_operand:V4HI 1 "register_operand" "0")) (sign_extend:V4SI (match_operand:V4HI 2 "nonimmediate_operand" "ym"))) (const_vector:V4SI [(const_int 32768) (const_int 32768) (const_int 32768) (const_int 32768)])) (const_int 16))))] "TARGET_3DNOW" "pmulhrw\\t{%2, %0|%0, %2}" [(set_attr "type" "mmxmul") (set_attr "mode" "TI")]) (define_insn "pswapdv2si2" [(set (match_operand:V2SI 0 "register_operand" "=y") (vec_select:V2SI (match_operand:V2SI 1 "nonimmediate_operand" "ym") (parallel [(const_int 1) (const_int 0)])))] "TARGET_3DNOW_A" "pswapd\\t{%1, %0|%0, %1}" [(set_attr "type" "mmxcvt") (set_attr "mode" "TI")]) (define_insn "pswapdv2sf2" [(set (match_operand:V2SF 0 "register_operand" "=y") (vec_select:V2SF (match_operand:V2SF 1 "nonimmediate_operand" "ym") (parallel [(const_int 1) (const_int 0)])))] "TARGET_3DNOW_A" "pswapd\\t{%1, %0|%0, %1}" [(set_attr "type" "mmxcvt") (set_attr "mode" "TI")]) (define_expand "prefetch" [(prefetch (match_operand 0 "address_operand" "") (match_operand:SI 1 "const_int_operand" "") (match_operand:SI 2 "const_int_operand" ""))] "TARGET_PREFETCH_SSE || TARGET_3DNOW" { int rw = INTVAL (operands[1]); int locality = INTVAL (operands[2]); if (rw != 0 && rw != 1) abort (); if (locality < 0 || locality > 3) abort (); if (GET_MODE (operands[0]) != Pmode && GET_MODE (operands[0]) != VOIDmode) abort (); /* Use 3dNOW prefetch in case we are asking for write prefetch not suported by SSE counterpart or the SSE prefetch is not available (K6 machines). Otherwise use SSE prefetch as it allows specifying of locality. */ if (TARGET_3DNOW && (!TARGET_PREFETCH_SSE || rw)) operands[2] = GEN_INT (3); else operands[1] = const0_rtx; }) (define_insn "*prefetch_sse" [(prefetch (match_operand:SI 0 "address_operand" "p") (const_int 0) (match_operand:SI 1 "const_int_operand" ""))] "TARGET_PREFETCH_SSE && !TARGET_64BIT" { static const char * const patterns[4] = { "prefetchnta\t%a0", "prefetcht2\t%a0", "prefetcht1\t%a0", "prefetcht0\t%a0" }; int locality = INTVAL (operands[1]); if (locality < 0 || locality > 3) abort (); return patterns[locality]; } [(set_attr "type" "sse") (set_attr "memory" "none")]) (define_insn "*prefetch_sse_rex" [(prefetch (match_operand:DI 0 "address_operand" "p") (const_int 0) (match_operand:SI 1 "const_int_operand" ""))] "TARGET_PREFETCH_SSE && TARGET_64BIT" { static const char * const patterns[4] = { "prefetchnta\t%a0", "prefetcht2\t%a0", "prefetcht1\t%a0", "prefetcht0\t%a0" }; int locality = INTVAL (operands[1]); if (locality < 0 || locality > 3) abort (); return patterns[locality]; } [(set_attr "type" "sse") (set_attr "memory" "none")]) (define_insn "*prefetch_3dnow" [(prefetch (match_operand:SI 0 "address_operand" "p") (match_operand:SI 1 "const_int_operand" "n") (const_int 3))] "TARGET_3DNOW && !TARGET_64BIT" { if (INTVAL (operands[1]) == 0) return "prefetch\t%a0"; else return "prefetchw\t%a0"; } [(set_attr "type" "mmx") (set_attr "memory" "none")]) (define_insn "*prefetch_3dnow_rex" [(prefetch (match_operand:DI 0 "address_operand" "p") (match_operand:SI 1 "const_int_operand" "n") (const_int 3))] "TARGET_3DNOW && TARGET_64BIT" { if (INTVAL (operands[1]) == 0) return "prefetch\t%a0"; else return "prefetchw\t%a0"; } [(set_attr "type" "mmx") (set_attr "memory" "none")]) ;; SSE2 support (define_insn "addv2df3" [(set (match_operand:V2DF 0 "register_operand" "=x") (plus:V2DF (match_operand:V2DF 1 "register_operand" "0") (match_operand:V2DF 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "addpd\t{%2, %0|%0, %2}" [(set_attr "type" "sseadd") (set_attr "mode" "V2DF")]) (define_insn "vmaddv2df3" [(set (match_operand:V2DF 0 "register_operand" "=x") (vec_merge:V2DF (plus:V2DF (match_operand:V2DF 1 "register_operand" "0") (match_operand:V2DF 2 "nonimmediate_operand" "xm")) (match_dup 1) (const_int 1)))] "TARGET_SSE2" "addsd\t{%2, %0|%0, %2}" [(set_attr "type" "sseadd") (set_attr "mode" "DF")]) (define_insn "subv2df3" [(set (match_operand:V2DF 0 "register_operand" "=x") (minus:V2DF (match_operand:V2DF 1 "register_operand" "0") (match_operand:V2DF 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "subpd\t{%2, %0|%0, %2}" [(set_attr "type" "sseadd") (set_attr "mode" "V2DF")]) (define_insn "vmsubv2df3" [(set (match_operand:V2DF 0 "register_operand" "=x") (vec_merge:V2DF (minus:V2DF (match_operand:V2DF 1 "register_operand" "0") (match_operand:V2DF 2 "nonimmediate_operand" "xm")) (match_dup 1) (const_int 1)))] "TARGET_SSE2" "subsd\t{%2, %0|%0, %2}" [(set_attr "type" "sseadd") (set_attr "mode" "DF")]) (define_insn "mulv2df3" [(set (match_operand:V2DF 0 "register_operand" "=x") (mult:V2DF (match_operand:V2DF 1 "register_operand" "0") (match_operand:V2DF 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "mulpd\t{%2, %0|%0, %2}" [(set_attr "type" "ssemul") (set_attr "mode" "V2DF")]) (define_insn "vmmulv2df3" [(set (match_operand:V2DF 0 "register_operand" "=x") (vec_merge:V2DF (mult:V2DF (match_operand:V2DF 1 "register_operand" "0") (match_operand:V2DF 2 "nonimmediate_operand" "xm")) (match_dup 1) (const_int 1)))] "TARGET_SSE2" "mulsd\t{%2, %0|%0, %2}" [(set_attr "type" "ssemul") (set_attr "mode" "DF")]) (define_insn "divv2df3" [(set (match_operand:V2DF 0 "register_operand" "=x") (div:V2DF (match_operand:V2DF 1 "register_operand" "0") (match_operand:V2DF 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "divpd\t{%2, %0|%0, %2}" [(set_attr "type" "ssediv") (set_attr "mode" "V2DF")]) (define_insn "vmdivv2df3" [(set (match_operand:V2DF 0 "register_operand" "=x") (vec_merge:V2DF (div:V2DF (match_operand:V2DF 1 "register_operand" "0") (match_operand:V2DF 2 "nonimmediate_operand" "xm")) (match_dup 1) (const_int 1)))] "TARGET_SSE2" "divsd\t{%2, %0|%0, %2}" [(set_attr "type" "ssediv") (set_attr "mode" "DF")]) ;; SSE min/max (define_insn "smaxv2df3" [(set (match_operand:V2DF 0 "register_operand" "=x") (smax:V2DF (match_operand:V2DF 1 "register_operand" "0") (match_operand:V2DF 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "maxpd\t{%2, %0|%0, %2}" [(set_attr "type" "sseadd") (set_attr "mode" "V2DF")]) (define_insn "vmsmaxv2df3" [(set (match_operand:V2DF 0 "register_operand" "=x") (vec_merge:V2DF (smax:V2DF (match_operand:V2DF 1 "register_operand" "0") (match_operand:V2DF 2 "nonimmediate_operand" "xm")) (match_dup 1) (const_int 1)))] "TARGET_SSE2" "maxsd\t{%2, %0|%0, %2}" [(set_attr "type" "sseadd") (set_attr "mode" "DF")]) (define_insn "sminv2df3" [(set (match_operand:V2DF 0 "register_operand" "=x") (smin:V2DF (match_operand:V2DF 1 "register_operand" "0") (match_operand:V2DF 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "minpd\t{%2, %0|%0, %2}" [(set_attr "type" "sseadd") (set_attr "mode" "V2DF")]) (define_insn "vmsminv2df3" [(set (match_operand:V2DF 0 "register_operand" "=x") (vec_merge:V2DF (smin:V2DF (match_operand:V2DF 1 "register_operand" "0") (match_operand:V2DF 2 "nonimmediate_operand" "xm")) (match_dup 1) (const_int 1)))] "TARGET_SSE2" "minsd\t{%2, %0|%0, %2}" [(set_attr "type" "sseadd") (set_attr "mode" "DF")]) ;; SSE2 square root. There doesn't appear to be an extension for the ;; reciprocal/rsqrt instructions if the Intel manual is to be believed. (define_insn "sqrtv2df2" [(set (match_operand:V2DF 0 "register_operand" "=x") (sqrt:V2DF (match_operand:V2DF 1 "register_operand" "xm")))] "TARGET_SSE2" "sqrtpd\t{%1, %0|%0, %1}" [(set_attr "type" "sse") (set_attr "mode" "V2DF")]) (define_insn "vmsqrtv2df2" [(set (match_operand:V2DF 0 "register_operand" "=x") (vec_merge:V2DF (sqrt:V2DF (match_operand:V2DF 1 "register_operand" "xm")) (match_operand:V2DF 2 "register_operand" "0") (const_int 1)))] "TARGET_SSE2" "sqrtsd\t{%1, %0|%0, %1}" [(set_attr "type" "sse") (set_attr "mode" "SF")]) ;; SSE mask-generating compares (define_insn "maskcmpv2df3" [(set (match_operand:V2DI 0 "register_operand" "=x") (match_operator:V2DI 3 "sse_comparison_operator" [(match_operand:V2DF 1 "register_operand" "0") (match_operand:V2DF 2 "nonimmediate_operand" "x")]))] "TARGET_SSE2" "cmp%D3pd\t{%2, %0|%0, %2}" [(set_attr "type" "ssecmp") (set_attr "mode" "V2DF")]) (define_insn "maskncmpv2df3" [(set (match_operand:V2DI 0 "register_operand" "=x") (not:V2DI (match_operator:V2DI 3 "sse_comparison_operator" [(match_operand:V2DF 1 "register_operand" "0") (match_operand:V2DF 2 "nonimmediate_operand" "x")])))] "TARGET_SSE2" { if (GET_CODE (operands[3]) == UNORDERED) return "cmpordps\t{%2, %0|%0, %2}"; else return "cmpn%D3pd\t{%2, %0|%0, %2}"; } [(set_attr "type" "ssecmp") (set_attr "mode" "V2DF")]) (define_insn "vmmaskcmpv2df3" [(set (match_operand:V2DI 0 "register_operand" "=x") (vec_merge:V2DI (match_operator:V2DI 3 "sse_comparison_operator" [(match_operand:V2DF 1 "register_operand" "0") (match_operand:V2DF 2 "nonimmediate_operand" "x")]) (subreg:V2DI (match_dup 1) 0) (const_int 1)))] "TARGET_SSE2" "cmp%D3sd\t{%2, %0|%0, %2}" [(set_attr "type" "ssecmp") (set_attr "mode" "DF")]) (define_insn "vmmaskncmpv2df3" [(set (match_operand:V2DI 0 "register_operand" "=x") (vec_merge:V2DI (not:V2DI (match_operator:V2DI 3 "sse_comparison_operator" [(match_operand:V2DF 1 "register_operand" "0") (match_operand:V2DF 2 "nonimmediate_operand" "x")])) (subreg:V2DI (match_dup 1) 0) (const_int 1)))] "TARGET_SSE2" { if (GET_CODE (operands[3]) == UNORDERED) return "cmpordsd\t{%2, %0|%0, %2}"; else return "cmpn%D3sd\t{%2, %0|%0, %2}"; } [(set_attr "type" "ssecmp") (set_attr "mode" "DF")]) (define_insn "sse2_comi" [(set (reg:CCFP 17) (compare:CCFP (vec_select:DF (match_operand:V2DF 0 "register_operand" "x") (parallel [(const_int 0)])) (vec_select:DF (match_operand:V2DF 1 "register_operand" "x") (parallel [(const_int 0)]))))] "TARGET_SSE2" "comisd\t{%1, %0|%0, %1}" [(set_attr "type" "ssecomi") (set_attr "mode" "DF")]) (define_insn "sse2_ucomi" [(set (reg:CCFPU 17) (compare:CCFPU (vec_select:DF (match_operand:V2DF 0 "register_operand" "x") (parallel [(const_int 0)])) (vec_select:DF (match_operand:V2DF 1 "register_operand" "x") (parallel [(const_int 0)]))))] "TARGET_SSE2" "ucomisd\t{%1, %0|%0, %1}" [(set_attr "type" "ssecomi") (set_attr "mode" "DF")]) ;; SSE Strange Moves. (define_insn "sse2_movmskpd" [(set (match_operand:SI 0 "register_operand" "=r") (unspec:SI [(match_operand:V2DF 1 "register_operand" "x")] UNSPEC_MOVMSK))] "TARGET_SSE2" "movmskpd\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "V2DF")]) (define_insn "sse2_pmovmskb" [(set (match_operand:SI 0 "register_operand" "=r") (unspec:SI [(match_operand:V16QI 1 "register_operand" "x")] UNSPEC_MOVMSK))] "TARGET_SSE2" "pmovmskb\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "V2DF")]) (define_insn "sse2_maskmovdqu" [(set (mem:V16QI (match_operand:SI 0 "register_operand" "D")) (unspec:V16QI [(match_operand:V16QI 1 "register_operand" "x") (match_operand:V16QI 2 "register_operand" "x")] UNSPEC_MASKMOV))] "TARGET_SSE2" ;; @@@ check ordering of operands in intel/nonintel syntax "maskmovdqu\t{%2, %1|%1, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_maskmovdqu_rex64" [(set (mem:V16QI (match_operand:DI 0 "register_operand" "D")) (unspec:V16QI [(match_operand:V16QI 1 "register_operand" "x") (match_operand:V16QI 2 "register_operand" "x")] UNSPEC_MASKMOV))] "TARGET_SSE2" ;; @@@ check ordering of operands in intel/nonintel syntax "maskmovdqu\t{%2, %1|%1, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_movntv2df" [(set (match_operand:V2DF 0 "memory_operand" "=m") (unspec:V2DF [(match_operand:V2DF 1 "register_operand" "x")] UNSPEC_MOVNT))] "TARGET_SSE2" "movntpd\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "V2DF")]) (define_insn "sse2_movntv2di" [(set (match_operand:V2DI 0 "memory_operand" "=m") (unspec:V2DI [(match_operand:V2DI 1 "register_operand" "x")] UNSPEC_MOVNT))] "TARGET_SSE2" "movntdq\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_movntsi" [(set (match_operand:SI 0 "memory_operand" "=m") (unspec:SI [(match_operand:SI 1 "register_operand" "r")] UNSPEC_MOVNT))] "TARGET_SSE2" "movnti\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "V2DF")]) ;; SSE <-> integer/MMX conversions ;; Conversions between SI and SF (define_insn "cvtdq2ps" [(set (match_operand:V4SF 0 "register_operand" "=x") (float:V4SF (match_operand:V4SI 1 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "cvtdq2ps\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "V2DF")]) (define_insn "cvtps2dq" [(set (match_operand:V4SI 0 "register_operand" "=x") (fix:V4SI (match_operand:V4SF 1 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "cvtps2dq\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "cvttps2dq" [(set (match_operand:V4SI 0 "register_operand" "=x") (unspec:V4SI [(match_operand:V4SF 1 "nonimmediate_operand" "xm")] UNSPEC_FIX))] "TARGET_SSE2" "cvttps2dq\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) ;; Conversions between SI and DF (define_insn "cvtdq2pd" [(set (match_operand:V2DF 0 "register_operand" "=x") (float:V2DF (vec_select:V2SI (match_operand:V4SI 1 "nonimmediate_operand" "xm") (parallel [(const_int 0) (const_int 1)]))))] "TARGET_SSE2" "cvtdq2pd\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "V2DF")]) (define_insn "cvtpd2dq" [(set (match_operand:V4SI 0 "register_operand" "=x") (vec_concat:V4SI (fix:V2SI (match_operand:V2DF 1 "nonimmediate_operand" "xm")) (const_vector:V2SI [(const_int 0) (const_int 0)])))] "TARGET_SSE2" "cvtpd2dq\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "cvttpd2dq" [(set (match_operand:V4SI 0 "register_operand" "=x") (vec_concat:V4SI (unspec:V2SI [(match_operand:V2DF 1 "nonimmediate_operand" "xm")] UNSPEC_FIX) (const_vector:V2SI [(const_int 0) (const_int 0)])))] "TARGET_SSE2" "cvttpd2dq\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "cvtpd2pi" [(set (match_operand:V2SI 0 "register_operand" "=y") (fix:V2SI (match_operand:V2DF 1 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "cvtpd2pi\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "cvttpd2pi" [(set (match_operand:V2SI 0 "register_operand" "=y") (unspec:V2SI [(match_operand:V2DF 1 "nonimmediate_operand" "xm")] UNSPEC_FIX))] "TARGET_SSE2" "cvttpd2pi\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "cvtpi2pd" [(set (match_operand:V2DF 0 "register_operand" "=x") (float:V2DF (match_operand:V2SI 1 "nonimmediate_operand" "ym")))] "TARGET_SSE2" "cvtpi2pd\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) ;; Conversions between SI and DF (define_insn "cvtsd2si" [(set (match_operand:SI 0 "register_operand" "=r,r") (fix:SI (vec_select:DF (match_operand:V2DF 1 "register_operand" "x,m") (parallel [(const_int 0)]))))] "TARGET_SSE2" "cvtsd2si\t{%1, %0|%0, %1}" [(set_attr "type" "sseicvt") (set_attr "athlon_decode" "double,vector") (set_attr "mode" "SI")]) (define_insn "cvtsd2siq" [(set (match_operand:DI 0 "register_operand" "=r,r") (fix:DI (vec_select:DF (match_operand:V2DF 1 "register_operand" "x,m") (parallel [(const_int 0)]))))] "TARGET_SSE2 && TARGET_64BIT" "cvtsd2siq\t{%1, %0|%0, %1}" [(set_attr "type" "sseicvt") (set_attr "athlon_decode" "double,vector") (set_attr "mode" "DI")]) (define_insn "cvttsd2si" [(set (match_operand:SI 0 "register_operand" "=r,r") (unspec:SI [(vec_select:DF (match_operand:V2DF 1 "register_operand" "x,xm") (parallel [(const_int 0)]))] UNSPEC_FIX))] "TARGET_SSE2" "cvttsd2si\t{%1, %0|%0, %1}" [(set_attr "type" "sseicvt") (set_attr "mode" "SI") (set_attr "athlon_decode" "double,vector")]) (define_insn "cvttsd2siq" [(set (match_operand:DI 0 "register_operand" "=r,r") (unspec:DI [(vec_select:DF (match_operand:V2DF 1 "register_operand" "x,xm") (parallel [(const_int 0)]))] UNSPEC_FIX))] "TARGET_SSE2 && TARGET_64BIT" "cvttsd2siq\t{%1, %0|%0, %1}" [(set_attr "type" "sseicvt") (set_attr "mode" "DI") (set_attr "athlon_decode" "double,vector")]) (define_insn "cvtsi2sd" [(set (match_operand:V2DF 0 "register_operand" "=x,x") (vec_merge:V2DF (match_operand:V2DF 1 "register_operand" "0,0") (vec_duplicate:V2DF (float:DF (match_operand:SI 2 "nonimmediate_operand" "r,rm"))) (const_int 2)))] "TARGET_SSE2" "cvtsi2sd\t{%2, %0|%0, %2}" [(set_attr "type" "sseicvt") (set_attr "mode" "DF") (set_attr "athlon_decode" "double,direct")]) (define_insn "cvtsi2sdq" [(set (match_operand:V2DF 0 "register_operand" "=x,x") (vec_merge:V2DF (match_operand:V2DF 1 "register_operand" "0,0") (vec_duplicate:V2DF (float:DF (match_operand:DI 2 "nonimmediate_operand" "r,rm"))) (const_int 2)))] "TARGET_SSE2 && TARGET_64BIT" "cvtsi2sdq\t{%2, %0|%0, %2}" [(set_attr "type" "sseicvt") (set_attr "mode" "DF") (set_attr "athlon_decode" "double,direct")]) ;; Conversions between SF and DF (define_insn "cvtsd2ss" [(set (match_operand:V4SF 0 "register_operand" "=x,x") (vec_merge:V4SF (match_operand:V4SF 1 "register_operand" "0,0") (vec_duplicate:V4SF (float_truncate:V2SF (match_operand:V2DF 2 "nonimmediate_operand" "x,xm"))) (const_int 14)))] "TARGET_SSE2" "cvtsd2ss\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "athlon_decode" "vector,double") (set_attr "mode" "SF")]) (define_insn "cvtss2sd" [(set (match_operand:V2DF 0 "register_operand" "=x") (vec_merge:V2DF (match_operand:V2DF 1 "register_operand" "0") (float_extend:V2DF (vec_select:V2SF (match_operand:V4SF 2 "nonimmediate_operand" "xm") (parallel [(const_int 0) (const_int 1)]))) (const_int 2)))] "TARGET_SSE2" "cvtss2sd\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "DF")]) (define_insn "cvtpd2ps" [(set (match_operand:V4SF 0 "register_operand" "=x") (subreg:V4SF (vec_concat:V4SI (subreg:V2SI (float_truncate:V2SF (match_operand:V2DF 1 "nonimmediate_operand" "xm")) 0) (const_vector:V2SI [(const_int 0) (const_int 0)])) 0))] "TARGET_SSE2" "cvtpd2ps\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "V4SF")]) (define_insn "cvtps2pd" [(set (match_operand:V2DF 0 "register_operand" "=x") (float_extend:V2DF (vec_select:V2SF (match_operand:V4SF 1 "nonimmediate_operand" "xm") (parallel [(const_int 0) (const_int 1)]))))] "TARGET_SSE2" "cvtps2pd\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "V2DF")]) ;; SSE2 variants of MMX insns ;; MMX arithmetic (define_insn "addv16qi3" [(set (match_operand:V16QI 0 "register_operand" "=x") (plus:V16QI (match_operand:V16QI 1 "register_operand" "%0") (match_operand:V16QI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "paddb\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) (define_insn "addv8hi3" [(set (match_operand:V8HI 0 "register_operand" "=x") (plus:V8HI (match_operand:V8HI 1 "register_operand" "%0") (match_operand:V8HI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "paddw\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) (define_insn "addv4si3" [(set (match_operand:V4SI 0 "register_operand" "=x") (plus:V4SI (match_operand:V4SI 1 "register_operand" "%0") (match_operand:V4SI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "paddd\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) (define_insn "addv2di3" [(set (match_operand:V2DI 0 "register_operand" "=x") (plus:V2DI (match_operand:V2DI 1 "register_operand" "%0") (match_operand:V2DI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "paddq\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) (define_insn "ssaddv16qi3" [(set (match_operand:V16QI 0 "register_operand" "=x") (ss_plus:V16QI (match_operand:V16QI 1 "register_operand" "%0") (match_operand:V16QI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "paddsb\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) (define_insn "ssaddv8hi3" [(set (match_operand:V8HI 0 "register_operand" "=x") (ss_plus:V8HI (match_operand:V8HI 1 "register_operand" "%0") (match_operand:V8HI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "paddsw\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) (define_insn "usaddv16qi3" [(set (match_operand:V16QI 0 "register_operand" "=x") (us_plus:V16QI (match_operand:V16QI 1 "register_operand" "%0") (match_operand:V16QI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "paddusb\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) (define_insn "usaddv8hi3" [(set (match_operand:V8HI 0 "register_operand" "=x") (us_plus:V8HI (match_operand:V8HI 1 "register_operand" "%0") (match_operand:V8HI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "paddusw\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) (define_insn "subv16qi3" [(set (match_operand:V16QI 0 "register_operand" "=x") (minus:V16QI (match_operand:V16QI 1 "register_operand" "0") (match_operand:V16QI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "psubb\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) (define_insn "subv8hi3" [(set (match_operand:V8HI 0 "register_operand" "=x") (minus:V8HI (match_operand:V8HI 1 "register_operand" "0") (match_operand:V8HI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "psubw\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) (define_insn "subv4si3" [(set (match_operand:V4SI 0 "register_operand" "=x") (minus:V4SI (match_operand:V4SI 1 "register_operand" "0") (match_operand:V4SI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "psubd\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) (define_insn "subv2di3" [(set (match_operand:V2DI 0 "register_operand" "=x") (minus:V2DI (match_operand:V2DI 1 "register_operand" "0") (match_operand:V2DI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "psubq\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) (define_insn "sssubv16qi3" [(set (match_operand:V16QI 0 "register_operand" "=x") (ss_minus:V16QI (match_operand:V16QI 1 "register_operand" "0") (match_operand:V16QI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "psubsb\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) (define_insn "sssubv8hi3" [(set (match_operand:V8HI 0 "register_operand" "=x") (ss_minus:V8HI (match_operand:V8HI 1 "register_operand" "0") (match_operand:V8HI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "psubsw\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) (define_insn "ussubv16qi3" [(set (match_operand:V16QI 0 "register_operand" "=x") (us_minus:V16QI (match_operand:V16QI 1 "register_operand" "0") (match_operand:V16QI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "psubusb\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) (define_insn "ussubv8hi3" [(set (match_operand:V8HI 0 "register_operand" "=x") (us_minus:V8HI (match_operand:V8HI 1 "register_operand" "0") (match_operand:V8HI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "psubusw\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) (define_insn "mulv8hi3" [(set (match_operand:V8HI 0 "register_operand" "=x") (mult:V8HI (match_operand:V8HI 1 "register_operand" "0") (match_operand:V8HI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "pmullw\t{%2, %0|%0, %2}" [(set_attr "type" "sseimul") (set_attr "mode" "TI")]) (define_insn "smulv8hi3_highpart" [(set (match_operand:V8HI 0 "register_operand" "=x") (truncate:V8HI (lshiftrt:V8SI (mult:V8SI (sign_extend:V8SI (match_operand:V8HI 1 "register_operand" "0")) (sign_extend:V8SI (match_operand:V8HI 2 "nonimmediate_operand" "xm"))) (const_int 16))))] "TARGET_SSE2" "pmulhw\t{%2, %0|%0, %2}" [(set_attr "type" "sseimul") (set_attr "mode" "TI")]) (define_insn "umulv8hi3_highpart" [(set (match_operand:V8HI 0 "register_operand" "=x") (truncate:V8HI (lshiftrt:V8SI (mult:V8SI (zero_extend:V8SI (match_operand:V8HI 1 "register_operand" "0")) (zero_extend:V8SI (match_operand:V8HI 2 "nonimmediate_operand" "xm"))) (const_int 16))))] "TARGET_SSE2" "pmulhuw\t{%2, %0|%0, %2}" [(set_attr "type" "sseimul") (set_attr "mode" "TI")]) (define_insn "sse2_umulsidi3" [(set (match_operand:DI 0 "register_operand" "=y") (mult:DI (zero_extend:DI (vec_select:SI (match_operand:V2SI 1 "register_operand" "0") (parallel [(const_int 0)]))) (zero_extend:DI (vec_select:SI (match_operand:V2SI 2 "nonimmediate_operand" "ym") (parallel [(const_int 0)])))))] "TARGET_SSE2" "pmuludq\t{%2, %0|%0, %2}" [(set_attr "type" "sseimul") (set_attr "mode" "TI")]) (define_insn "sse2_umulv2siv2di3" [(set (match_operand:V2DI 0 "register_operand" "=x") (mult:V2DI (zero_extend:V2DI (vec_select:V2SI (match_operand:V4SI 1 "register_operand" "0") (parallel [(const_int 0) (const_int 2)]))) (zero_extend:V2DI (vec_select:V2SI (match_operand:V4SI 2 "nonimmediate_operand" "xm") (parallel [(const_int 0) (const_int 2)])))))] "TARGET_SSE2" "pmuludq\t{%2, %0|%0, %2}" [(set_attr "type" "sseimul") (set_attr "mode" "TI")]) (define_insn "sse2_pmaddwd" [(set (match_operand:V4SI 0 "register_operand" "=x") (plus:V4SI (mult:V4SI (sign_extend:V4SI (vec_select:V4HI (match_operand:V8HI 1 "register_operand" "0") (parallel [(const_int 0) (const_int 2) (const_int 4) (const_int 6)]))) (sign_extend:V4SI (vec_select:V4HI (match_operand:V8HI 2 "nonimmediate_operand" "xm") (parallel [(const_int 0) (const_int 2) (const_int 4) (const_int 6)])))) (mult:V4SI (sign_extend:V4SI (vec_select:V4HI (match_dup 1) (parallel [(const_int 1) (const_int 3) (const_int 5) (const_int 7)]))) (sign_extend:V4SI (vec_select:V4HI (match_dup 2) (parallel [(const_int 1) (const_int 3) (const_int 5) (const_int 7)]))))))] "TARGET_SSE2" "pmaddwd\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) ;; Same as pxor, but don't show input operands so that we don't think ;; they are live. (define_insn "sse2_clrti" [(set (match_operand:TI 0 "register_operand" "=x") (const_int 0))] "TARGET_SSE2" { if (get_attr_mode (insn) == MODE_TI) return "pxor\t%0, %0"; else return "xorps\t%0, %0"; } [(set_attr "type" "ssemov") (set_attr "memory" "none") (set (attr "mode") (if_then_else (ne (symbol_ref "optimize_size") (const_int 0)) (const_string "V4SF") (const_string "TI")))]) ;; MMX unsigned averages/sum of absolute differences (define_insn "sse2_uavgv16qi3" [(set (match_operand:V16QI 0 "register_operand" "=x") (ashiftrt:V16QI (plus:V16QI (plus:V16QI (match_operand:V16QI 1 "register_operand" "0") (match_operand:V16QI 2 "nonimmediate_operand" "xm")) (const_vector:V16QI [(const_int 1) (const_int 1) (const_int 1) (const_int 1) (const_int 1) (const_int 1) (const_int 1) (const_int 1) (const_int 1) (const_int 1) (const_int 1) (const_int 1) (const_int 1) (const_int 1) (const_int 1) (const_int 1)])) (const_int 1)))] "TARGET_SSE2" "pavgb\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) (define_insn "sse2_uavgv8hi3" [(set (match_operand:V8HI 0 "register_operand" "=x") (ashiftrt:V8HI (plus:V8HI (plus:V8HI (match_operand:V8HI 1 "register_operand" "0") (match_operand:V8HI 2 "nonimmediate_operand" "xm")) (const_vector:V8HI [(const_int 1) (const_int 1) (const_int 1) (const_int 1) (const_int 1) (const_int 1) (const_int 1) (const_int 1)])) (const_int 1)))] "TARGET_SSE2" "pavgw\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) ;; @@@ this isn't the right representation. (define_insn "sse2_psadbw" [(set (match_operand:V2DI 0 "register_operand" "=x") (unspec:V2DI [(match_operand:V16QI 1 "register_operand" "0") (match_operand:V16QI 2 "nonimmediate_operand" "xm")] UNSPEC_PSADBW))] "TARGET_SSE2" "psadbw\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) ;; MMX insert/extract/shuffle (define_insn "sse2_pinsrw" [(set (match_operand:V8HI 0 "register_operand" "=x") (vec_merge:V8HI (match_operand:V8HI 1 "register_operand" "0") (vec_duplicate:V8HI (truncate:HI (match_operand:SI 2 "nonimmediate_operand" "rm"))) (match_operand:SI 3 "const_0_to_255_operand" "N")))] "TARGET_SSE2" "pinsrw\t{%3, %2, %0|%0, %2, %3}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_pextrw" [(set (match_operand:SI 0 "register_operand" "=r") (zero_extend:SI (vec_select:HI (match_operand:V8HI 1 "register_operand" "x") (parallel [(match_operand:SI 2 "const_0_to_7_operand" "N")]))))] "TARGET_SSE2" "pextrw\t{%2, %1, %0|%0, %1, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_pshufd" [(set (match_operand:V4SI 0 "register_operand" "=x") (unspec:V4SI [(match_operand:V4SI 1 "register_operand" "0") (match_operand:SI 2 "immediate_operand" "i")] UNSPEC_SHUFFLE))] "TARGET_SSE2" "pshufd\t{%2, %1, %0|%0, %1, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_pshuflw" [(set (match_operand:V8HI 0 "register_operand" "=x") (unspec:V8HI [(match_operand:V8HI 1 "register_operand" "0") (match_operand:SI 2 "immediate_operand" "i")] UNSPEC_PSHUFLW))] "TARGET_SSE2" "pshuflw\t{%2, %1, %0|%0, %1, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_pshufhw" [(set (match_operand:V8HI 0 "register_operand" "=x") (unspec:V8HI [(match_operand:V8HI 1 "register_operand" "0") (match_operand:SI 2 "immediate_operand" "i")] UNSPEC_PSHUFHW))] "TARGET_SSE2" "pshufhw\t{%2, %1, %0|%0, %1, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) ;; MMX mask-generating comparisons (define_insn "eqv16qi3" [(set (match_operand:V16QI 0 "register_operand" "=x") (eq:V16QI (match_operand:V16QI 1 "register_operand" "0") (match_operand:V16QI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "pcmpeqb\t{%2, %0|%0, %2}" [(set_attr "type" "ssecmp") (set_attr "mode" "TI")]) (define_insn "eqv8hi3" [(set (match_operand:V8HI 0 "register_operand" "=x") (eq:V8HI (match_operand:V8HI 1 "register_operand" "0") (match_operand:V8HI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "pcmpeqw\t{%2, %0|%0, %2}" [(set_attr "type" "ssecmp") (set_attr "mode" "TI")]) (define_insn "eqv4si3" [(set (match_operand:V4SI 0 "register_operand" "=x") (eq:V4SI (match_operand:V4SI 1 "register_operand" "0") (match_operand:V4SI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "pcmpeqd\t{%2, %0|%0, %2}" [(set_attr "type" "ssecmp") (set_attr "mode" "TI")]) (define_insn "gtv16qi3" [(set (match_operand:V16QI 0 "register_operand" "=x") (gt:V16QI (match_operand:V16QI 1 "register_operand" "0") (match_operand:V16QI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "pcmpgtb\t{%2, %0|%0, %2}" [(set_attr "type" "ssecmp") (set_attr "mode" "TI")]) (define_insn "gtv8hi3" [(set (match_operand:V8HI 0 "register_operand" "=x") (gt:V8HI (match_operand:V8HI 1 "register_operand" "0") (match_operand:V8HI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "pcmpgtw\t{%2, %0|%0, %2}" [(set_attr "type" "ssecmp") (set_attr "mode" "TI")]) (define_insn "gtv4si3" [(set (match_operand:V4SI 0 "register_operand" "=x") (gt:V4SI (match_operand:V4SI 1 "register_operand" "0") (match_operand:V4SI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "pcmpgtd\t{%2, %0|%0, %2}" [(set_attr "type" "ssecmp") (set_attr "mode" "TI")]) ;; MMX max/min insns (define_insn "umaxv16qi3" [(set (match_operand:V16QI 0 "register_operand" "=x") (umax:V16QI (match_operand:V16QI 1 "register_operand" "0") (match_operand:V16QI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "pmaxub\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) (define_insn "smaxv8hi3" [(set (match_operand:V8HI 0 "register_operand" "=x") (smax:V8HI (match_operand:V8HI 1 "register_operand" "0") (match_operand:V8HI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "pmaxsw\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) (define_insn "uminv16qi3" [(set (match_operand:V16QI 0 "register_operand" "=x") (umin:V16QI (match_operand:V16QI 1 "register_operand" "0") (match_operand:V16QI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "pminub\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) (define_insn "sminv8hi3" [(set (match_operand:V8HI 0 "register_operand" "=x") (smin:V8HI (match_operand:V8HI 1 "register_operand" "0") (match_operand:V8HI 2 "nonimmediate_operand" "xm")))] "TARGET_SSE2" "pminsw\t{%2, %0|%0, %2}" [(set_attr "type" "sseiadd") (set_attr "mode" "TI")]) ;; MMX shifts (define_insn "ashrv8hi3" [(set (match_operand:V8HI 0 "register_operand" "=x") (ashiftrt:V8HI (match_operand:V8HI 1 "register_operand" "0") (match_operand:SI 2 "nonmemory_operand" "xi")))] "TARGET_SSE2" "psraw\t{%2, %0|%0, %2}" [(set_attr "type" "sseishft") (set_attr "mode" "TI")]) (define_insn "ashrv4si3" [(set (match_operand:V4SI 0 "register_operand" "=x") (ashiftrt:V4SI (match_operand:V4SI 1 "register_operand" "0") (match_operand:SI 2 "nonmemory_operand" "xi")))] "TARGET_SSE2" "psrad\t{%2, %0|%0, %2}" [(set_attr "type" "sseishft") (set_attr "mode" "TI")]) (define_insn "lshrv8hi3" [(set (match_operand:V8HI 0 "register_operand" "=x") (lshiftrt:V8HI (match_operand:V8HI 1 "register_operand" "0") (match_operand:SI 2 "nonmemory_operand" "xi")))] "TARGET_SSE2" "psrlw\t{%2, %0|%0, %2}" [(set_attr "type" "sseishft") (set_attr "mode" "TI")]) (define_insn "lshrv4si3" [(set (match_operand:V4SI 0 "register_operand" "=x") (lshiftrt:V4SI (match_operand:V4SI 1 "register_operand" "0") (match_operand:SI 2 "nonmemory_operand" "xi")))] "TARGET_SSE2" "psrld\t{%2, %0|%0, %2}" [(set_attr "type" "sseishft") (set_attr "mode" "TI")]) (define_insn "lshrv2di3" [(set (match_operand:V2DI 0 "register_operand" "=x") (lshiftrt:V2DI (match_operand:V2DI 1 "register_operand" "0") (match_operand:SI 2 "nonmemory_operand" "xi")))] "TARGET_SSE2" "psrlq\t{%2, %0|%0, %2}" [(set_attr "type" "sseishft") (set_attr "mode" "TI")]) (define_insn "ashlv8hi3" [(set (match_operand:V8HI 0 "register_operand" "=x") (ashift:V8HI (match_operand:V8HI 1 "register_operand" "0") (match_operand:SI 2 "nonmemory_operand" "xi")))] "TARGET_SSE2" "psllw\t{%2, %0|%0, %2}" [(set_attr "type" "sseishft") (set_attr "mode" "TI")]) (define_insn "ashlv4si3" [(set (match_operand:V4SI 0 "register_operand" "=x") (ashift:V4SI (match_operand:V4SI 1 "register_operand" "0") (match_operand:SI 2 "nonmemory_operand" "xi")))] "TARGET_SSE2" "pslld\t{%2, %0|%0, %2}" [(set_attr "type" "sseishft") (set_attr "mode" "TI")]) (define_insn "ashlv2di3" [(set (match_operand:V2DI 0 "register_operand" "=x") (ashift:V2DI (match_operand:V2DI 1 "register_operand" "0") (match_operand:SI 2 "nonmemory_operand" "xi")))] "TARGET_SSE2" "psllq\t{%2, %0|%0, %2}" [(set_attr "type" "sseishft") (set_attr "mode" "TI")]) (define_insn "ashrv8hi3_ti" [(set (match_operand:V8HI 0 "register_operand" "=x") (ashiftrt:V8HI (match_operand:V8HI 1 "register_operand" "0") (subreg:SI (match_operand:V2DI 2 "nonmemory_operand" "xi") 0)))] "TARGET_SSE2" "psraw\t{%2, %0|%0, %2}" [(set_attr "type" "sseishft") (set_attr "mode" "TI")]) (define_insn "ashrv4si3_ti" [(set (match_operand:V4SI 0 "register_operand" "=x") (ashiftrt:V4SI (match_operand:V4SI 1 "register_operand" "0") (subreg:SI (match_operand:V2DI 2 "nonmemory_operand" "xi") 0)))] "TARGET_SSE2" "psrad\t{%2, %0|%0, %2}" [(set_attr "type" "sseishft") (set_attr "mode" "TI")]) (define_insn "lshrv8hi3_ti" [(set (match_operand:V8HI 0 "register_operand" "=x") (lshiftrt:V8HI (match_operand:V8HI 1 "register_operand" "0") (subreg:SI (match_operand:V2DI 2 "nonmemory_operand" "xi") 0)))] "TARGET_SSE2" "psrlw\t{%2, %0|%0, %2}" [(set_attr "type" "sseishft") (set_attr "mode" "TI")]) (define_insn "lshrv4si3_ti" [(set (match_operand:V4SI 0 "register_operand" "=x") (lshiftrt:V4SI (match_operand:V4SI 1 "register_operand" "0") (subreg:SI (match_operand:V2DI 2 "nonmemory_operand" "xi") 0)))] "TARGET_SSE2" "psrld\t{%2, %0|%0, %2}" [(set_attr "type" "sseishft") (set_attr "mode" "TI")]) (define_insn "lshrv2di3_ti" [(set (match_operand:V2DI 0 "register_operand" "=x") (lshiftrt:V2DI (match_operand:V2DI 1 "register_operand" "0") (subreg:SI (match_operand:V2DI 2 "nonmemory_operand" "xi") 0)))] "TARGET_SSE2" "psrlq\t{%2, %0|%0, %2}" [(set_attr "type" "sseishft") (set_attr "mode" "TI")]) (define_insn "ashlv8hi3_ti" [(set (match_operand:V8HI 0 "register_operand" "=x") (ashift:V8HI (match_operand:V8HI 1 "register_operand" "0") (subreg:SI (match_operand:V2DI 2 "nonmemory_operand" "xi") 0)))] "TARGET_SSE2" "psllw\t{%2, %0|%0, %2}" [(set_attr "type" "sseishft") (set_attr "mode" "TI")]) (define_insn "ashlv4si3_ti" [(set (match_operand:V4SI 0 "register_operand" "=x") (ashift:V4SI (match_operand:V4SI 1 "register_operand" "0") (subreg:SI (match_operand:V2DI 2 "nonmemory_operand" "xi") 0)))] "TARGET_SSE2" "pslld\t{%2, %0|%0, %2}" [(set_attr "type" "sseishft") (set_attr "mode" "TI")]) (define_insn "ashlv2di3_ti" [(set (match_operand:V2DI 0 "register_operand" "=x") (ashift:V2DI (match_operand:V2DI 1 "register_operand" "0") (subreg:SI (match_operand:V2DI 2 "nonmemory_operand" "xi") 0)))] "TARGET_SSE2" "psllq\t{%2, %0|%0, %2}" [(set_attr "type" "sseishft") (set_attr "mode" "TI")]) ;; See logical MMX insns for the reason for the unspec. Strictly speaking ;; we wouldn't need here it since we never generate TImode arithmetic. ;; There has to be some kind of prize for the weirdest new instruction... (define_insn "sse2_ashlti3" [(set (match_operand:TI 0 "register_operand" "=x") (unspec:TI [(ashift:TI (match_operand:TI 1 "register_operand" "0") (mult:SI (match_operand:SI 2 "immediate_operand" "i") (const_int 8)))] UNSPEC_NOP))] "TARGET_SSE2" "pslldq\t{%2, %0|%0, %2}" [(set_attr "type" "sseishft") (set_attr "mode" "TI")]) (define_insn "sse2_lshrti3" [(set (match_operand:TI 0 "register_operand" "=x") (unspec:TI [(lshiftrt:TI (match_operand:TI 1 "register_operand" "0") (mult:SI (match_operand:SI 2 "immediate_operand" "i") (const_int 8)))] UNSPEC_NOP))] "TARGET_SSE2" "psrldq\t{%2, %0|%0, %2}" [(set_attr "type" "sseishft") (set_attr "mode" "TI")]) ;; SSE unpack (define_insn "sse2_unpckhpd" [(set (match_operand:V2DF 0 "register_operand" "=x") (vec_concat:V2DF (vec_select:DF (match_operand:V2DF 1 "register_operand" "0") (parallel [(const_int 1)])) (vec_select:DF (match_operand:V2DF 2 "register_operand" "x") (parallel [(const_int 1)]))))] "TARGET_SSE2" "unpckhpd\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "V2DF")]) (define_insn "sse2_unpcklpd" [(set (match_operand:V2DF 0 "register_operand" "=x") (vec_concat:V2DF (vec_select:DF (match_operand:V2DF 1 "register_operand" "0") (parallel [(const_int 0)])) (vec_select:DF (match_operand:V2DF 2 "register_operand" "x") (parallel [(const_int 0)]))))] "TARGET_SSE2" "unpcklpd\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "V2DF")]) ;; MMX pack/unpack insns. (define_insn "sse2_packsswb" [(set (match_operand:V16QI 0 "register_operand" "=x") (vec_concat:V16QI (ss_truncate:V8QI (match_operand:V8HI 1 "register_operand" "0")) (ss_truncate:V8QI (match_operand:V8HI 2 "register_operand" "x"))))] "TARGET_SSE2" "packsswb\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_packssdw" [(set (match_operand:V8HI 0 "register_operand" "=x") (vec_concat:V8HI (ss_truncate:V4HI (match_operand:V4SI 1 "register_operand" "0")) (ss_truncate:V4HI (match_operand:V4SI 2 "register_operand" "x"))))] "TARGET_SSE2" "packssdw\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_packuswb" [(set (match_operand:V16QI 0 "register_operand" "=x") (vec_concat:V16QI (us_truncate:V8QI (match_operand:V8HI 1 "register_operand" "0")) (us_truncate:V8QI (match_operand:V8HI 2 "register_operand" "x"))))] "TARGET_SSE2" "packuswb\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_punpckhbw" [(set (match_operand:V16QI 0 "register_operand" "=x") (vec_merge:V16QI (vec_select:V16QI (match_operand:V16QI 1 "register_operand" "0") (parallel [(const_int 8) (const_int 0) (const_int 9) (const_int 1) (const_int 10) (const_int 2) (const_int 11) (const_int 3) (const_int 12) (const_int 4) (const_int 13) (const_int 5) (const_int 14) (const_int 6) (const_int 15) (const_int 7)])) (vec_select:V16QI (match_operand:V16QI 2 "register_operand" "x") (parallel [(const_int 0) (const_int 8) (const_int 1) (const_int 9) (const_int 2) (const_int 10) (const_int 3) (const_int 11) (const_int 4) (const_int 12) (const_int 5) (const_int 13) (const_int 6) (const_int 14) (const_int 7) (const_int 15)])) (const_int 21845)))] "TARGET_SSE2" "punpckhbw\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_punpckhwd" [(set (match_operand:V8HI 0 "register_operand" "=x") (vec_merge:V8HI (vec_select:V8HI (match_operand:V8HI 1 "register_operand" "0") (parallel [(const_int 4) (const_int 0) (const_int 5) (const_int 1) (const_int 6) (const_int 2) (const_int 7) (const_int 3)])) (vec_select:V8HI (match_operand:V8HI 2 "register_operand" "x") (parallel [(const_int 0) (const_int 4) (const_int 1) (const_int 5) (const_int 2) (const_int 6) (const_int 3) (const_int 7)])) (const_int 85)))] "TARGET_SSE2" "punpckhwd\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_punpckhdq" [(set (match_operand:V4SI 0 "register_operand" "=x") (vec_merge:V4SI (vec_select:V4SI (match_operand:V4SI 1 "register_operand" "0") (parallel [(const_int 2) (const_int 0) (const_int 3) (const_int 1)])) (vec_select:V4SI (match_operand:V4SI 2 "register_operand" "x") (parallel [(const_int 0) (const_int 2) (const_int 1) (const_int 3)])) (const_int 5)))] "TARGET_SSE2" "punpckhdq\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_punpcklbw" [(set (match_operand:V16QI 0 "register_operand" "=x") (vec_merge:V16QI (vec_select:V16QI (match_operand:V16QI 1 "register_operand" "0") (parallel [(const_int 0) (const_int 8) (const_int 1) (const_int 9) (const_int 2) (const_int 10) (const_int 3) (const_int 11) (const_int 4) (const_int 12) (const_int 5) (const_int 13) (const_int 6) (const_int 14) (const_int 7) (const_int 15)])) (vec_select:V16QI (match_operand:V16QI 2 "register_operand" "x") (parallel [(const_int 8) (const_int 0) (const_int 9) (const_int 1) (const_int 10) (const_int 2) (const_int 11) (const_int 3) (const_int 12) (const_int 4) (const_int 13) (const_int 5) (const_int 14) (const_int 6) (const_int 15) (const_int 7)])) (const_int 21845)))] "TARGET_SSE2" "punpcklbw\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_punpcklwd" [(set (match_operand:V8HI 0 "register_operand" "=x") (vec_merge:V8HI (vec_select:V8HI (match_operand:V8HI 1 "register_operand" "0") (parallel [(const_int 0) (const_int 4) (const_int 1) (const_int 5) (const_int 2) (const_int 6) (const_int 3) (const_int 7)])) (vec_select:V8HI (match_operand:V8HI 2 "register_operand" "x") (parallel [(const_int 4) (const_int 0) (const_int 5) (const_int 1) (const_int 6) (const_int 2) (const_int 7) (const_int 3)])) (const_int 85)))] "TARGET_SSE2" "punpcklwd\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_punpckldq" [(set (match_operand:V4SI 0 "register_operand" "=x") (vec_merge:V4SI (vec_select:V4SI (match_operand:V4SI 1 "register_operand" "0") (parallel [(const_int 0) (const_int 2) (const_int 1) (const_int 3)])) (vec_select:V4SI (match_operand:V4SI 2 "register_operand" "x") (parallel [(const_int 2) (const_int 0) (const_int 3) (const_int 1)])) (const_int 5)))] "TARGET_SSE2" "punpckldq\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_punpcklqdq" [(set (match_operand:V2DI 0 "register_operand" "=x") (vec_merge:V2DI (vec_select:V2DI (match_operand:V2DI 2 "register_operand" "x") (parallel [(const_int 1) (const_int 0)])) (match_operand:V2DI 1 "register_operand" "0") (const_int 1)))] "TARGET_SSE2" "punpcklqdq\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_punpckhqdq" [(set (match_operand:V2DI 0 "register_operand" "=x") (vec_merge:V2DI (match_operand:V2DI 1 "register_operand" "0") (vec_select:V2DI (match_operand:V2DI 2 "register_operand" "x") (parallel [(const_int 1) (const_int 0)])) (const_int 1)))] "TARGET_SSE2" "punpckhqdq\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) ;; SSE2 moves (define_insn "sse2_movapd" [(set (match_operand:V2DF 0 "nonimmediate_operand" "=x,m") (unspec:V2DF [(match_operand:V2DF 1 "nonimmediate_operand" "xm,x")] UNSPEC_MOVA))] "TARGET_SSE2 && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "movapd\t{%1, %0|%0, %1}" [(set_attr "type" "ssemov") (set_attr "mode" "V2DF")]) (define_insn "sse2_movupd" [(set (match_operand:V2DF 0 "nonimmediate_operand" "=x,m") (unspec:V2DF [(match_operand:V2DF 1 "nonimmediate_operand" "xm,x")] UNSPEC_MOVU))] "TARGET_SSE2 && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "movupd\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "V2DF")]) (define_insn "sse2_movdqa" [(set (match_operand:V16QI 0 "nonimmediate_operand" "=x,m") (unspec:V16QI [(match_operand:V16QI 1 "nonimmediate_operand" "xm,x")] UNSPEC_MOVA))] "TARGET_SSE2 && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "movdqa\t{%1, %0|%0, %1}" [(set_attr "type" "ssemov") (set_attr "mode" "TI")]) (define_insn "sse2_movdqu" [(set (match_operand:V16QI 0 "nonimmediate_operand" "=x,m") (unspec:V16QI [(match_operand:V16QI 1 "nonimmediate_operand" "xm,x")] UNSPEC_MOVU))] "TARGET_SSE2 && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "movdqu\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_movdq2q" [(set (match_operand:DI 0 "nonimmediate_operand" "=m,y") (vec_select:DI (match_operand:V2DI 1 "register_operand" "x,x") (parallel [(const_int 0)])))] "TARGET_SSE2 && !TARGET_64BIT" "@ movq\t{%1, %0|%0, %1} movdq2q\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_movdq2q_rex64" [(set (match_operand:DI 0 "nonimmediate_operand" "=m,y,r") (vec_select:DI (match_operand:V2DI 1 "register_operand" "x,x,x") (parallel [(const_int 0)])))] "TARGET_SSE2 && TARGET_64BIT" "@ movq\t{%1, %0|%0, %1} movdq2q\t{%1, %0|%0, %1} movd\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_movq2dq" [(set (match_operand:V2DI 0 "register_operand" "=x,?x") (vec_concat:V2DI (match_operand:DI 1 "nonimmediate_operand" "m,y") (const_int 0)))] "TARGET_SSE2 && !TARGET_64BIT" "@ movq\t{%1, %0|%0, %1} movq2dq\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt,ssemov") (set_attr "mode" "TI")]) (define_insn "sse2_movq2dq_rex64" [(set (match_operand:V2DI 0 "register_operand" "=x,?x,?x") (vec_concat:V2DI (match_operand:DI 1 "nonimmediate_operand" "m,y,r") (const_int 0)))] "TARGET_SSE2 && TARGET_64BIT" "@ movq\t{%1, %0|%0, %1} movq2dq\t{%1, %0|%0, %1} movd\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt,ssemov,ssecvt") (set_attr "mode" "TI")]) (define_insn "sse2_movq" [(set (match_operand:V2DI 0 "register_operand" "=x") (vec_concat:V2DI (vec_select:DI (match_operand:V2DI 1 "nonimmediate_operand" "xm") (parallel [(const_int 0)])) (const_int 0)))] "TARGET_SSE2" "movq\t{%1, %0|%0, %1}" [(set_attr "type" "ssemov") (set_attr "mode" "TI")]) (define_insn "sse2_loadd" [(set (match_operand:V4SI 0 "register_operand" "=x") (vec_merge:V4SI (vec_duplicate:V4SI (match_operand:SI 1 "nonimmediate_operand" "mr")) (const_vector:V4SI [(const_int 0) (const_int 0) (const_int 0) (const_int 0)]) (const_int 1)))] "TARGET_SSE2" "movd\t{%1, %0|%0, %1}" [(set_attr "type" "ssemov") (set_attr "mode" "TI")]) (define_insn "sse2_stored" [(set (match_operand:SI 0 "nonimmediate_operand" "=mr") (vec_select:SI (match_operand:V4SI 1 "register_operand" "x") (parallel [(const_int 0)])))] "TARGET_SSE2" "movd\t{%1, %0|%0, %1}" [(set_attr "type" "ssemov") (set_attr "mode" "TI")]) (define_insn "sse2_movhpd" [(set (match_operand:V2DF 0 "nonimmediate_operand" "=x,m") (vec_merge:V2DF (match_operand:V2DF 1 "nonimmediate_operand" "0,0") (match_operand:V2DF 2 "nonimmediate_operand" "m,x") (const_int 2)))] "TARGET_SSE2 && (GET_CODE (operands[1]) == MEM || GET_CODE (operands[2]) == MEM)" "movhpd\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "V2DF")]) (define_expand "sse2_loadsd" [(match_operand:V2DF 0 "register_operand" "") (match_operand:DF 1 "memory_operand" "")] "TARGET_SSE2" { emit_insn (gen_sse2_loadsd_1 (operands[0], operands[1], CONST0_RTX (V2DFmode))); DONE; }) (define_insn "sse2_loadsd_1" [(set (match_operand:V2DF 0 "register_operand" "=x") (vec_merge:V2DF (vec_duplicate:V2DF (match_operand:DF 1 "memory_operand" "m")) (match_operand:V2DF 2 "const0_operand" "X") (const_int 1)))] "TARGET_SSE2" "movsd\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "DF")]) (define_insn "sse2_movsd" [(set (match_operand:V2DF 0 "nonimmediate_operand" "=x,x,m") (vec_merge:V2DF (match_operand:V2DF 1 "nonimmediate_operand" "0,0,0") (match_operand:V2DF 2 "nonimmediate_operand" "x,m,x") (const_int 1)))] "TARGET_SSE2 && ix86_binary_operator_ok (UNKNOWN, V2DFmode, operands)" "@movsd\t{%2, %0|%0, %2} movlpd\t{%2, %0|%0, %2} movlpd\t{%2, %0|%0, %2}" [(set_attr "type" "ssecvt") (set_attr "mode" "DF,V2DF,V2DF")]) (define_insn "sse2_storesd" [(set (match_operand:DF 0 "memory_operand" "=m") (vec_select:DF (match_operand:V2DF 1 "register_operand" "x") (parallel [(const_int 0)])))] "TARGET_SSE2" "movsd\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "DF")]) (define_insn "sse2_shufpd" [(set (match_operand:V2DF 0 "register_operand" "=x") (unspec:V2DF [(match_operand:V2DF 1 "register_operand" "0") (match_operand:V2DF 2 "nonimmediate_operand" "xm") (match_operand:SI 3 "immediate_operand" "i")] UNSPEC_SHUFFLE))] "TARGET_SSE2" ;; @@@ check operand order for intel/nonintel syntax "shufpd\t{%3, %2, %0|%0, %2, %3}" [(set_attr "type" "ssecvt") (set_attr "mode" "V2DF")]) (define_insn "sse2_clflush" [(unspec_volatile [(match_operand 0 "address_operand" "p")] UNSPECV_CLFLUSH)] "TARGET_SSE2" "clflush %0" [(set_attr "type" "sse") (set_attr "memory" "unknown")]) (define_expand "sse2_mfence" [(set (match_dup 0) (unspec:BLK [(match_dup 0)] UNSPEC_MFENCE))] "TARGET_SSE2" { operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); MEM_VOLATILE_P (operands[0]) = 1; }) (define_insn "*mfence_insn" [(set (match_operand:BLK 0 "" "") (unspec:BLK [(match_dup 0)] UNSPEC_MFENCE))] "TARGET_SSE2" "mfence" [(set_attr "type" "sse") (set_attr "memory" "unknown")]) (define_expand "sse2_lfence" [(set (match_dup 0) (unspec:BLK [(match_dup 0)] UNSPEC_LFENCE))] "TARGET_SSE2" { operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); MEM_VOLATILE_P (operands[0]) = 1; }) (define_insn "*lfence_insn" [(set (match_operand:BLK 0 "" "") (unspec:BLK [(match_dup 0)] UNSPEC_LFENCE))] "TARGET_SSE2" "lfence" [(set_attr "type" "sse") (set_attr "memory" "unknown")]) ;; SSE3 (define_insn "mwait" [(unspec_volatile [(match_operand:SI 0 "register_operand" "a") (match_operand:SI 1 "register_operand" "c")] UNSPECV_MWAIT)] "TARGET_SSE3" "mwait\t%0, %1" [(set_attr "length" "3")]) (define_insn "monitor" [(unspec_volatile [(match_operand:SI 0 "register_operand" "a") (match_operand:SI 1 "register_operand" "c") (match_operand:SI 2 "register_operand" "d")] UNSPECV_MONITOR)] "TARGET_SSE3" "monitor\t%0, %1, %2" [(set_attr "length" "3")]) ;; SSE3 arithmetic (define_insn "addsubv4sf3" [(set (match_operand:V4SF 0 "register_operand" "=x") (unspec:V4SF [(match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "nonimmediate_operand" "xm")] UNSPEC_ADDSUB))] "TARGET_SSE3" "addsubps\t{%2, %0|%0, %2}" [(set_attr "type" "sseadd") (set_attr "mode" "V4SF")]) (define_insn "addsubv2df3" [(set (match_operand:V2DF 0 "register_operand" "=x") (unspec:V2DF [(match_operand:V2DF 1 "register_operand" "0") (match_operand:V2DF 2 "nonimmediate_operand" "xm")] UNSPEC_ADDSUB))] "TARGET_SSE3" "addsubpd\t{%2, %0|%0, %2}" [(set_attr "type" "sseadd") (set_attr "mode" "V2DF")]) (define_insn "haddv4sf3" [(set (match_operand:V4SF 0 "register_operand" "=x") (unspec:V4SF [(match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "nonimmediate_operand" "xm")] UNSPEC_HADD))] "TARGET_SSE3" "haddps\t{%2, %0|%0, %2}" [(set_attr "type" "sseadd") (set_attr "mode" "V4SF")]) (define_insn "haddv2df3" [(set (match_operand:V2DF 0 "register_operand" "=x") (unspec:V2DF [(match_operand:V2DF 1 "register_operand" "0") (match_operand:V2DF 2 "nonimmediate_operand" "xm")] UNSPEC_HADD))] "TARGET_SSE3" "haddpd\t{%2, %0|%0, %2}" [(set_attr "type" "sseadd") (set_attr "mode" "V2DF")]) (define_insn "hsubv4sf3" [(set (match_operand:V4SF 0 "register_operand" "=x") (unspec:V4SF [(match_operand:V4SF 1 "register_operand" "0") (match_operand:V4SF 2 "nonimmediate_operand" "xm")] UNSPEC_HSUB))] "TARGET_SSE3" "hsubps\t{%2, %0|%0, %2}" [(set_attr "type" "sseadd") (set_attr "mode" "V4SF")]) (define_insn "hsubv2df3" [(set (match_operand:V2DF 0 "register_operand" "=x") (unspec:V2DF [(match_operand:V2DF 1 "register_operand" "0") (match_operand:V2DF 2 "nonimmediate_operand" "xm")] UNSPEC_HSUB))] "TARGET_SSE3" "hsubpd\t{%2, %0|%0, %2}" [(set_attr "type" "sseadd") (set_attr "mode" "V2DF")]) (define_insn "movshdup" [(set (match_operand:V4SF 0 "register_operand" "=x") (unspec:V4SF [(match_operand:V4SF 1 "nonimmediate_operand" "xm")] UNSPEC_MOVSHDUP))] "TARGET_SSE3" "movshdup\t{%1, %0|%0, %1}" [(set_attr "type" "sse") (set_attr "mode" "V4SF")]) (define_insn "movsldup" [(set (match_operand:V4SF 0 "register_operand" "=x") (unspec:V4SF [(match_operand:V4SF 1 "nonimmediate_operand" "xm")] UNSPEC_MOVSLDUP))] "TARGET_SSE3" "movsldup\t{%1, %0|%0, %1}" [(set_attr "type" "sse") (set_attr "mode" "V4SF")]) (define_insn "lddqu" [(set (match_operand:V16QI 0 "register_operand" "=x") (unspec:V16QI [(match_operand:V16QI 1 "memory_operand" "m")] UNSPEC_LDQQU))] "TARGET_SSE3" "lddqu\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "TI")]) (define_insn "loadddup" [(set (match_operand:V2DF 0 "register_operand" "=x") (vec_duplicate:V2DF (match_operand:DF 1 "memory_operand" "m")))] "TARGET_SSE3" "movddup\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "DF")]) (define_insn "movddup" [(set (match_operand:V2DF 0 "register_operand" "=x") (vec_duplicate:V2DF (vec_select:DF (match_operand:V2DF 1 "register_operand" "x") (parallel [(const_int 0)]))))] "TARGET_SSE3" "movddup\t{%1, %0|%0, %1}" [(set_attr "type" "ssecvt") (set_attr "mode" "DF")]) diff --git a/contrib/gcc/config/x-linux b/contrib/gcc/config/x-linux new file mode 100644 index 000000000000..d14586b0b363 --- /dev/null +++ b/contrib/gcc/config/x-linux @@ -0,0 +1,4 @@ +host-linux.o : $(srcdir)/config/host-linux.c $(CONFIG_H) $(SYSTEM_H) \ + coretypes.h hosthooks.h hosthooks-def.h + $(CC) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \ + $(srcdir)/config/host-linux.c diff --git a/contrib/gcc/configure b/contrib/gcc/configure index bd8a927d528b..91f9326d9b22 100755 --- a/contrib/gcc/configure +++ b/contrib/gcc/configure @@ -1,13859 +1,13898 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.59. # # Copyright (C) 2003 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then set -o posix fi DUALCASE=1; export DUALCASE # for MKS sh # Support unset when possible. if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # Work around bugs in pre-3.0 UWIN ksh. $as_unset ENV MAIL MAILPATH PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)$' \| \ . : '\(.\)' 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } /^X\/\(\/\/\)$/{ s//\1/; q; } /^X\/\(\/\).*/{ s//\1/; q; } s/.*/./; q'` # PATH needs CR, and LINENO needs CR and PATH. # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" || { # Find who we are. Look in the path if we contain no path at all # relative or not. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 { (exit 1); exit 1; }; } fi case $CONFIG_SHELL in '') as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for as_base in sh bash ksh sh5; do case $as_dir in /*) if ("$as_dir/$as_base" -c ' as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } CONFIG_SHELL=$as_dir/$as_base export CONFIG_SHELL exec "$CONFIG_SHELL" "$0" ${1+"$@"} fi;; esac done done ;; esac # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line before each line; the second 'sed' does the real # work. The second script uses 'N' to pair each line-number line # with the numbered line, and appends trailing '-' during # substitution so that $LINENO is not a special case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) sed '=' <$as_myself | sed ' N s,$,-, : loop s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, t loop s,-$,, s,^['$as_cr_digits']*\n,, ' >$as_me.lineno && chmod +x $as_me.lineno || { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensible to this). . ./$as_me.lineno # Exit status is that of the last command. exit } case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in *c*,-n*) ECHO_N= ECHO_C=' ' ECHO_T=' ' ;; *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; *) ECHO_N= ECHO_C='\c' ECHO_T= ;; esac if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then # We could just check for DJGPP; but this test a) works b) is more generic # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). if test -f conf$$.exe; then # Don't use ln at all; we don't have any links as_ln_s='cp -p' else as_ln_s='ln -s' fi elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.file if mkdir -p . 2>/dev/null; then as_mkdir_p=: else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_executable_p="test -f" # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" # IFS # We need space, tab and new line, in precisely that order. as_nl=' ' IFS=" $as_nl" # CDPATH. $as_unset CDPATH # Name of the host. # hostname on some systems (SVR3.2, Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` exec 6>&1 # # Initializations. # ac_default_prefix=/usr/local ac_config_libobj_dir=. cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= SHELL=${CONFIG_SHELL-/bin/sh} # Maximum number of lines to put in a shell here document. # This variable seems obsolete. It should probably be removed, and # only ac_max_sed_lines should be used. : ${ac_max_here_lines=38} # Identity of this package. PACKAGE_NAME= PACKAGE_TARNAME= PACKAGE_VERSION= PACKAGE_STRING= PACKAGE_BUGREPORT= ac_unique_file="tree.c" # Factoring default headers for most tests. ac_includes_default="\ #include #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_STAT_H # include #endif #if STDC_HEADERS # include # include #else # if HAVE_STDLIB_H # include # endif #endif #if HAVE_STRING_H # if !STDC_HEADERS && HAVE_MEMORY_H # include # endif # include #endif #if HAVE_STRINGS_H # include #endif #if HAVE_INTTYPES_H # include #else # if HAVE_STDINT_H # include # endif #endif #if HAVE_UNISTD_H # include #endif" ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os build_subdir host_subdir target_subdir GENINSRC CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT NO_MINUS_C_MINUS_O OUTPUT_OPTION CPP GNATBIND ac_ct_GNATBIND strict1_warn warn_cflags WERROR nocommon_flag EGREP valgrind_path valgrind_path_defines valgrind_command coverage_flags enable_multilib enable_shared TARGET_SYSTEM_ROOT TARGET_SYSTEM_ROOT_DEFINE CROSS_SYSTEM_HEADER_DIR onestep SET_MAKE AWK LN LN_S RANLIB ac_ct_RANLIB INSTALL INSTALL_PROGRAM INSTALL_DATA make_compare_target have_mktemp_command MAKEINFO BUILD_INFO GENERATED_MANPAGES FLEX BISON stage1_cflags COLLECT2_LIBS GNAT_LIBEXC LDEXP_LIB TARGET_GETGROUPS_T LIBICONV LIBICONV_DEP manext objext extra_modes_file FORBUILD PACKAGE VERSION USE_NLS LIBINTL LIBINTL_DEP INCINTL XGETTEXT GMSGFMT POSUB CATALOGS CROSS ALL SYSTEM_HEADER_DIR inhibit_libc BUILD_PREFIX BUILD_PREFIX_1 CC_FOR_BUILD BUILD_CFLAGS STMP_FIXINC STMP_FIXPROTO libgcc_visibility gthread_flags GGC zlibdir zlibinc MAINT gcc_tooldir dollar slibdir objdir subdirs srcdir all_boot_languages all_compilers all_gtfiles all_gtfiles_files_langs all_gtfiles_files_files all_lang_makefrags all_lang_makefiles all_languages all_stagestuff build_exeext build_install_headers_dir build_xm_file_list build_xm_include_list build_xm_defines check_languages cc_set_by_configure quoted_cc_set_by_configure cpp_install_dir xmake_file tmake_file extra_gcc_objs extra_headers_list extra_objs extra_parts extra_passes extra_programs float_h_file gcc_config_arguments gcc_gxx_include_dir libstdcxx_incdir gcc_version gcc_version_full gcc_version_trigger host_exeext host_xm_file_list host_xm_include_list host_xm_defines out_host_hook_obj install lang_opt_files lang_specs_files lang_tree_files local_prefix md_file objc_boehm_gc out_file out_object_file stage_prefix_set_by_configure quoted_stage_prefix_set_by_configure symbolic_link thread_file tm_file_list tm_include_list tm_defines tm_p_file_list tm_p_include_list xm_file_list xm_include_list xm_defines target_noncanonical c_target_objs cxx_target_objs target_cpu_default set_gcc_lib_path LIBOBJS LTLIBOBJS' ac_subst_files='language_hooks' # Initialize some variables set by options. ac_init_help= ac_init_version=false # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datadir='${prefix}/share' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' libdir='${exec_prefix}/lib' includedir='${prefix}/include' oldincludedir='/usr/include' infodir='${prefix}/info' mandir='${prefix}/man' ac_prev= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval "$ac_prev=\$ac_option" ac_prev= continue fi ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_option in -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad | --data | --dat | --da) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ | --da=*) datadir=$ac_optarg ;; -disable-* | --disable-*) ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid feature name: $ac_feature" >&2 { (exit 1); exit 1; }; } ac_feature=`echo $ac_feature | sed 's/-/_/g'` eval "enable_$ac_feature=no" ;; -enable-* | --enable-*) ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid feature name: $ac_feature" >&2 { (exit 1); exit 1; }; } ac_feature=`echo $ac_feature | sed 's/-/_/g'` case $ac_option in *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; *) ac_optarg=yes ;; esac eval "enable_$ac_feature='$ac_optarg'" ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst \ | --locals | --local | --loca | --loc | --lo) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* \ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid package name: $ac_package" >&2 { (exit 1); exit 1; }; } ac_package=`echo $ac_package| sed 's/-/_/g'` case $ac_option in *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; *) ac_optarg=yes ;; esac eval "with_$ac_package='$ac_optarg'" ;; -without-* | --without-*) ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid package name: $ac_package" >&2 { (exit 1); exit 1; }; } ac_package=`echo $ac_package | sed 's/-/_/g'` eval "with_$ac_package=no" ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) { echo "$as_me: error: unrecognized option: $ac_option Try \`$0 --help' for more information." >&2 { (exit 1); exit 1; }; } ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 { (exit 1); exit 1; }; } ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` eval "$ac_envvar='$ac_optarg'" export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` { echo "$as_me: error: missing argument to $ac_option" >&2 { (exit 1); exit 1; }; } fi # Be sure to have absolute paths. for ac_var in exec_prefix prefix do eval ac_val=$`echo $ac_var` case $ac_val in [\\/$]* | ?:[\\/]* | NONE | '' ) ;; *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 { (exit 1); exit 1; }; };; esac done # Be sure to have absolute paths. for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ localstatedir libdir includedir oldincludedir infodir mandir do eval ac_val=$`echo $ac_var` case $ac_val in [\\/$]* | ?:[\\/]* ) ;; *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 { (exit 1); exit 1; }; };; esac done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. If a cross compiler is detected then cross compile mode will be used." >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then its parent. ac_confdir=`(dirname "$0") 2>/dev/null || $as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$0" : 'X\(//\)[^/]' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$0" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` srcdir=$ac_confdir if test ! -r $srcdir/$ac_unique_file; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r $srcdir/$ac_unique_file; then if test "$ac_srcdir_defaulted" = yes; then { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 { (exit 1); exit 1; }; } else { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 { (exit 1); exit 1; }; } fi fi (cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 { (exit 1); exit 1; }; } srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` ac_env_build_alias_set=${build_alias+set} ac_env_build_alias_value=$build_alias ac_cv_env_build_alias_set=${build_alias+set} ac_cv_env_build_alias_value=$build_alias ac_env_host_alias_set=${host_alias+set} ac_env_host_alias_value=$host_alias ac_cv_env_host_alias_set=${host_alias+set} ac_cv_env_host_alias_value=$host_alias ac_env_target_alias_set=${target_alias+set} ac_env_target_alias_value=$target_alias ac_cv_env_target_alias_set=${target_alias+set} ac_cv_env_target_alias_value=$target_alias ac_env_CC_set=${CC+set} ac_env_CC_value=$CC ac_cv_env_CC_set=${CC+set} ac_cv_env_CC_value=$CC ac_env_CFLAGS_set=${CFLAGS+set} ac_env_CFLAGS_value=$CFLAGS ac_cv_env_CFLAGS_set=${CFLAGS+set} ac_cv_env_CFLAGS_value=$CFLAGS ac_env_LDFLAGS_set=${LDFLAGS+set} ac_env_LDFLAGS_value=$LDFLAGS ac_cv_env_LDFLAGS_set=${LDFLAGS+set} ac_cv_env_LDFLAGS_value=$LDFLAGS ac_env_CPPFLAGS_set=${CPPFLAGS+set} ac_env_CPPFLAGS_value=$CPPFLAGS ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} ac_cv_env_CPPFLAGS_value=$CPPFLAGS ac_env_CPP_set=${CPP+set} ac_env_CPP_value=$CPP ac_cv_env_CPP_set=${CPP+set} ac_cv_env_CPP_value=$CPP # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures this package to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] _ACEOF cat <<_ACEOF Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --datadir=DIR read-only architecture-independent data [PREFIX/share] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --infodir=DIR info documentation [PREFIX/info] --mandir=DIR man documentation [PREFIX/man] _ACEOF cat <<\_ACEOF Program names: --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] --target=TARGET configure for building compilers for TARGET [HOST] _ACEOF fi if test -n "$ac_init_help"; then cat <<\_ACEOF Optional Features: --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-generated-files-in-srcdir put copies of generated files in source dir intended for creating source tarballs for users without texinfo bison or flex. --enable-werror enable -Werror in bootstrap stage2 and later --enable-checking=LIST enable expensive run-time checks. With LIST, enable only specific categories of checks. Categories are: misc,tree,rtl,rtlflag,gc,gcac,fold; default is no checking --enable-coverage=LEVEL enable compiler\'s code coverage collection. Use to measure compiler performance and locate unused parts of the compiler. With LEVEL, specify optimization. Values are opt, noopt, default is noopt --enable-gather-detailed-mem-stats enable detailed memory allocation stats gathering --enable-multilib enable library support for multiple ABIs --enable-__cxa_atexit enable __cxa_atexit for C++ --enable-threads enable thread usage for target GCC --enable-threads=LIB use LIB thread package for target GCC --enable-objc-gc enable the use of Boehm's garbage collector with the GNU Objective-C runtime --disable-shared don't provide a shared libgcc --enable-intermodule build the compiler in one step --enable-initfini-array use .init_array/.fini_array sections --enable-sjlj-exceptions arrange to use setjmp/longjmp exception handling --disable-win32-registry disable lookup of installation paths in the Registry on Windows hosts --enable-win32-registry enable registry lookup (default) --enable-win32-registry=KEY use KEY instead of GCC version as the last portion of the registry key --enable-maintainer-mode enable make rules and dependencies not useful (and sometimes confusing) to the casual installer --enable-version-specific-runtime-libs specify that runtime libraries should be installed in a compiler-specific directory Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-local-prefix=DIR specifies directory to put local include --with-gxx-include-dir=DIR specifies directory to put g++ header files --with-cpp-install-dir=DIR install the user visible C preprocessor in DIR (relative to PREFIX) as well as PREFIX/bin --with-gnu-ld arrange to work with GNU ld. --with-ld arrange to use the specified ld (full pathname) --with-gnu-as arrange to work with GNU as --with-as arrange to use the specified as (full pathname) --with-stabs arrange to use stabs instead of host debug format --with-dwarf2 force the default debug format to be DWARF 2 --with-sysroot=DIR Search for usr/lib, usr/include, et al, within DIR. --with-libiconv-prefix=DIR search for libiconv in DIR/include and DIR/lib --with-system-libunwind use installed libunwind --with-gc={simple,page,zone} choose the garbage collection mechanism to use with the compiler --with-system-zlib use installed libz --with-slibdir=DIR shared libraries in DIR LIBDIR Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. _ACEOF fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. ac_popdir=`pwd` for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d $ac_dir || continue ac_builddir=. if test "$ac_dir" != .; then ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A "../" for each directory in $ac_dir_suffix. ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` else ac_dir_suffix= ac_top_builddir= fi case $srcdir in .) # No --srcdir option. We are building in place. ac_srcdir=. if test -z "$ac_top_builddir"; then ac_top_srcdir=. else ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` fi ;; [\\/]* | ?:[\\/]* ) # Absolute path. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ;; *) # Relative path. ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_builddir$srcdir ;; esac # Do not use `cd foo && pwd` to compute absolute paths, because # the directories may not exist. case `pwd` in .) ac_abs_builddir="$ac_dir";; *) case "$ac_dir" in .) ac_abs_builddir=`pwd`;; [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; *) ac_abs_builddir=`pwd`/"$ac_dir";; esac;; esac case $ac_abs_builddir in .) ac_abs_top_builddir=${ac_top_builddir}.;; *) case ${ac_top_builddir}. in .) ac_abs_top_builddir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; esac;; esac case $ac_abs_builddir in .) ac_abs_srcdir=$ac_srcdir;; *) case $ac_srcdir in .) ac_abs_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; esac;; esac case $ac_abs_builddir in .) ac_abs_top_srcdir=$ac_top_srcdir;; *) case $ac_top_srcdir in .) ac_abs_top_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; esac;; esac cd $ac_dir # Check for guested configure; otherwise get Cygnus style configure. if test -f $ac_srcdir/configure.gnu; then echo $SHELL $ac_srcdir/configure.gnu --help=recursive elif test -f $ac_srcdir/configure; then echo $SHELL $ac_srcdir/configure --help=recursive elif test -f $ac_srcdir/configure.ac || test -f $ac_srcdir/configure.in; then echo $ac_configure --help else echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi cd $ac_popdir done fi test -n "$ac_init_help" && exit 0 if $ac_init_version; then cat <<\_ACEOF Copyright (C) 2003 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit 0 fi exec 5>config.log cat >&5 <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by $as_me, which was generated by GNU Autoconf 2.59. Invocation command line was $ $0 $@ _ACEOF { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` hostinfo = `(hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. echo "PATH: $as_dir" done } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_sep= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; 2) ac_configure_args1="$ac_configure_args1 '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" # Get rid of the leading space. ac_sep=" " ;; esac done done $as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } $as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Be sure not to use single quotes in there, as some shells, # such as our DU 5.0 friend, will then `close' the trap. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo cat <<\_ASBOX ## ---------------- ## ## Cache variables. ## ## ---------------- ## _ASBOX echo # The following way of writing the cache mishandles newlines in values, { (set) 2>&1 | case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in *ac_space=\ *) sed -n \ "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" ;; *) sed -n \ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" ;; esac; } echo cat <<\_ASBOX ## ----------------- ## ## Output variables. ## ## ----------------- ## _ASBOX echo for ac_var in $ac_subst_vars do eval ac_val=$`echo $ac_var` echo "$ac_var='"'"'$ac_val'"'"'" done | sort echo if test -n "$ac_subst_files"; then cat <<\_ASBOX ## ------------- ## ## Output files. ## ## ------------- ## _ASBOX echo for ac_var in $ac_subst_files do eval ac_val=$`echo $ac_var` echo "$ac_var='"'"'$ac_val'"'"'" done | sort echo fi if test -s confdefs.h; then cat <<\_ASBOX ## ----------- ## ## confdefs.h. ## ## ----------- ## _ASBOX echo sed "/^$/d" confdefs.h | sort echo fi test "$ac_signal" != 0 && echo "$as_me: caught signal $ac_signal" echo "$as_me: exit $exit_status" } >&5 rm -f core *.core && rm -rf conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -rf conftest* confdefs.h # AIX cpp loses on an empty file, so make sure it contains at least a newline. echo >confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer explicitly selected file to automatically selected ones. if test -z "$CONFIG_SITE"; then if test "x$prefix" != xNONE; then CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" else CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi fi for ac_site_file in $CONFIG_SITE; do if test -r "$ac_site_file"; then { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special # files actually), so we avoid doing that. if test -f "$cache_file"; then { echo "$as_me:$LINENO: loading cache $cache_file" >&5 echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . $cache_file;; *) . ./$cache_file;; esac fi else { echo "$as_me:$LINENO: creating cache $cache_file" >&5 echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in `(set) 2>&1 | sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val="\$ac_cv_env_${ac_var}_value" eval ac_new_val="\$ac_env_${ac_var}_value" case $ac_old_set,$ac_new_set in set,) { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 echo "$as_me: former value: $ac_old_val" >&2;} { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 echo "$as_me: current value: $ac_new_val" >&2;} ac_cache_corrupted=: fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 echo "$as_me: error: changes in the environment can compromise the build" >&2;} { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} { (exit 1); exit 1; }; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers auto-host.h:config.in" # Determine the host, build, and target systems ac_aux_dir= for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do if test -f $ac_dir/install-sh; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f $ac_dir/install.sh; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f $ac_dir/shtool; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} { (exit 1); exit 1; }; } fi ac_config_guess="$SHELL $ac_aux_dir/config.guess" ac_config_sub="$SHELL $ac_aux_dir/config.sub" ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. # Make sure we can run config.sub. $ac_config_sub sun4 >/dev/null 2>&1 || { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5 echo "$as_me: error: cannot run $ac_config_sub" >&2;} { (exit 1); exit 1; }; } echo "$as_me:$LINENO: checking build system type" >&5 echo $ECHO_N "checking build system type... $ECHO_C" >&6 if test "${ac_cv_build+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_build_alias=$build_alias test -z "$ac_cv_build_alias" && ac_cv_build_alias=`$ac_config_guess` test -z "$ac_cv_build_alias" && { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5 echo "$as_me: error: cannot guess build type; you must specify one" >&2;} { (exit 1); exit 1; }; } ac_cv_build=`$ac_config_sub $ac_cv_build_alias` || { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5 echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;} { (exit 1); exit 1; }; } fi echo "$as_me:$LINENO: result: $ac_cv_build" >&5 echo "${ECHO_T}$ac_cv_build" >&6 build=$ac_cv_build build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` echo "$as_me:$LINENO: checking host system type" >&5 echo $ECHO_N "checking host system type... $ECHO_C" >&6 if test "${ac_cv_host+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_host_alias=$host_alias test -z "$ac_cv_host_alias" && ac_cv_host_alias=$ac_cv_build_alias ac_cv_host=`$ac_config_sub $ac_cv_host_alias` || { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5 echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;} { (exit 1); exit 1; }; } fi echo "$as_me:$LINENO: result: $ac_cv_host" >&5 echo "${ECHO_T}$ac_cv_host" >&6 host=$ac_cv_host host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` echo "$as_me:$LINENO: checking target system type" >&5 echo $ECHO_N "checking target system type... $ECHO_C" >&6 if test "${ac_cv_target+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_target_alias=$target_alias test "x$ac_cv_target_alias" = "x" && ac_cv_target_alias=$ac_cv_host_alias ac_cv_target=`$ac_config_sub $ac_cv_target_alias` || { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_target_alias failed" >&5 echo "$as_me: error: $ac_config_sub $ac_cv_target_alias failed" >&2;} { (exit 1); exit 1; }; } fi echo "$as_me:$LINENO: result: $ac_cv_target" >&5 echo "${ECHO_T}$ac_cv_target" >&6 target=$ac_cv_target target_cpu=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` target_vendor=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` target_os=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` # The aliases save the names the user supplied, while $host etc. # will get canonicalized. test -n "$target_alias" && test "$program_prefix$program_suffix$program_transform_name" = \ NONENONEs,x,x, && program_prefix=${target_alias}- # Determine the noncanonical target name, for directory use. case ${build_alias} in "") build_noncanonical=${build} ;; *) build_noncanonical=${build_alias} ;; esac case ${host_alias} in "") host_noncanonical=${build_noncanonical} ;; *) host_noncanonical=${host_alias} ;; esac case ${target_alias} in "") target_noncanonical=${host_noncanonical} ;; *) target_noncanonical=${target_alias} ;; esac # Determine the target- and build-specific subdirectories # Prefix 'build-' so this never conflicts with target_subdir. build_subdir="build-${build_noncanonical}" # Not really a subdirectory, but here for completeness. host_subdir=. # No prefix. target_subdir=${target_noncanonical} # Set program_transform_name test "$program_prefix" != NONE && program_transform_name="s,^,$program_prefix,;$program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s,\$,$program_suffix,;$program_transform_name" # Double any \ or $. echo might interpret backslashes. # By default was `s,x,x', remove it if useless. cat <<\_ACEOF >conftest.sed s/[\\$]/&&/g;s/;s,x,x,$// _ACEOF program_transform_name=`echo $program_transform_name | sed -f conftest.sed` rm conftest.sed # Check for bogus environment variables. # Test if LIBRARY_PATH contains the notation for the current directory # since this would lead to problems installing/building glibc. # LIBRARY_PATH contains the current directory if one of the following # is true: # - one of the terminals (":" and ";") is the first or last sign # - two terminals occur directly after each other # - the path contains an element with a dot in it echo "$as_me:$LINENO: checking LIBRARY_PATH variable" >&5 echo $ECHO_N "checking LIBRARY_PATH variable... $ECHO_C" >&6 case ${LIBRARY_PATH} in [:\;]* | *[:\;] | *[:\;][:\;]* | *[:\;]. | .[:\;]*| . | *[:\;].[:\;]* ) library_path_setting="contains current directory" ;; *) library_path_setting="ok" ;; esac echo "$as_me:$LINENO: result: $library_path_setting" >&5 echo "${ECHO_T}$library_path_setting" >&6 if test "$library_path_setting" != "ok"; then { { echo "$as_me:$LINENO: error: *** LIBRARY_PATH shouldn't contain the current directory when *** building gcc. Please change the environment variable *** and run configure again." >&5 echo "$as_me: error: *** LIBRARY_PATH shouldn't contain the current directory when *** building gcc. Please change the environment variable *** and run configure again." >&2;} { (exit 1); exit 1; }; } fi # Test if GCC_EXEC_PREFIX contains the notation for the current directory # since this would lead to problems installing/building glibc. # GCC_EXEC_PREFIX contains the current directory if one of the following # is true: # - one of the terminals (":" and ";") is the first or last sign # - two terminals occur directly after each other # - the path contains an element with a dot in it echo "$as_me:$LINENO: checking GCC_EXEC_PREFIX variable" >&5 echo $ECHO_N "checking GCC_EXEC_PREFIX variable... $ECHO_C" >&6 case ${GCC_EXEC_PREFIX} in [:\;]* | *[:\;] | *[:\;][:\;]* | *[:\;]. | .[:\;]*| . | *[:\;].[:\;]* ) gcc_exec_prefix_setting="contains current directory" ;; *) gcc_exec_prefix_setting="ok" ;; esac echo "$as_me:$LINENO: result: $gcc_exec_prefix_setting" >&5 echo "${ECHO_T}$gcc_exec_prefix_setting" >&6 if test "$gcc_exec_prefix_setting" != "ok"; then { { echo "$as_me:$LINENO: error: *** GCC_EXEC_PREFIX shouldn't contain the current directory when *** building gcc. Please change the environment variable *** and run configure again." >&5 echo "$as_me: error: *** GCC_EXEC_PREFIX shouldn't contain the current directory when *** building gcc. Please change the environment variable *** and run configure again." >&2;} { (exit 1); exit 1; }; } fi # ----------- # Directories # ----------- # Specify the local prefix local_prefix= # Check whether --with-local-prefix or --without-local-prefix was given. if test "${with_local_prefix+set}" = set; then withval="$with_local_prefix" case "${withval}" in yes) { { echo "$as_me:$LINENO: error: bad value ${withval} given for local include directory prefix" >&5 echo "$as_me: error: bad value ${withval} given for local include directory prefix" >&2;} { (exit 1); exit 1; }; } ;; no) ;; *) local_prefix=$with_local_prefix ;; esac fi; # Default local prefix if it is empty if test x$local_prefix = x; then local_prefix=/usr/local fi # Don't set gcc_gxx_include_dir to gxx_include_dir since that's only # passed in by the toplevel make and thus we'd get different behavior # depending on where we built the sources. gcc_gxx_include_dir= # Specify the g++ header file directory # Check whether --with-gxx-include-dir or --without-gxx-include-dir was given. if test "${with_gxx_include_dir+set}" = set; then withval="$with_gxx_include_dir" case "${withval}" in yes) { { echo "$as_me:$LINENO: error: bad value ${withval} given for g++ include directory" >&5 echo "$as_me: error: bad value ${withval} given for g++ include directory" >&2;} { (exit 1); exit 1; }; } ;; no) ;; *) gcc_gxx_include_dir=$with_gxx_include_dir ;; esac fi; if test x${gcc_gxx_include_dir} = x; then if test x${enable_version_specific_runtime_libs} = xyes; then gcc_gxx_include_dir='${libsubdir}/include/c++' else topsrcdir=${srcdir}/.. . ${srcdir}/../config.if gcc_gxx_include_dir="\$(libsubdir)/\$(unlibsubdir)/..\`echo \$(exec_prefix) | sed -e 's|^\$(prefix)||' -e 's|/[^/]*|/..|g'\`/include/"${libstdcxx_incdir} fi fi # Check whether --with-cpp_install_dir or --without-cpp_install_dir was given. if test "${with_cpp_install_dir+set}" = set; then withval="$with_cpp_install_dir" if test x$withval = xyes; then { { echo "$as_me:$LINENO: error: option --with-cpp-install-dir requires an argument" >&5 echo "$as_me: error: option --with-cpp-install-dir requires an argument" >&2;} { (exit 1); exit 1; }; } elif test x$withval != xno; then cpp_install_dir=$withval fi fi; # We would like to our source tree to be readonly. However when releases or # pre-releases are generated, the flex/bison generated files as well as the # various formats of manuals need to be included along with the rest of the # sources. Therefore we have --enable-generated-files-in-srcdir to do # just that. echo "$as_me:$LINENO: checking whether to place generated files in the source directory" >&5 echo $ECHO_N "checking whether to place generated files in the source directory... $ECHO_C" >&6 # Check whether --enable-generated-files-in-srcdir or --disable-generated-files-in-srcdir was given. if test "${enable_generated_files_in_srcdir+set}" = set; then enableval="$enable_generated_files_in_srcdir" generated_files_in_srcdir=$enableval else generated_files_in_srcdir=no fi; echo "$as_me:$LINENO: result: $generated_files_in_srcdir" >&5 echo "${ECHO_T}$generated_files_in_srcdir" >&6 if test "$generated_files_in_srcdir" = "yes"; then GENINSRC='' else GENINSRC='#' fi # ------------------- # Find default linker # ------------------- # With GNU ld # Check whether --with-gnu-ld or --without-gnu-ld was given. if test "${with_gnu_ld+set}" = set; then withval="$with_gnu_ld" gnu_ld_flag="$with_gnu_ld" else gnu_ld_flag=no fi; # With pre-defined ld # Check whether --with-ld or --without-ld was given. if test "${with_ld+set}" = set; then withval="$with_ld" DEFAULT_LINKER="$with_ld" fi; if test x"${DEFAULT_LINKER+set}" = x"set"; then if test ! -x "$DEFAULT_LINKER"; then { echo "$as_me:$LINENO: WARNING: cannot execute: $DEFAULT_LINKER: check --with-ld or env. var. DEFAULT_LINKER" >&5 echo "$as_me: WARNING: cannot execute: $DEFAULT_LINKER: check --with-ld or env. var. DEFAULT_LINKER" >&2;} elif $DEFAULT_LINKER -v < /dev/null 2>&1 | grep GNU > /dev/null; then gnu_ld_flag=yes fi cat >>confdefs.h <<_ACEOF #define DEFAULT_LINKER "$DEFAULT_LINKER" _ACEOF fi echo "$as_me:$LINENO: checking whether a default linker was specified" >&5 echo $ECHO_N "checking whether a default linker was specified... $ECHO_C" >&6 if test x"${DEFAULT_LINKER+set}" = x"set"; then if test x"$gnu_ld_flag" = x"no"; then echo "$as_me:$LINENO: result: yes ($DEFAULT_LINKER)" >&5 echo "${ECHO_T}yes ($DEFAULT_LINKER)" >&6 else echo "$as_me:$LINENO: result: yes ($DEFAULT_LINKER - GNU ld)" >&5 echo "${ECHO_T}yes ($DEFAULT_LINKER - GNU ld)" >&6 fi else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi # ---------------------- # Find default assembler # ---------------------- # With GNU as # Check whether --with-gnu-as or --without-gnu-as was given. if test "${with_gnu_as+set}" = set; then withval="$with_gnu_as" gas_flag="$with_gnu_as" else gas_flag=no fi; # Check whether --with-as or --without-as was given. if test "${with_as+set}" = set; then withval="$with_as" DEFAULT_ASSEMBLER="$with_as" fi; if test x"${DEFAULT_ASSEMBLER+set}" = x"set"; then if test ! -x "$DEFAULT_ASSEMBLER"; then { echo "$as_me:$LINENO: WARNING: cannot execute: $DEFAULT_ASSEMBLER: check --with-as or env. var. DEFAULT_ASSEMBLER" >&5 echo "$as_me: WARNING: cannot execute: $DEFAULT_ASSEMBLER: check --with-as or env. var. DEFAULT_ASSEMBLER" >&2;} elif $DEFAULT_ASSEMBLER -v < /dev/null 2>&1 | grep GNU > /dev/null; then gas_flag=yes fi cat >>confdefs.h <<_ACEOF #define DEFAULT_ASSEMBLER "$DEFAULT_ASSEMBLER" _ACEOF fi echo "$as_me:$LINENO: checking whether a default assembler was specified" >&5 echo $ECHO_N "checking whether a default assembler was specified... $ECHO_C" >&6 if test x"${DEFAULT_ASSEMBLER+set}" = x"set"; then if test x"$gas_flag" = x"no"; then echo "$as_me:$LINENO: result: yes ($DEFAULT_ASSEMBLER)" >&5 echo "${ECHO_T}yes ($DEFAULT_ASSEMBLER)" >&6 else echo "$as_me:$LINENO: result: yes ($DEFAULT_ASSEMBLER - GNU as)" >&5 echo "${ECHO_T}yes ($DEFAULT_ASSEMBLER - GNU as)" >&6 fi else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi # --------------- # Find C compiler # --------------- # If a non-executable a.out is present (e.g. created by GNU as above even if # invoked with -v only), the IRIX 6 native ld just overwrites the existing # file, even when creating an executable, so an execution test fails. # Remove possible default executable files to avoid this. # # FIXME: This really belongs into AC_PROG_CC and can be removed once # Autoconf includes it. rm -f a.out a.exe b.out # Find the native compiler ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi CC=$ac_ct_CC else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi CC=$ac_ct_CC else CC="$ac_cv_prog_CC" fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$ac_ct_CC" && break done CC=$ac_ct_CC fi fi test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&5 echo "$as_me: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } # Provide some information about the compiler. echo "$as_me:$LINENO:" \ "checking for C compiler version" >&5 ac_compiler=`set X $ac_compile; echo $2` { (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 (eval $ac_compiler --version &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 (eval $ac_compiler -v &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 (eval $ac_compiler -V &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6 ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 (eval $ac_link_default) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # Find the output, starting from the most likely. This scheme is # not robust to junk in `.', hence go to wildcards (a.*) only as a last # resort. # Be careful to initialize this variable, since it used to be cached. # Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. ac_cv_exeext= # b.out is created by i960 compilers. for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; conftest.$ac_ext ) # This is the source file. ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` # FIXME: I believe we export ac_cv_exeext for Libtool, # but it would be cool to find out if it's true. Does anybody # maintain Libtool? --akim. export ac_cv_exeext break;; * ) break;; esac done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { echo "$as_me:$LINENO: error: C compiler cannot create executables See \`config.log' for more details." >&5 echo "$as_me: error: C compiler cannot create executables See \`config.log' for more details." >&2;} { (exit 77); exit 77; }; } fi ac_exeext=$ac_cv_exeext echo "$as_me:$LINENO: result: $ac_file" >&5 echo "${ECHO_T}$ac_file" >&6 # Check the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. echo "$as_me:$LINENO: checking whether the C compiler works" >&5 echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 # FIXME: These cross compiler hacks should be removed for Autoconf 3.0 # If not cross compiling, check that we can run a simple program. if test "$cross_compiling" != yes; then if { ac_try='./$ac_file' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { echo "$as_me:$LINENO: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&5 echo "$as_me: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi fi fi echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 rm -f a.out a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save # Check the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 echo "$as_me:$LINENO: result: $cross_compiling" >&5 echo "${ECHO_T}$cross_compiling" >&6 echo "$as_me:$LINENO: checking for suffix of executables" >&5 echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` export ac_cv_exeext break;; * ) break;; esac done else { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f conftest$ac_cv_exeext echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 echo "${ECHO_T}$ac_cv_exeext" >&6 rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT echo "$as_me:$LINENO: checking for suffix of object files" >&5 echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 if test "${ac_cv_objext+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 echo "${ECHO_T}$ac_cv_objext" >&6 OBJEXT=$ac_cv_objext ac_objext=$OBJEXT echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 if test "${ac_cv_c_compiler_gnu+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_compiler_gnu=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_compiler_gnu=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 GCC=`test $ac_compiler_gnu = yes && echo yes` ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS CFLAGS="-g" echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 if test "${ac_cv_prog_cc_g+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_prog_cc_g=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_prog_cc_g=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 if test "${ac_cv_prog_cc_stdc+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_prog_cc_stdc=no ac_save_CC=$CC cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std1 is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std1. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF # Don't try gcc -ansi; that turns off useful extensions and # breaks some systems' header files. # AIX -qlanglvl=ansi # Ultrix and OSF/1 -std1 # HP-UX 10.20 and later -Ae # HP-UX older versions -Aa -D_HPUX_SOURCE # SVR4 -Xc -D__EXTENSIONS__ for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_prog_cc_stdc=$ac_arg break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext done rm -f conftest.$ac_ext conftest.$ac_objext CC=$ac_save_CC fi case "x$ac_cv_prog_cc_stdc" in x|xno) echo "$as_me:$LINENO: result: none needed" >&5 echo "${ECHO_T}none needed" >&6 ;; *) echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 CC="$CC $ac_cv_prog_cc_stdc" ;; esac # Some people use a C++ compiler to compile C. Since we use `exit', # in C++ we need to declare it. In case someone uses the same compiler # for both compiling C and C++ we need to have the C++ compiler decide # the declaration of exit, since it's the most demanding environment. cat >conftest.$ac_ext <<_ACEOF #ifndef __cplusplus choke me #endif _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then for ac_declaration in \ '' \ 'extern "C" void std::exit (int) throw (); using std::exit;' \ 'extern "C" void std::exit (int); using std::exit;' \ 'extern "C" void exit (int) throw ();' \ 'extern "C" void exit (int);' \ 'void exit (int);' do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_declaration #include int main () { exit (42); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 continue fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_declaration int main () { exit (42); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done rm -f conftest* if test -n "$ac_declaration"; then echo '#ifdef __cplusplus' >>confdefs.h echo $ac_declaration >>confdefs.h echo '#endif' >>confdefs.h fi else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test "x$CC" != xcc; then echo "$as_me:$LINENO: checking whether $CC and cc understand -c and -o together" >&5 echo $ECHO_N "checking whether $CC and cc understand -c and -o together... $ECHO_C" >&6 else echo "$as_me:$LINENO: checking whether cc understands -c and -o together" >&5 echo $ECHO_N "checking whether cc understands -c and -o together... $ECHO_C" >&6 fi set dummy $CC; ac_cc=`echo $2 | sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'` if eval "test \"\${ac_cv_prog_cc_${ac_cc}_c_o+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # We do the test twice because some compilers refuse to overwrite an # existing .o file with -o, though they will create one. ac_try='$CC -c conftest.$ac_ext -o conftest.$ac_objext >&5' if { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && test -f conftest.$ac_objext && { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then eval ac_cv_prog_cc_${ac_cc}_c_o=yes if test "x$CC" != xcc; then # Test first that cc exists at all. if { ac_try='cc -c conftest.$ac_ext >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_try='cc -c conftest.$ac_ext -o conftest.$ac_objext >&5' if { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && test -f conftest.$ac_objext && { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # cc works too. : else # cc exists but doesn't like -o. eval ac_cv_prog_cc_${ac_cc}_c_o=no fi fi fi else eval ac_cv_prog_cc_${ac_cc}_c_o=no fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_prog_cc_'${ac_cc}_c_o`\" = yes"; then echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 cat >>confdefs.h <<\_ACEOF #define NO_MINUS_C_MINUS_O 1 _ACEOF fi # autoconf is lame and doesn't give us any substitution variable for this. if eval "test \"`echo '$ac_cv_prog_cc_'${ac_cc}_c_o`\" = no"; then NO_MINUS_C_MINUS_O=yes else OUTPUT_OPTION='-o $@' fi # ------------------------- # Check C compiler features # ------------------------- echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wno-long-long" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wno-long-long... $ECHO_C" >&6 if test "${ac_cv_prog_cc_no_long_long+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else save_CFLAGS="$CFLAGS" CFLAGS="-Wno-long-long" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_prog_cc_no_long_long=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_prog_cc_no_long_long=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$save_CFLAGS" fi echo "$as_me:$LINENO: result: $ac_cv_prog_cc_no_long_long" >&5 echo "${ECHO_T}$ac_cv_prog_cc_no_long_long" >&6 ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if test "${ac_cv_prog_CPP+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Broken: fails on valid input. continue fi rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether non-existent headers # can be detected and how. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then # Broken: success on invalid input. continue else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi echo "$as_me:$LINENO: result: $CPP" >&5 echo "${ECHO_T}$CPP" >&6 ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Broken: fails on valid input. continue fi rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether non-existent headers # can be detected and how. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then # Broken: success on invalid input. continue else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details." >&5 echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu echo "$as_me:$LINENO: checking for inline" >&5 echo $ECHO_N "checking for inline... $ECHO_C" >&6 if test "${ac_cv_c_inline+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_c_inline=no for ac_kw in inline __inline__ __inline; do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifndef __cplusplus typedef int foo_t; static $ac_kw foo_t static_foo () {return 0; } $ac_kw foo_t foo () {return 0; } #endif _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_c_inline=$ac_kw; break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done fi echo "$as_me:$LINENO: result: $ac_cv_c_inline" >&5 echo "${ECHO_T}$ac_cv_c_inline" >&6 case $ac_cv_c_inline in inline | yes) ;; *) case $ac_cv_c_inline in no) ac_val=;; *) ac_val=$ac_cv_c_inline;; esac cat >>confdefs.h <<_ACEOF #ifndef __cplusplus #define inline $ac_val #endif _ACEOF ;; esac echo "$as_me:$LINENO: checking for long long int" >&5 echo $ECHO_N "checking for long long int... $ECHO_C" >&6 if test "${ac_cv_c_long_long+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { long long int i; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_c_long_long=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_c_long_long=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_c_long_long" >&5 echo "${ECHO_T}$ac_cv_c_long_long" >&6 if test $ac_cv_c_long_long = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_LONG_LONG 1 _ACEOF fi echo "$as_me:$LINENO: checking for __int64" >&5 echo $ECHO_N "checking for __int64... $ECHO_C" >&6 if test "${ac_cv_c___int64+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { __int64 i; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_c___int64=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_c___int64=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_c___int64" >&5 echo "${ECHO_T}$ac_cv_c___int64" >&6 if test $ac_cv_c___int64 = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE___INT64 1 _ACEOF fi echo "$as_me:$LINENO: checking for built-in _Bool" >&5 echo $ECHO_N "checking for built-in _Bool... $ECHO_C" >&6 if test "${gcc_cv_c__bool+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { _Bool foo; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then gcc_cv_c__bool=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 gcc_cv_c__bool=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $gcc_cv_c__bool" >&5 echo "${ECHO_T}$gcc_cv_c__bool" >&6 if test $gcc_cv_c__bool = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE__BOOL 1 _ACEOF fi # sizeof(char) is 1 by definition. echo "$as_me:$LINENO: checking size of void *" >&5 echo $ECHO_N "checking size of void *... $ECHO_C" >&6 if test "${ac_cv_sizeof_void_p+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else for ac_size in 4 8 1 2 16 12 ; do # List sizes in rough order of prevalence. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include "confdefs.h" #include int main () { switch (0) case 0: case (sizeof (void *) == $ac_size):; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_sizeof_void_p=$ac_size else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext if test x$ac_cv_sizeof_void_p != x ; then break; fi done fi if test x$ac_cv_sizeof_void_p = x ; then { { echo "$as_me:$LINENO: error: cannot determine a size for void *" >&5 echo "$as_me: error: cannot determine a size for void *" >&2;} { (exit 1); exit 1; }; } fi echo "$as_me:$LINENO: result: $ac_cv_sizeof_void_p" >&5 echo "${ECHO_T}$ac_cv_sizeof_void_p" >&6 cat >>confdefs.h <<_ACEOF #define SIZEOF_VOID_P $ac_cv_sizeof_void_p _ACEOF echo "$as_me:$LINENO: checking size of short" >&5 echo $ECHO_N "checking size of short... $ECHO_C" >&6 if test "${ac_cv_sizeof_short+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else for ac_size in 4 8 1 2 16 12 ; do # List sizes in rough order of prevalence. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include "confdefs.h" #include int main () { switch (0) case 0: case (sizeof (short) == $ac_size):; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_sizeof_short=$ac_size else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext if test x$ac_cv_sizeof_short != x ; then break; fi done fi if test x$ac_cv_sizeof_short = x ; then { { echo "$as_me:$LINENO: error: cannot determine a size for short" >&5 echo "$as_me: error: cannot determine a size for short" >&2;} { (exit 1); exit 1; }; } fi echo "$as_me:$LINENO: result: $ac_cv_sizeof_short" >&5 echo "${ECHO_T}$ac_cv_sizeof_short" >&6 cat >>confdefs.h <<_ACEOF #define SIZEOF_SHORT $ac_cv_sizeof_short _ACEOF echo "$as_me:$LINENO: checking size of int" >&5 echo $ECHO_N "checking size of int... $ECHO_C" >&6 if test "${ac_cv_sizeof_int+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else for ac_size in 4 8 1 2 16 12 ; do # List sizes in rough order of prevalence. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include "confdefs.h" #include int main () { switch (0) case 0: case (sizeof (int) == $ac_size):; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_sizeof_int=$ac_size else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext if test x$ac_cv_sizeof_int != x ; then break; fi done fi if test x$ac_cv_sizeof_int = x ; then { { echo "$as_me:$LINENO: error: cannot determine a size for int" >&5 echo "$as_me: error: cannot determine a size for int" >&2;} { (exit 1); exit 1; }; } fi echo "$as_me:$LINENO: result: $ac_cv_sizeof_int" >&5 echo "${ECHO_T}$ac_cv_sizeof_int" >&6 cat >>confdefs.h <<_ACEOF #define SIZEOF_INT $ac_cv_sizeof_int _ACEOF echo "$as_me:$LINENO: checking size of long" >&5 echo $ECHO_N "checking size of long... $ECHO_C" >&6 if test "${ac_cv_sizeof_long+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else for ac_size in 4 8 1 2 16 12 ; do # List sizes in rough order of prevalence. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include "confdefs.h" #include int main () { switch (0) case 0: case (sizeof (long) == $ac_size):; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_sizeof_long=$ac_size else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext if test x$ac_cv_sizeof_long != x ; then break; fi done fi if test x$ac_cv_sizeof_long = x ; then { { echo "$as_me:$LINENO: error: cannot determine a size for long" >&5 echo "$as_me: error: cannot determine a size for long" >&2;} { (exit 1); exit 1; }; } fi echo "$as_me:$LINENO: result: $ac_cv_sizeof_long" >&5 echo "${ECHO_T}$ac_cv_sizeof_long" >&6 cat >>confdefs.h <<_ACEOF #define SIZEOF_LONG $ac_cv_sizeof_long _ACEOF if test $ac_cv_c_long_long = yes; then echo "$as_me:$LINENO: checking size of long long" >&5 echo $ECHO_N "checking size of long long... $ECHO_C" >&6 if test "${ac_cv_sizeof_long_long+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else for ac_size in 4 8 1 2 16 12 ; do # List sizes in rough order of prevalence. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include "confdefs.h" #include int main () { switch (0) case 0: case (sizeof (long long) == $ac_size):; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_sizeof_long_long=$ac_size else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext if test x$ac_cv_sizeof_long_long != x ; then break; fi done fi if test x$ac_cv_sizeof_long_long = x ; then { { echo "$as_me:$LINENO: error: cannot determine a size for long long" >&5 echo "$as_me: error: cannot determine a size for long long" >&2;} { (exit 1); exit 1; }; } fi echo "$as_me:$LINENO: result: $ac_cv_sizeof_long_long" >&5 echo "${ECHO_T}$ac_cv_sizeof_long_long" >&6 cat >>confdefs.h <<_ACEOF #define SIZEOF_LONG_LONG $ac_cv_sizeof_long_long _ACEOF fi if test $ac_cv_c___int64 = yes; then echo "$as_me:$LINENO: checking size of __int64" >&5 echo $ECHO_N "checking size of __int64... $ECHO_C" >&6 if test "${ac_cv_sizeof___int64+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else for ac_size in 4 8 1 2 16 12 ; do # List sizes in rough order of prevalence. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include "confdefs.h" #include int main () { switch (0) case 0: case (sizeof (__int64) == $ac_size):; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_sizeof___int64=$ac_size else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext if test x$ac_cv_sizeof___int64 != x ; then break; fi done fi if test x$ac_cv_sizeof___int64 = x ; then { { echo "$as_me:$LINENO: error: cannot determine a size for __int64" >&5 echo "$as_me: error: cannot determine a size for __int64" >&2;} { (exit 1); exit 1; }; } fi echo "$as_me:$LINENO: result: $ac_cv_sizeof___int64" >&5 echo "${ECHO_T}$ac_cv_sizeof___int64" >&6 cat >>confdefs.h <<_ACEOF #define SIZEOF___INT64 $ac_cv_sizeof___int64 _ACEOF fi # ----------------- # Find Ada compiler # ----------------- # See if GNAT has been installed if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gnatbind", so it can be a program name with args. set dummy ${ac_tool_prefix}gnatbind; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_GNATBIND+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$GNATBIND"; then ac_cv_prog_GNATBIND="$GNATBIND" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_GNATBIND="${ac_tool_prefix}gnatbind" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi GNATBIND=$ac_cv_prog_GNATBIND if test -n "$GNATBIND"; then echo "$as_me:$LINENO: result: $GNATBIND" >&5 echo "${ECHO_T}$GNATBIND" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$ac_cv_prog_GNATBIND"; then ac_ct_GNATBIND=$GNATBIND # Extract the first word of "gnatbind", so it can be a program name with args. set dummy gnatbind; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_GNATBIND+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_GNATBIND"; then ac_cv_prog_ac_ct_GNATBIND="$ac_ct_GNATBIND" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_GNATBIND="gnatbind" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done test -z "$ac_cv_prog_ac_ct_GNATBIND" && ac_cv_prog_ac_ct_GNATBIND="no" fi fi ac_ct_GNATBIND=$ac_cv_prog_ac_ct_GNATBIND if test -n "$ac_ct_GNATBIND"; then echo "$as_me:$LINENO: result: $ac_ct_GNATBIND" >&5 echo "${ECHO_T}$ac_ct_GNATBIND" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi GNATBIND=$ac_ct_GNATBIND else GNATBIND="$ac_cv_prog_GNATBIND" fi echo "$as_me:$LINENO: checking whether compiler driver understands Ada" >&5 echo $ECHO_N "checking whether compiler driver understands Ada... $ECHO_C" >&6 if test "${gcc_cv_cc_supports_ada+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.adb <&1 || echo failure` if test x"$errors" = x && test -f conftest.$ac_objext; then gcc_cv_cc_supports_ada=yes break fi rm -f conftest.* fi echo "$as_me:$LINENO: result: $gcc_cv_cc_supports_ada" >&5 echo "${ECHO_T}$gcc_cv_cc_supports_ada" >&6 if test x$GNATBIND != xno && test x$gcc_cv_cc_supports_ada != xno; then have_gnat=yes else have_gnat=no fi # --------------------- # Warnings and checking # --------------------- strict1_warn= if test $ac_cv_prog_cc_no_long_long = yes ; then strict1_warn="-pedantic -Wno-long-long" fi # If the native compiler is GCC, we can enable warnings even in stage1. # That's useful for people building cross-compilers, or just running a # quick `make'. warn_cflags= if test "x$GCC" = "xyes"; then warn_cflags='$(GCC_WARN_CFLAGS)' fi # Enable -Werror in bootstrap stage2 and later. # Change the default to "no" on release branches. # Check whether --enable-werror or --disable-werror was given. if test "${enable_werror+set}" = set; then enableval="$enable_werror" else enable_werror=no fi; if test x$enable_werror = xyes ; then WERROR=-Werror fi # Enable expensive internal checks # Check whether --enable-checking or --disable-checking was given. if test "${enable_checking+set}" = set; then enableval="$enable_checking" ac_checking= ac_tree_checking= ac_rtl_checking= ac_rtlflag_checking= ac_gc_checking= ac_gc_always_collect= ac_fold_checking= case "${enableval}" in yes) ac_checking=1 ; ac_tree_checking=1 ; ac_gc_checking=1 ; ac_rtlflag_checking=1 ;; no) ;; *) IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS="$IFS," set fnord $enableval; shift IFS="$ac_save_IFS" for check do case $check in misc) ac_checking=1 ;; tree) ac_tree_checking=1 ;; rtlflag) ac_rtlflag_checking=1 ;; rtl) ac_rtl_checking=1 ;; gc) ac_gc_checking=1 ;; gcac) ac_gc_always_collect=1 ;; fold) ac_fold_checking=1 ;; valgrind) ac_checking_valgrind=1 ;; *) { { echo "$as_me:$LINENO: error: unknown check category $check" >&5 echo "$as_me: error: unknown check category $check" >&2;} { (exit 1); exit 1; }; } ;; esac done ;; esac else # By default, disable all checks for release versions of GCC. ac_checking=; ac_tree_checking=; ac_gc_checking=; ac_rtlflag_checking=; fi; nocommon_flag="" if test x$ac_checking != x ; then cat >>confdefs.h <<\_ACEOF #define ENABLE_CHECKING 1 _ACEOF nocommon_flag=-fno-common fi if test x$ac_tree_checking != x ; then cat >>confdefs.h <<\_ACEOF #define ENABLE_TREE_CHECKING 1 _ACEOF fi if test x$ac_rtl_checking != x ; then cat >>confdefs.h <<\_ACEOF #define ENABLE_RTL_CHECKING 1 _ACEOF fi if test x$ac_rtlflag_checking != x ; then cat >>confdefs.h <<\_ACEOF #define ENABLE_RTL_FLAG_CHECKING 1 _ACEOF fi if test x$ac_gc_checking != x ; then cat >>confdefs.h <<\_ACEOF #define ENABLE_GC_CHECKING 1 _ACEOF fi if test x$ac_gc_always_collect != x ; then cat >>confdefs.h <<\_ACEOF #define ENABLE_GC_ALWAYS_COLLECT 1 _ACEOF fi if test x$ac_fold_checking != x ; then cat >>confdefs.h <<\_ACEOF #define ENABLE_FOLD_CHECKING 1 _ACEOF fi valgrind_path_defines= valgrind_command= echo "$as_me:$LINENO: checking for egrep" >&5 echo $ECHO_N "checking for egrep... $ECHO_C" >&6 if test "${ac_cv_prog_egrep+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if echo a | (grep -E '(a|b)') >/dev/null 2>&1 then ac_cv_prog_egrep='grep -E' else ac_cv_prog_egrep='egrep' fi fi echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 echo "${ECHO_T}$ac_cv_prog_egrep" >&6 EGREP=$ac_cv_prog_egrep echo "$as_me:$LINENO: checking for ANSI C header files" >&5 echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 if test "${ac_cv_header_stdc+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_header_stdc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_stdc=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); exit (0); } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_header_stdc=no fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi fi echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 echo "${ECHO_T}$ac_cv_header_stdc" >&6 if test $ac_cv_header_stdc = yes; then cat >>confdefs.h <<\_ACEOF #define STDC_HEADERS 1 _ACEOF fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "$as_ac_Header=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_Header=no" fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done if test "${ac_cv_header_valgrind_h+set}" = set; then echo "$as_me:$LINENO: checking for valgrind.h" >&5 echo $ECHO_N "checking for valgrind.h... $ECHO_C" >&6 if test "${ac_cv_header_valgrind_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: $ac_cv_header_valgrind_h" >&5 echo "${ECHO_T}$ac_cv_header_valgrind_h" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking valgrind.h usability" >&5 echo $ECHO_N "checking valgrind.h usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking valgrind.h presence" >&5 echo $ECHO_N "checking valgrind.h presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: valgrind.h: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: valgrind.h: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: valgrind.h: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: valgrind.h: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: valgrind.h: present but cannot be compiled" >&5 echo "$as_me: WARNING: valgrind.h: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: valgrind.h: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: valgrind.h: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: valgrind.h: see the Autoconf documentation" >&5 echo "$as_me: WARNING: valgrind.h: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: valgrind.h: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: valgrind.h: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: valgrind.h: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: valgrind.h: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: valgrind.h: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: valgrind.h: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## ------------------------------------------ ## ## Report this to the AC_PACKAGE_NAME lists. ## ## ------------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for valgrind.h" >&5 echo $ECHO_N "checking for valgrind.h... $ECHO_C" >&6 if test "${ac_cv_header_valgrind_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_header_valgrind_h=$ac_header_preproc fi echo "$as_me:$LINENO: result: $ac_cv_header_valgrind_h" >&5 echo "${ECHO_T}$ac_cv_header_valgrind_h" >&6 fi if test $ac_cv_header_valgrind_h = yes; then have_valgrind_h=yes else have_valgrind_h=no fi if test x$ac_checking_valgrind != x ; then # It is certainly possible that there's valgrind but no valgrind.h. # GCC relies on making annotations so we must have both. echo "$as_me:$LINENO: checking for VALGRIND_DISCARD in " >&5 echo $ECHO_N "checking for VALGRIND_DISCARD in ... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #ifndef VALGRIND_DISCARD #error VALGRIND_DISCARD not defined #endif _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then gcc_cv_header_valgrind_memcheck_h=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 gcc_cv_header_valgrind_memcheck_h=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $gcc_cv_header_valgrind_memcheck_h" >&5 echo "${ECHO_T}$gcc_cv_header_valgrind_memcheck_h" >&6 echo "$as_me:$LINENO: checking for VALGRIND_DISCARD in " >&5 echo $ECHO_N "checking for VALGRIND_DISCARD in ... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #ifndef VALGRIND_DISCARD #error VALGRIND_DISCARD not defined #endif _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then gcc_cv_header_memcheck_h=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 gcc_cv_header_memcheck_h=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $gcc_cv_header_memcheck_h" >&5 echo "${ECHO_T}$gcc_cv_header_memcheck_h" >&6 # Prepare PATH_SEPARATOR. # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi # Find out how to test for executable files. Don't use a zero-byte file, # as systems may use methods other than mode bits to determine executability. cat >conf$$.file <<_ASEOF #! /bin/sh exit 0 _ASEOF chmod +x conf$$.file if test -x conf$$.file >/dev/null 2>&1; then ac_executable_p="test -x" else ac_executable_p="test -f" fi rm -f conf$$.file # Extract the first word of "valgrind", so it can be a program name with args. set dummy valgrind; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_path_valgrind_path+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case "$valgrind_path" in [\\/]* | ?:[\\/]*) ac_cv_path_valgrind_path="$valgrind_path" # Let the user override the test with a path. ;; *) ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS="$ac_save_IFS" test -z "$ac_dir" && ac_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then if $ac_dir/$ac_word --version | grep valgrind- >/dev/null 2>&1; then ac_cv_path_valgrind_path="$ac_dir/$ac_word$ac_exec_ext" break 2 fi fi done done IFS="$ac_save_IFS" ;; esac fi valgrind_path="$ac_cv_path_valgrind_path" if test -n "$valgrind_path"; then echo "$as_me:$LINENO: result: $valgrind_path" >&5 echo "${ECHO_T}$valgrind_path" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi if test "x$valgrind_path" = "x" \ || (test $have_valgrind_h = no \ && test $gcc_cv_header_memcheck_h = no \ && test $gcc_cv_header_valgrind_memcheck_h = no); then { { echo "$as_me:$LINENO: error: *** Can't find both valgrind and valgrind/memcheck.h, memcheck.h or valgrind.h" >&5 echo "$as_me: error: *** Can't find both valgrind and valgrind/memcheck.h, memcheck.h or valgrind.h" >&2;} { (exit 1); exit 1; }; } fi valgrind_path_defines=-DVALGRIND_PATH='\"'$valgrind_path'\"' valgrind_command="$valgrind_path -q" cat >>confdefs.h <<\_ACEOF #define ENABLE_VALGRIND_CHECKING 1 _ACEOF if test $gcc_cv_header_valgrind_memcheck_h = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_VALGRIND_MEMCHECK_H 1 _ACEOF fi if test $gcc_cv_header_memcheck_h = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_MEMCHECK_H 1 _ACEOF fi fi # Enable code coverage collection # Check whether --enable-coverage or --disable-coverage was given. if test "${enable_coverage+set}" = set; then enableval="$enable_coverage" case "${enableval}" in yes|noopt) coverage_flags="-fprofile-arcs -ftest-coverage -frandom-seed=\$@ -O0" ;; opt) coverage_flags="-fprofile-arcs -ftest-coverage -frandom-seed=\$@ -O2" ;; *) { { echo "$as_me:$LINENO: error: unknown coverage setting $enableval" >&5 echo "$as_me: error: unknown coverage setting $enableval" >&2;} { (exit 1); exit 1; }; } ;; esac else coverage_flags="" fi; # Check whether --enable-gather-detailed-mem-stats or --disable-gather-detailed-mem-stats was given. if test "${enable_gather_detailed_mem_stats+set}" = set; then enableval="$enable_gather_detailed_mem_stats" else enable_gather_detailed_mem_stats=no fi; if test x$enable_gather_detailed_mem_stats = xyes ; then cat >>confdefs.h <<\_ACEOF #define GATHER_STATISTICS 1 _ACEOF fi # ------------------------------- # Miscenalleous configure options # ------------------------------- # With stabs # Check whether --with-stabs or --without-stabs was given. if test "${with_stabs+set}" = set; then withval="$with_stabs" stabs="$with_stabs" else stabs=no fi; # Determine whether or not multilibs are enabled. # Check whether --enable-multilib or --disable-multilib was given. if test "${enable_multilib+set}" = set; then enableval="$enable_multilib" else enable_multilib=yes fi; # Enable __cxa_atexit for C++. # Check whether --enable-__cxa_atexit or --disable-__cxa_atexit was given. if test "${enable___cxa_atexit+set}" = set; then enableval="$enable___cxa_atexit" fi; # Enable threads # Pass with no value to take the default # Pass with a value to specify a thread package # Check whether --enable-threads or --disable-threads was given. if test "${enable_threads+set}" = set; then enableval="$enable_threads" else enable_threads='' fi; # Save in case it gets overwritten in config.gcc enable_threads_flag=$enable_threads # Check whether --enable-objc-gc or --disable-objc-gc was given. if test "${enable_objc_gc+set}" = set; then enableval="$enable_objc_gc" if test x$enable_objc_gc = xno; then objc_boehm_gc='' else objc_boehm_gc=1 fi else objc_boehm_gc='' fi; # Check whether --with-dwarf2 or --without-dwarf2 was given. if test "${with_dwarf2+set}" = set; then withval="$with_dwarf2" dwarf2="$with_dwarf2" else dwarf2=no fi; # Check whether --enable-shared or --disable-shared was given. if test "${enable_shared+set}" = set; then enableval="$enable_shared" case $enable_shared in yes | no) ;; *) enable_shared=no IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:," for pkg in $enableval; do if test "X$pkg" = "Xgcc" || test "X$pkg" = "Xlibgcc"; then enable_shared=yes fi done IFS="$ac_save_ifs" ;; esac else enable_shared=yes fi; # Check whether --with-sysroot or --without-sysroot was given. if test "${with_sysroot+set}" = set; then withval="$with_sysroot" case ${with_sysroot} in yes) TARGET_SYSTEM_ROOT='${exec_prefix}/${target_noncanonical}/sys-root' ;; *) TARGET_SYSTEM_ROOT=$with_sysroot ;; esac TARGET_SYSTEM_ROOT_DEFINE='-DTARGET_SYSTEM_ROOT=\"$(TARGET_SYSTEM_ROOT)\"' CROSS_SYSTEM_HEADER_DIR='$(TARGET_SYSTEM_ROOT)$(NATIVE_SYSTEM_HEADER_DIR)' if test "x$exec_prefix" = xNONE; then if test "x$prefix" = xNONE; then test_prefix=/usr/local else test_prefix=$prefix fi else test_prefix=$exec_prefix fi case ${TARGET_SYSTEM_ROOT} in "${test_prefix}"|"${test_prefix}/"*|\ '${exec_prefix}'|'${exec_prefix}/'*) t="$TARGET_SYSTEM_ROOT_DEFINE -DTARGET_SYSTEM_ROOT_RELOCATABLE" TARGET_SYSTEM_ROOT_DEFINE="$t" ;; esac else TARGET_SYSTEM_ROOT= TARGET_SYSTEM_ROOT_DEFINE= CROSS_SYSTEM_HEADER_DIR='$(gcc_tooldir)/sys-include' fi; # Build with intermodule optimisations # Check whether --enable-intermodule or --disable-intermodule was given. if test "${enable_intermodule+set}" = set; then enableval="$enable_intermodule" case ${enable_intermodule} in yes) onestep="-onestep";; *) onestep="";; esac else onestep="" fi; # ------------------------- # Checks for other programs # ------------------------- echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6 set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'` if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.make <<\_ACEOF all: @echo 'ac_maketemp="$(MAKE)"' _ACEOF # GNU make sometimes prints "make[1]: Entering...", which would confuse us. eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` if test -n "$ac_maketemp"; then eval ac_cv_prog_make_${ac_make}_set=yes else eval ac_cv_prog_make_${ac_make}_set=no fi rm -f conftest.make fi if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 SET_MAKE= else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 SET_MAKE="MAKE=${MAKE-make}" fi # Find some useful tools for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_AWK+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then echo "$as_me:$LINENO: result: $AWK" >&5 echo "${ECHO_T}$AWK" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$AWK" && break done # We need awk to run opts.sh (to create options.c and options.h). # Bail out if it's missing. case ${AWK} in "") { { echo "$as_me:$LINENO: error: can't build without awk, bailing out" >&5 echo "$as_me: error: can't build without awk, bailing out" >&2;} { (exit 1); exit 1; }; } ;; esac echo "$as_me:$LINENO: checking whether ln works" >&5 echo $ECHO_N "checking whether ln works... $ECHO_C" >&6 if test "${gcc_cv_prog_LN+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else rm -f conftestdata_t echo >conftestdata_f if ln conftestdata_f conftestdata_t 2>/dev/null then gcc_cv_prog_LN="ln" else if ln -s conftestdata_f conftestdata_t 2>/dev/null then gcc_cv_prog_LN="ln -s" else gcc_cv_prog_LN=cp fi fi rm -f conftestdata_f conftestdata_t fi LN="$gcc_cv_prog_LN" if test "$gcc_cv_prog_LN" = "ln"; then echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 else if test "$gcc_cv_prog_LN" = "ln -s"; then echo "$as_me:$LINENO: result: no, using ln -s" >&5 echo "${ECHO_T}no, using ln -s" >&6 else echo "$as_me:$LINENO: result: no, and neither does ln -s, so using cp" >&5 echo "${ECHO_T}no, and neither does ln -s, so using cp" >&6 fi fi echo "$as_me:$LINENO: checking whether ln -s works" >&5 echo $ECHO_N "checking whether ln -s works... $ECHO_C" >&6 if test "${gcc_cv_prog_LN_S+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else rm -f conftestdata_t echo >conftestdata_f if ln -s conftestdata_f conftestdata_t 2>/dev/null then gcc_cv_prog_LN_S="ln -s" else if ln conftestdata_f conftestdata_t 2>/dev/null then gcc_cv_prog_LN_S=ln else gcc_cv_prog_LN_S=cp fi fi rm -f conftestdata_f conftestdata_t fi LN_S="$gcc_cv_prog_LN_S" if test "$gcc_cv_prog_LN_S" = "ln -s"; then echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 else if test "$gcc_cv_prog_LN_S" = "ln"; then echo "$as_me:$LINENO: result: no, using ln" >&5 echo "${ECHO_T}no, using ln" >&6 else echo "$as_me:$LINENO: result: no, and neither does ln, so using cp" >&5 echo "${ECHO_T}no, and neither does ln, so using cp" >&6 fi fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_RANLIB+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then echo "$as_me:$LINENO: result: $RANLIB" >&5 echo "${ECHO_T}$RANLIB" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":" fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 echo "${ECHO_T}$ac_ct_RANLIB" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi RANLIB=$ac_ct_RANLIB else RANLIB="$ac_cv_prog_RANLIB" fi # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # ./install, which can be erroneously created by make from ./install.sh. echo "$as_me:$LINENO: checking for a BSD compatible install" >&5 echo $ECHO_N "checking for a BSD compatible install... $ECHO_C" >&6 if test -z "$INSTALL"; then if test "${ac_cv_path_install+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS="${IFS}:" for ac_dir in $PATH; do # Account for people who put trailing slashes in PATH elements. case "$ac_dir/" in /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. for ac_prog in ginstall scoinst install; do if test -f $ac_dir/$ac_prog; then if test $ac_prog = install && grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. # OSF/1 installbsd also uses dspmsg, but is usable. : else ac_cv_path_install="$ac_dir/$ac_prog -c" break 2 fi fi done ;; esac done IFS="$ac_save_IFS" fi if test "${ac_cv_path_install+set}" = set; then INSTALL="$ac_cv_path_install" else # As a last resort, use the slow shell script. We don't cache a # path for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the path is relative. INSTALL="$ac_install_sh" fi fi echo "$as_me:$LINENO: result: $INSTALL" >&5 echo "${ECHO_T}$INSTALL" >&6 # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' # See if cmp has --ignore-initial. echo "$as_me:$LINENO: checking for cmp's capabilities" >&5 echo $ECHO_N "checking for cmp's capabilities... $ECHO_C" >&6 if test "${gcc_cv_prog_cmp_skip+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo abfoo >t1 echo cdfoo >t2 gcc_cv_prog_cmp_skip=slowcompare if cmp --ignore-initial=2 t1 t2 > /dev/null 2>&1; then if cmp --ignore-initial=1 t1 t2 > /dev/null 2>&1; then : else gcc_cv_prog_cmp_skip=gnucompare fi fi if test $gcc_cv_prog_cmp_skip = slowcompare ; then if cmp t1 t2 2 2 > /dev/null 2>&1; then if cmp t1 t2 1 1 > /dev/null 2>&1; then : else gcc_cv_prog_cmp_skip=fastcompare fi fi fi rm t1 t2 fi echo "$as_me:$LINENO: result: $gcc_cv_prog_cmp_skip" >&5 echo "${ECHO_T}$gcc_cv_prog_cmp_skip" >&6 make_compare_target=$gcc_cv_prog_cmp_skip # See if we have the mktemp command. # Extract the first word of "mktemp", so it can be a program name with args. set dummy mktemp; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_have_mktemp_command+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$have_mktemp_command"; then ac_cv_prog_have_mktemp_command="$have_mktemp_command" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_have_mktemp_command="yes" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done test -z "$ac_cv_prog_have_mktemp_command" && ac_cv_prog_have_mktemp_command="no" fi fi have_mktemp_command=$ac_cv_prog_have_mktemp_command if test -n "$have_mktemp_command"; then echo "$as_me:$LINENO: result: $have_mktemp_command" >&5 echo "${ECHO_T}$have_mktemp_command" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi # Do we have a single-tree copy of texinfo? if test -f $srcdir/../texinfo/Makefile.in; then MAKEINFO='$(objdir)/../texinfo/makeinfo/makeinfo' gcc_cv_prog_makeinfo_modern=yes echo "$as_me:$LINENO: result: Using makeinfo from the unified source tree." >&5 echo "${ECHO_T}Using makeinfo from the unified source tree." >&6 else # See if makeinfo has been installed and is modern enough # that we can use it. # Extract the first word of "makeinfo", so it can be a program name with args. set dummy makeinfo; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_MAKEINFO+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$MAKEINFO"; then ac_cv_prog_MAKEINFO="$MAKEINFO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_MAKEINFO="makeinfo" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi MAKEINFO=$ac_cv_prog_MAKEINFO if test -n "$MAKEINFO"; then echo "$as_me:$LINENO: result: $MAKEINFO" >&5 echo "${ECHO_T}$MAKEINFO" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi if test -n "$MAKEINFO"; then # Found it, now check the version. echo "$as_me:$LINENO: checking for modern makeinfo" >&5 echo $ECHO_N "checking for modern makeinfo... $ECHO_C" >&6 if test "${gcc_cv_prog_makeinfo_modern+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_prog_version=`$MAKEINFO --version 2>&1 | sed -n 's/^.*GNU texinfo.* \([0-9][0-9.]*\).*$/\1/p'` echo "configure:5232: version of makeinfo is $ac_prog_version" >&5 case $ac_prog_version in '') gcc_cv_prog_makeinfo_modern=no;; 4.[2-9]*) gcc_cv_prog_makeinfo_modern=yes;; *) gcc_cv_prog_makeinfo_modern=no;; esac fi echo "$as_me:$LINENO: result: $gcc_cv_prog_makeinfo_modern" >&5 echo "${ECHO_T}$gcc_cv_prog_makeinfo_modern" >&6 else gcc_cv_prog_makeinfo_modern=no fi fi if test $gcc_cv_prog_makeinfo_modern = no; then { echo "$as_me:$LINENO: WARNING: *** Makeinfo is missing or too old. *** Info documentation will not be built." >&5 echo "$as_me: WARNING: *** Makeinfo is missing or too old. *** Info documentation will not be built." >&2;} BUILD_INFO= else BUILD_INFO=info fi # Is pod2man recent enough to regenerate manpages? echo "$as_me:$LINENO: checking for recent Pod::Man" >&5 echo $ECHO_N "checking for recent Pod::Man... $ECHO_C" >&6 if (perl -e 'use 1.10 Pod::Man') >/dev/null 2>&1; then echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 GENERATED_MANPAGES=generated-manpages else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 GENERATED_MANPAGES= fi # How about lex? if test -f $srcdir/../flex/skel.c; then FLEX='$(objdir)/../flex/flex' else # Extract the first word of "flex", so it can be a program name with args. set dummy flex; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_FLEX+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$FLEX"; then ac_cv_prog_FLEX="$FLEX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_FLEX="flex" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done test -z "$ac_cv_prog_FLEX" && ac_cv_prog_FLEX="${CONFIG_SHELL-/bin/sh} ${srcdir}/../missing flex" fi fi FLEX=$ac_cv_prog_FLEX if test -n "$FLEX"; then echo "$as_me:$LINENO: result: $FLEX" >&5 echo "${ECHO_T}$FLEX" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi # Bison? # The -L switch is so bison can find its skeleton file. if test -f $srcdir/../bison/bison.simple; then BISON='$(objdir)/../bison/bison -L $(srcdir)/../bison/' else # Extract the first word of "bison", so it can be a program name with args. set dummy bison; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_BISON+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$BISON"; then ac_cv_prog_BISON="$BISON" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_BISON="bison" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done test -z "$ac_cv_prog_BISON" && ac_cv_prog_BISON="${CONFIG_SHELL-/bin/sh} ${srcdir}/../missing bison" fi fi BISON=$ac_cv_prog_BISON if test -n "$BISON"; then echo "$as_me:$LINENO: result: $BISON" >&5 echo "${ECHO_T}$BISON" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi # -------------------- # Checks for C headers # -------------------- echo "$as_me:$LINENO: checking for GNU C library" >&5 echo $ECHO_N "checking for GNU C library... $ECHO_C" >&6 if test "${gcc_cv_glibc+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { #if ! (defined __GLIBC__ || defined __GNU_LIBRARY__) #error Not a GNU C library system #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then gcc_cv_glibc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 gcc_cv_glibc=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $gcc_cv_glibc" >&5 echo "${ECHO_T}$gcc_cv_glibc" >&6 if test $gcc_cv_glibc = yes; then cat >>confdefs.h <<\_ACEOF #define _GNU_SOURCE 1 _ACEOF fi # Need to reject headers which give warnings, so that the -Werror bootstrap # works later. *sigh* This needs to come before all header checks. ac_c_preproc_warn_flag=yes echo "$as_me:$LINENO: checking for ANSI C header files" >&5 echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 if test "${ac_cv_header_stdc+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_header_stdc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_stdc=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); exit (0); } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_header_stdc=no fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi fi echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 echo "${ECHO_T}$ac_cv_header_stdc" >&6 if test $ac_cv_header_stdc = yes; then cat >>confdefs.h <<\_ACEOF #define STDC_HEADERS 1 _ACEOF fi echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6 if test "${ac_cv_header_time+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include int main () { if ((struct tm *) 0) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_header_time=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_time=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 echo "${ECHO_T}$ac_cv_header_time" >&6 if test $ac_cv_header_time = yes; then cat >>confdefs.h <<\_ACEOF #define TIME_WITH_SYS_TIME 1 _ACEOF fi echo "$as_me:$LINENO: checking for working stdbool.h" >&5 echo $ECHO_N "checking for working stdbool.h... $ECHO_C" >&6 if test "${ac_cv_header_stdbool_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { bool foo = false; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_header_stdbool_h=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_stdbool_h=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_header_stdbool_h" >&5 echo "${ECHO_T}$ac_cv_header_stdbool_h" >&6 if test $ac_cv_header_stdbool_h = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_STDBOOL_H 1 _ACEOF fi echo "$as_me:$LINENO: checking whether string.h and strings.h may both be included" >&5 echo $ECHO_N "checking whether string.h and strings.h may both be included... $ECHO_C" >&6 if test "${gcc_cv_header_string+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then gcc_cv_header_string=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 gcc_cv_header_string=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $gcc_cv_header_string" >&5 echo "${ECHO_T}$gcc_cv_header_string" >&6 if test $gcc_cv_header_string = yes; then cat >>confdefs.h <<\_ACEOF #define STRING_WITH_STRINGS 1 _ACEOF fi echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5 echo $ECHO_N "checking for sys/wait.h that is POSIX.1 compatible... $ECHO_C" >&6 if test "${ac_cv_header_sys_wait_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #ifndef WEXITSTATUS # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) #endif #ifndef WIFEXITED # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif int main () { int s; wait (&s); s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_header_sys_wait_h=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_sys_wait_h=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5 echo "${ECHO_T}$ac_cv_header_sys_wait_h" >&6 if test $ac_cv_header_sys_wait_h = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_SYS_WAIT_H 1 _ACEOF fi for ac_header in limits.h stddef.h string.h strings.h stdlib.h time.h \ fcntl.h unistd.h sys/file.h sys/time.h sys/mman.h \ sys/resource.h sys/param.h sys/times.h sys/stat.h \ direct.h malloc.h langinfo.h ldfcn.h locale.h wchar.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then eval "$as_ac_Header=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_Header=no" fi rm -f conftest.err conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done # Check for thread headers. echo "$as_me:$LINENO: checking for thread.h" >&5 echo $ECHO_N "checking for thread.h... $ECHO_C" >&6 if test "${ac_cv_header_thread_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_cv_header_thread_h=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_thread_h=no fi rm -f conftest.err conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_header_thread_h" >&5 echo "${ECHO_T}$ac_cv_header_thread_h" >&6 if test $ac_cv_header_thread_h = yes; then have_thread_h=yes else have_thread_h= fi echo "$as_me:$LINENO: checking for pthread.h" >&5 echo $ECHO_N "checking for pthread.h... $ECHO_C" >&6 if test "${ac_cv_header_pthread_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_cv_header_pthread_h=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_pthread_h=no fi rm -f conftest.err conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_header_pthread_h" >&5 echo "${ECHO_T}$ac_cv_header_pthread_h" >&6 if test $ac_cv_header_pthread_h = yes; then have_pthread_h=yes else have_pthread_h= fi # These tests can't be done till we know if we have limits.h. echo "$as_me:$LINENO: checking for CHAR_BIT" >&5 echo $ECHO_N "checking for CHAR_BIT... $ECHO_C" >&6 if test "${gcc_cv_decl_char_bit+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef HAVE_LIMITS_H #include #endif #ifdef CHAR_BIT found #endif _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "found" >/dev/null 2>&1; then gcc_cv_decl_char_bit=yes else gcc_cv_decl_char_bit=no fi rm -f conftest* fi echo "$as_me:$LINENO: result: $gcc_cv_decl_char_bit" >&5 echo "${ECHO_T}$gcc_cv_decl_char_bit" >&6 if test $gcc_cv_decl_char_bit = no; then echo "$as_me:$LINENO: checking number of bits in a byte" >&5 echo $ECHO_N "checking number of bits in a byte... $ECHO_C" >&6 if test "${gcc_cv_c_nbby+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else i=8 gcc_cv_c_nbby= while test $i -lt 65; do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { switch(0) { case (unsigned char)((unsigned long)1 << $i) == ((unsigned long)1 << $i): case (unsigned char)((unsigned long)1<<($i-1)) == ((unsigned long)1<<($i-1)): ; } ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then gcc_cv_c_nbby=$i; break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext i=`expr $i + 1` done test -z "$gcc_cv_c_nbby" && gcc_cv_c_nbby=failed fi echo "$as_me:$LINENO: result: $gcc_cv_c_nbby" >&5 echo "${ECHO_T}$gcc_cv_c_nbby" >&6 if test $gcc_cv_c_nbby = failed; then { { echo "$as_me:$LINENO: error: cannot determine number of bits in a byte" >&5 echo "$as_me: error: cannot determine number of bits in a byte" >&2;} { (exit 1); exit 1; }; } else cat >>confdefs.h <<_ACEOF #define CHAR_BIT $gcc_cv_c_nbby _ACEOF fi fi echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5 echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6 if test "${ac_cv_c_bigendian+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_c_bigendian=unknown # See if sys/param.h defines the BYTE_ORDER macro. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include int main () { #if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN bogus endian macros #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then # It does; now see whether it defined to BIG_ENDIAN or not. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include int main () { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_c_bigendian=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_c_bigendian=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_c_bigendian = unknown; then if test "$cross_compiling" = yes; then echo $ac_n "cross-compiling... " 2>&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ main () { /* Are we little or big endian? From Harbison&Steele. */ union { long l; char c[sizeof (long)]; } u; u.l = 1; exit (u.c[sizeof (long) - 1] == 1); } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_c_bigendian=no else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_c_bigendian=yes fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi fi echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5 echo "${ECHO_T}$ac_cv_c_bigendian" >&6 if test $ac_cv_c_bigendian = unknown; then echo "$as_me:$LINENO: checking to probe for byte ordering" >&5 echo $ECHO_N "checking to probe for byte ordering... $ECHO_C" >&6 cat >conftest.c <&6 ac_cv_c_bigendian=yes fi if test `grep -l LiTTleEnDian conftest.o` ; then echo $ac_n ' little endian probe OK, ' 1>&6 if test $ac_cv_c_bigendian = yes ; then ac_cv_c_bigendian=unknown; else ac_cv_c_bigendian=no fi fi echo $ac_n 'guessing bigendian ... ' >&6 fi fi echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5 echo "${ECHO_T}$ac_cv_c_bigendian" >&6 fi if test $ac_cv_c_bigendian = yes; then cat >>confdefs.h <<\_ACEOF #define WORDS_BIGENDIAN 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HOST_WORDS_BIG_ENDIAN 1 _ACEOF BYTEORDER=4321 else BYTEORDER=1234 fi cat >>confdefs.h <<_ACEOF #define BYTEORDER $BYTEORDER _ACEOF if test $ac_cv_c_bigendian = unknown; then { { echo "$as_me:$LINENO: error: unknown endianess - sorry" >&5 echo "$as_me: error: unknown endianess - sorry" >&2;} { (exit please pre-set ac_cv_c_bigendian); exit please pre-set ac_cv_c_bigendian; }; } fi # -------- # UNSORTED # -------- # Stage specific cflags for build. stage1_cflags= case $build in vax-*-*) if test x$GCC = xyes then stage1_cflags="-Wa,-J" else stage1_cflags="-J" fi ;; powerpc-*-darwin*) # The spiffy cpp-precomp chokes on some legitimate constructs in GCC # sources; use -no-cpp-precomp to get to GNU cpp. # Apple's GCC has bugs in designated initializer handling, so disable # that too. stage1_cflags="-no-cpp-precomp -DHAVE_DESIGNATED_INITIALIZERS=0" ;; esac # These libraries may be used by collect2. # We may need a special search path to get them linked. echo "$as_me:$LINENO: checking for collect2 libraries" >&5 echo $ECHO_N "checking for collect2 libraries... $ECHO_C" >&6 if test "${gcc_cv_collect2_libs+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else save_LIBS="$LIBS" for libs in '' -lld -lmld \ '-L/usr/lib/cmplrs/cc2.11 -lmld' \ '-L/usr/lib/cmplrs/cc3.11 -lmld' do LIBS="$libs" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char ldopen (); int main () { ldopen (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then gcc_cv_collect2_libs="$libs"; break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext done LIBS="$save_LIBS" test -z "$gcc_cv_collect2_libs" && gcc_cv_collect2_libs='none required' fi echo "$as_me:$LINENO: result: $gcc_cv_collect2_libs" >&5 echo "${ECHO_T}$gcc_cv_collect2_libs" >&6 case $gcc_cv_collect2_libs in "none required") ;; *) COLLECT2_LIBS=$gcc_cv_collect2_libs ;; esac # When building Ada code on Alpha, we need exc_resume which is usually in # -lexc. So test for it. save_LIBS="$LIBS" LIBS= echo "$as_me:$LINENO: checking for library containing exc_resume" >&5 echo $ECHO_N "checking for library containing exc_resume... $ECHO_C" >&6 if test "${ac_cv_search_exc_resume+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_func_search_save_LIBS=$LIBS ac_cv_search_exc_resume=no cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char exc_resume (); int main () { exc_resume (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_exc_resume="none required" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "$ac_cv_search_exc_resume" = no; then for ac_lib in exc; do LIBS="-l$ac_lib $ac_func_search_save_LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char exc_resume (); int main () { exc_resume (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_exc_resume="-l$ac_lib" break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext done fi LIBS=$ac_func_search_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_search_exc_resume" >&5 echo "${ECHO_T}$ac_cv_search_exc_resume" >&6 if test "$ac_cv_search_exc_resume" != no; then test "$ac_cv_search_exc_resume" = "none required" || LIBS="$ac_cv_search_exc_resume $LIBS" fi GNAT_LIBEXC="$LIBS" LIBS="$save_LIBS" # Some systems put ldexp and frexp in libm instead of libc; assume # they're both in the same place. jcf-dump needs them. save_LIBS="$LIBS" LIBS= echo "$as_me:$LINENO: checking for library containing ldexp" >&5 echo $ECHO_N "checking for library containing ldexp... $ECHO_C" >&6 if test "${ac_cv_search_ldexp+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_func_search_save_LIBS=$LIBS ac_cv_search_ldexp=no cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char ldexp (); int main () { ldexp (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_ldexp="none required" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "$ac_cv_search_ldexp" = no; then for ac_lib in m; do LIBS="-l$ac_lib $ac_func_search_save_LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char ldexp (); int main () { ldexp (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_ldexp="-l$ac_lib" break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext done fi LIBS=$ac_func_search_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_search_ldexp" >&5 echo "${ECHO_T}$ac_cv_search_ldexp" >&6 if test "$ac_cv_search_ldexp" != no; then test "$ac_cv_search_ldexp" = "none required" || LIBS="$ac_cv_search_ldexp $LIBS" fi LDEXP_LIB="$LIBS" LIBS="$save_LIBS" # Use only if it exists, # doesn't clash with , and declares intmax_t. echo "$as_me:$LINENO: checking for inttypes.h" >&5 echo $ECHO_N "checking for inttypes.h... $ECHO_C" >&6 if test "${gcc_cv_header_inttypes_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include int main () { intmax_t i = -1; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then gcc_cv_header_inttypes_h=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 gcc_cv_header_inttypes_h=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $gcc_cv_header_inttypes_h" >&5 echo "${ECHO_T}$gcc_cv_header_inttypes_h" >&6 if test $gcc_cv_header_inttypes_h = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_INTTYPES_H 1 _ACEOF fi for ac_func in times clock dup2 kill getrlimit setrlimit atoll atoq \ sysconf strsignal putc_unlocked fputc_unlocked fputs_unlocked \ fwrite_unlocked fprintf_unlocked getrusage nl_langinfo \ scandir alphasort gettimeofday mbstowcs wcswidth mmap mincore \ setlocale do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_func" >&5 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 if eval "test \"\${$as_ac_var+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define $ac_func to an innocuous variant, in case declares $ac_func. For example, HP-UX 11i declares gettimeofday. */ #define $ac_func innocuous_$ac_func /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $ac_func /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" { #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else char (*f) () = $ac_func; #endif #ifdef __cplusplus } #endif int main () { return f != $ac_func; ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "$as_ac_var=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_var=no" fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 if test `eval echo '${'$as_ac_var'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done if test x$ac_cv_func_mbstowcs = xyes; then echo "$as_me:$LINENO: checking whether mbstowcs works" >&5 echo $ECHO_N "checking whether mbstowcs works... $ECHO_C" >&6 if test "${gcc_cv_func_mbstowcs_works+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test "$cross_compiling" = yes; then gcc_cv_func_mbstowcs_works=yes else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main() { mbstowcs(0, "", 0); return 0; } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then gcc_cv_func_mbstowcs_works=yes else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) gcc_cv_func_mbstowcs_works=no fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi echo "$as_me:$LINENO: result: $gcc_cv_func_mbstowcs_works" >&5 echo "${ECHO_T}$gcc_cv_func_mbstowcs_works" >&6 if test x$gcc_cv_func_mbstowcs_works = xyes; then cat >>confdefs.h <<\_ACEOF #define HAVE_WORKING_MBSTOWCS 1 _ACEOF fi fi echo "$as_me:$LINENO: checking for ssize_t" >&5 echo $ECHO_N "checking for ssize_t... $ECHO_C" >&6 if test "${ac_cv_type_ssize_t+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { if ((ssize_t *) 0) return 0; if (sizeof (ssize_t)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_ssize_t=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_ssize_t=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_ssize_t" >&5 echo "${ECHO_T}$ac_cv_type_ssize_t" >&6 if test $ac_cv_type_ssize_t = yes; then : else cat >>confdefs.h <<_ACEOF #define ssize_t int _ACEOF fi # Try to determine the array type of the second argument of getgroups # for the target system (int or gid_t). echo "$as_me:$LINENO: checking for uid_t in sys/types.h" >&5 echo $ECHO_N "checking for uid_t in sys/types.h... $ECHO_C" >&6 if test "${ac_cv_type_uid_t+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "uid_t" >/dev/null 2>&1; then ac_cv_type_uid_t=yes else ac_cv_type_uid_t=no fi rm -f conftest* fi echo "$as_me:$LINENO: result: $ac_cv_type_uid_t" >&5 echo "${ECHO_T}$ac_cv_type_uid_t" >&6 if test $ac_cv_type_uid_t = no; then cat >>confdefs.h <<\_ACEOF #define uid_t int _ACEOF cat >>confdefs.h <<\_ACEOF #define gid_t int _ACEOF fi echo "$as_me:$LINENO: checking type of array argument to getgroups" >&5 echo $ECHO_N "checking type of array argument to getgroups... $ECHO_C" >&6 if test "${ac_cv_type_getgroups+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test "$cross_compiling" = yes; then ac_cv_type_getgroups=cross else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Thanks to Mike Rendell for this test. */ #include #define NGID 256 #undef MAX #define MAX(x, y) ((x) > (y) ? (x) : (y)) int main () { gid_t gidset[NGID]; int i, n; union { gid_t gval; long lval; } val; val.lval = -1; for (i = 0; i < NGID; i++) gidset[i] = val.gval; n = getgroups (sizeof (gidset) / MAX (sizeof (int), sizeof (gid_t)) - 1, gidset); /* Exit non-zero if getgroups seems to require an array of ints. This happens when gid_t is short but getgroups modifies an array of ints. */ exit ((n > 0 && gidset[n] != val.gval) ? 1 : 0); } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_getgroups=gid_t else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_type_getgroups=int fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_type_getgroups = cross; then cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "getgroups.*int.*gid_t" >/dev/null 2>&1; then ac_cv_type_getgroups=gid_t else ac_cv_type_getgroups=int fi rm -f conftest* fi fi echo "$as_me:$LINENO: result: $ac_cv_type_getgroups" >&5 echo "${ECHO_T}$ac_cv_type_getgroups" >&6 cat >>confdefs.h <<_ACEOF #define GETGROUPS_T $ac_cv_type_getgroups _ACEOF if test "${target}" = "${build}"; then TARGET_GETGROUPS_T=$ac_cv_type_getgroups else case "${target}" in # This condition may need some tweaking. It should include all # targets where the array type of the second argument of getgroups # is int and the type of gid_t is not equivalent to int. *-*-sunos* | *-*-ultrix*) TARGET_GETGROUPS_T=int ;; *) TARGET_GETGROUPS_T=gid_t ;; esac fi echo "$as_me:$LINENO: checking whether the printf functions support %p" >&5 echo $ECHO_N "checking whether the printf functions support %p... $ECHO_C" >&6 if test "${gcc_cv_func_printf_ptr+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test "$cross_compiling" = yes; then gcc_cv_func_printf_ptr=no else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main() { char buf[64]; char *p = buf, *q = NULL; sprintf(buf, "%p", p); sscanf(buf, "%p", &q); return (p != q); } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then gcc_cv_func_printf_ptr=yes else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) gcc_cv_func_printf_ptr=no fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi rm -f core core.* *.core fi echo "$as_me:$LINENO: result: $gcc_cv_func_printf_ptr" >&5 echo "${ECHO_T}$gcc_cv_func_printf_ptr" >&6 if test $gcc_cv_func_printf_ptr = yes ; then cat >>confdefs.h <<\_ACEOF #define HAVE_PRINTF_PTR 1 _ACEOF fi if test $ac_cv_header_sys_mman_h != yes \ || test $ac_cv_func_mmap != yes; then gcc_cv_func_mmap_file=no gcc_cv_func_mmap_dev_zero=no gcc_cv_func_mmap_anon=no else echo "$as_me:$LINENO: checking whether read-only mmap of a plain file works" >&5 echo $ECHO_N "checking whether read-only mmap of a plain file works... $ECHO_C" >&6 if test "${gcc_cv_func_mmap_file+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else # Add a system to this blacklist if # mmap(0, stat_size, PROT_READ, MAP_PRIVATE, fd, 0) doesn't return a # memory area containing the same data that you'd get if you applied # read() to the same fd. The only system known to have a problem here # is VMS, where text files have record structure. case "$host_os" in vms* | ultrix*) gcc_cv_func_mmap_file=no ;; *) gcc_cv_func_mmap_file=yes;; esac fi echo "$as_me:$LINENO: result: $gcc_cv_func_mmap_file" >&5 echo "${ECHO_T}$gcc_cv_func_mmap_file" >&6 echo "$as_me:$LINENO: checking whether mmap from /dev/zero works" >&5 echo $ECHO_N "checking whether mmap from /dev/zero works... $ECHO_C" >&6 if test "${gcc_cv_func_mmap_dev_zero+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else # Add a system to this blacklist if it has mmap() but /dev/zero # does not exist, or if mmapping /dev/zero does not give anonymous # zeroed pages with both the following properties: # 1. If you map N consecutive pages in with one call, and then # unmap any subset of those pages, the pages that were not # explicitly unmapped remain accessible. # 2. If you map two adjacent blocks of memory and then unmap them # both at once, they must both go away. # Systems known to be in this category are Windows (all variants), # VMS, and Darwin. case "$host_os" in vms* | cygwin* | pe | mingw* | darwin* | ultrix* | hpux10* | hpux11.00) gcc_cv_func_mmap_dev_zero=no ;; *) gcc_cv_func_mmap_dev_zero=yes;; esac fi echo "$as_me:$LINENO: result: $gcc_cv_func_mmap_dev_zero" >&5 echo "${ECHO_T}$gcc_cv_func_mmap_dev_zero" >&6 # Unlike /dev/zero, the MAP_ANON(YMOUS) defines can be probed for. echo "$as_me:$LINENO: checking for MAP_ANON(YMOUS)" >&5 echo $ECHO_N "checking for MAP_ANON(YMOUS)... $ECHO_C" >&6 if test "${gcc_cv_decl_map_anon+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON #endif int main () { int n = MAP_ANONYMOUS; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then gcc_cv_decl_map_anon=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 gcc_cv_decl_map_anon=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $gcc_cv_decl_map_anon" >&5 echo "${ECHO_T}$gcc_cv_decl_map_anon" >&6 if test $gcc_cv_decl_map_anon = no; then gcc_cv_func_mmap_anon=no else echo "$as_me:$LINENO: checking whether mmap with MAP_ANON(YMOUS) works" >&5 echo $ECHO_N "checking whether mmap with MAP_ANON(YMOUS) works... $ECHO_C" >&6 if test "${gcc_cv_func_mmap_anon+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else # Add a system to this blacklist if it has mmap() and MAP_ANON or # MAP_ANONYMOUS, but using mmap(..., MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) # doesn't give anonymous zeroed pages with the same properties listed # above for use of /dev/zero. # Systems known to be in this category are Windows, VMS, and SCO Unix. case "$host_os" in vms* | cygwin* | pe | mingw* | sco* | udk* ) gcc_cv_func_mmap_anon=no ;; *) gcc_cv_func_mmap_anon=yes;; esac fi echo "$as_me:$LINENO: result: $gcc_cv_func_mmap_anon" >&5 echo "${ECHO_T}$gcc_cv_func_mmap_anon" >&6 fi fi if test $gcc_cv_func_mmap_file = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_MMAP_FILE 1 _ACEOF fi if test $gcc_cv_func_mmap_dev_zero = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_MMAP_DEV_ZERO 1 _ACEOF fi if test $gcc_cv_func_mmap_anon = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_MMAP_ANON 1 _ACEOF fi case "${host}" in *-*-*vms*) # Under VMS, vfork works very differently than on Unix. The standard test # won't work, and it isn't easily adaptable. It makes more sense to # just force it. ac_cv_func_vfork_works=yes ;; esac echo "$as_me:$LINENO: checking for pid_t" >&5 echo $ECHO_N "checking for pid_t... $ECHO_C" >&6 if test "${ac_cv_type_pid_t+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { if ((pid_t *) 0) return 0; if (sizeof (pid_t)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_pid_t=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_pid_t=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_pid_t" >&5 echo "${ECHO_T}$ac_cv_type_pid_t" >&6 if test $ac_cv_type_pid_t = yes; then : else cat >>confdefs.h <<_ACEOF #define pid_t int _ACEOF fi for ac_header in unistd.h vfork.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then eval "$as_ac_Header=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_Header=no" fi rm -f conftest.err conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_func in fork vfork do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_func" >&5 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 if eval "test \"\${$as_ac_var+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define $ac_func to an innocuous variant, in case declares $ac_func. For example, HP-UX 11i declares gettimeofday. */ #define $ac_func innocuous_$ac_func /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $ac_func /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" { #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else char (*f) () = $ac_func; #endif #ifdef __cplusplus } #endif int main () { return f != $ac_func; ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "$as_ac_var=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_var=no" fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 if test `eval echo '${'$as_ac_var'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done if test "x$ac_cv_func_fork" = xyes; then echo "$as_me:$LINENO: checking for working fork" >&5 echo $ECHO_N "checking for working fork... $ECHO_C" >&6 if test "${ac_cv_func_fork_works+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test "$cross_compiling" = yes; then ac_cv_func_fork_works=cross else cat >conftest.$ac_ext <<_ACEOF /* By Ruediger Kuhlmann. */ #include #if HAVE_UNISTD_H # include #endif /* Some systems only have a dummy stub for fork() */ int main () { if (fork() < 0) exit (1); exit (0); } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_func_fork_works=yes else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_func_fork_works=no fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi echo "$as_me:$LINENO: result: $ac_cv_func_fork_works" >&5 echo "${ECHO_T}$ac_cv_func_fork_works" >&6 else ac_cv_func_fork_works=$ac_cv_func_fork fi if test "x$ac_cv_func_fork_works" = xcross; then case $host in *-*-amigaos* | *-*-msdosdjgpp*) # Override, as these systems have only a dummy fork() stub ac_cv_func_fork_works=no ;; *) ac_cv_func_fork_works=yes ;; esac { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} fi ac_cv_func_vfork_works=$ac_cv_func_vfork if test "x$ac_cv_func_vfork" = xyes; then echo "$as_me:$LINENO: checking for working vfork" >&5 echo $ECHO_N "checking for working vfork... $ECHO_C" >&6 if test "${ac_cv_func_vfork_works+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test "$cross_compiling" = yes; then ac_cv_func_vfork_works=cross else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Thanks to Paul Eggert for this test. */ #include #include #include #include #include #if HAVE_UNISTD_H # include #endif #if HAVE_VFORK_H # include #endif /* On some sparc systems, changes by the child to local and incoming argument registers are propagated back to the parent. The compiler is told about this with #include , but some compilers (e.g. gcc -O) don't grok . Test for this by using a static variable whose address is put into a register that is clobbered by the vfork. */ static void #ifdef __cplusplus sparc_address_test (int arg) # else sparc_address_test (arg) int arg; #endif { static pid_t child; if (!child) { child = vfork (); if (child < 0) { perror ("vfork"); _exit(2); } if (!child) { arg = getpid(); write(-1, "", 0); _exit (arg); } } } int main () { pid_t parent = getpid (); pid_t child; sparc_address_test (0); child = vfork (); if (child == 0) { /* Here is another test for sparc vfork register problems. This test uses lots of local variables, at least as many local variables as main has allocated so far including compiler temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should reuse the register of parent for one of the local variables, since it will think that parent can't possibly be used any more in this routine. Assigning to the local variable will thus munge parent in the parent process. */ pid_t p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); /* Convince the compiler that p..p7 are live; otherwise, it might use the same hardware register for all 8 local variables. */ if (p != p1 || p != p2 || p != p3 || p != p4 || p != p5 || p != p6 || p != p7) _exit(1); /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent from child file descriptors. If the child closes a descriptor before it execs or exits, this munges the parent's descriptor as well. Test for this by closing stdout in the child. */ _exit(close(fileno(stdout)) != 0); } else { int status; struct stat st; while (wait(&status) != child) ; exit( /* Was there some problem with vforking? */ child < 0 /* Did the child fail? (This shouldn't happen.) */ || status /* Did the vfork/compiler bug occur? */ || parent != getpid() /* Did the file descriptor bug occur? */ || fstat(fileno(stdout), &st) != 0 ); } } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_func_vfork_works=yes else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_func_vfork_works=no fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi echo "$as_me:$LINENO: result: $ac_cv_func_vfork_works" >&5 echo "${ECHO_T}$ac_cv_func_vfork_works" >&6 fi; if test "x$ac_cv_func_fork_works" = xcross; then ac_cv_func_vfork_works=$ac_cv_func_vfork { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} fi if test "x$ac_cv_func_vfork_works" = xyes; then cat >>confdefs.h <<\_ACEOF #define HAVE_WORKING_VFORK 1 _ACEOF else cat >>confdefs.h <<\_ACEOF #define vfork fork _ACEOF fi if test "x$ac_cv_func_fork_works" = xyes; then cat >>confdefs.h <<\_ACEOF #define HAVE_WORKING_FORK 1 _ACEOF fi am_cv_lib_iconv_ldpath= # Check whether --with-libiconv-prefix or --without-libiconv-prefix was given. if test "${with_libiconv_prefix+set}" = set; then withval="$with_libiconv_prefix" for dir in `echo "$withval" | tr : ' '`; do if test -d $dir/include; then CPPFLAGS="$CPPFLAGS -I$dir/include"; fi if test -d $dir/lib; then am_cv_lib_iconv_ldpath="-L$dir/lib"; fi done fi; for ac_header in iconv.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then eval "$as_ac_Header=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_Header=no" fi rm -f conftest.err conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done echo "$as_me:$LINENO: checking for iconv" >&5 echo $ECHO_N "checking for iconv... $ECHO_C" >&6 if test "${am_cv_func_iconv+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else am_cv_func_iconv="no, consider installing GNU libiconv" am_cv_lib_iconv=no cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include int main () { iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then am_cv_func_iconv=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "$am_cv_func_iconv" != yes; then am_save_LIBS="$LIBS" LIBS="$LIBS $am_cv_libiconv_ldpath -liconv" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include int main () { iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then am_cv_lib_iconv=yes am_cv_func_iconv=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS="$am_save_LIBS" fi fi echo "$as_me:$LINENO: result: $am_cv_func_iconv" >&5 echo "${ECHO_T}$am_cv_func_iconv" >&6 if test "$am_cv_func_iconv" = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_ICONV 1 _ACEOF echo "$as_me:$LINENO: checking for iconv declaration" >&5 echo $ECHO_N "checking for iconv declaration... $ECHO_C" >&6 if test "${am_cv_proto_iconv+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include extern #ifdef __cplusplus "C" #endif #if defined(__STDC__) || defined(__cplusplus) size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft); #else size_t iconv(); #endif int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then am_cv_proto_iconv_arg1="" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 am_cv_proto_iconv_arg1="const" fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);" fi am_cv_proto_iconv=`echo "$am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'` echo "$as_me:$LINENO: result: ${ac_t:- }$am_cv_proto_iconv" >&5 echo "${ECHO_T}${ac_t:- }$am_cv_proto_iconv" >&6 cat >>confdefs.h <<_ACEOF #define ICONV_CONST $am_cv_proto_iconv_arg1 _ACEOF fi LIBICONV= if test "$am_cv_lib_iconv" = yes; then LIBICONV="$am_cv_lib_iconv_ldpath -liconv" fi # Until we have in-tree GNU iconv: LIBICONV_DEP= echo "$as_me:$LINENO: checking for LC_MESSAGES" >&5 echo $ECHO_N "checking for LC_MESSAGES... $ECHO_C" >&6 if test "${am_cv_val_LC_MESSAGES+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { return LC_MESSAGES ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then am_cv_val_LC_MESSAGES=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 am_cv_val_LC_MESSAGES=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $am_cv_val_LC_MESSAGES" >&5 echo "${ECHO_T}$am_cv_val_LC_MESSAGES" >&6 if test $am_cv_val_LC_MESSAGES = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_LC_MESSAGES 1 _ACEOF fi # We will need to find libiberty.h and ansidecl.h saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -I${srcdir} -I${srcdir}/../include" for ac_func in getenv atol sbrk abort atof getcwd getwd \ strsignal putc_unlocked fputs_unlocked fwrite_unlocked \ fprintf_unlocked strstr errno snprintf vasprintf \ malloc realloc calloc free basename getopt clock do ac_tr_decl=HAVE_DECL_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` echo "$as_me:$LINENO: checking whether $ac_func is declared" >&5 echo $ECHO_N "checking whether $ac_func is declared... $ECHO_C" >&6 if eval "test \"\${gcc_cv_have_decl_$ac_func+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #undef $ac_tr_decl #define $ac_tr_decl 1 #include "ansidecl.h" #include "system.h" int main () { #ifndef $ac_func char *(*pfn) = (char *(*)) $ac_func ; #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "gcc_cv_have_decl_$ac_func=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "gcc_cv_have_decl_$ac_func=no" fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi if eval "test \"`echo '$gcc_cv_have_decl_'$ac_func`\" = yes"; then echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 ; cat >>confdefs.h <<_ACEOF #define $ac_tr_decl 1 _ACEOF else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 ; cat >>confdefs.h <<_ACEOF #define $ac_tr_decl 0 _ACEOF fi done if test x = y ; then cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_GETENV 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_ATOL 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_SBRK 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_ABORT 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_ATOF 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_GETCWD 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_GETWD 1 _ACEOF \ cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_STRSIGNAL 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_PUTC_UNLOCKED 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_FPUTS_UNLOCKED 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_FWRITE_UNLOCKED 1 _ACEOF \ cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_FPRINTF_UNLOCKED 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_STRSTR 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_ERRNO 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_SNPRINTF 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_VASPRINTF 1 _ACEOF \ cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_MALLOC 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_REALLOC 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_CALLOC 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_FREE 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_BASENAME 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_GETOPT 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_CLOCK 1 _ACEOF fi for ac_func in getrlimit setrlimit getrusage do ac_tr_decl=HAVE_DECL_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` echo "$as_me:$LINENO: checking whether $ac_func is declared" >&5 echo $ECHO_N "checking whether $ac_func is declared... $ECHO_C" >&6 if eval "test \"\${gcc_cv_have_decl_$ac_func+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #undef $ac_tr_decl #define $ac_tr_decl 1 #include "ansidecl.h" #include "system.h" #ifdef HAVE_SYS_RESOURCE_H #include #endif int main () { #ifndef $ac_func char *(*pfn) = (char *(*)) $ac_func ; #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "gcc_cv_have_decl_$ac_func=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "gcc_cv_have_decl_$ac_func=no" fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi if eval "test \"`echo '$gcc_cv_have_decl_'$ac_func`\" = yes"; then echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 ; cat >>confdefs.h <<_ACEOF #define $ac_tr_decl 1 _ACEOF else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 ; cat >>confdefs.h <<_ACEOF #define $ac_tr_decl 0 _ACEOF fi done if test x = y ; then cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_GETRLIMIT 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_SETRLIMIT 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_GETRUSAGE 1 _ACEOF fi cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include "ansidecl.h" #include "system.h" #ifdef HAVE_SYS_RESOURCE_H #include #endif int main () { rlim_t l = 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 cat >>confdefs.h <<\_ACEOF #define rlim_t long _ACEOF fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext for ac_func in ldgetname do ac_tr_decl=HAVE_DECL_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` echo "$as_me:$LINENO: checking whether $ac_func is declared" >&5 echo $ECHO_N "checking whether $ac_func is declared... $ECHO_C" >&6 if eval "test \"\${gcc_cv_have_decl_$ac_func+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #undef $ac_tr_decl #define $ac_tr_decl 1 #include "ansidecl.h" #include "system.h" #ifdef HAVE_LDFCN_H #include #endif int main () { #ifndef $ac_func char *(*pfn) = (char *(*)) $ac_func ; #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "gcc_cv_have_decl_$ac_func=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "gcc_cv_have_decl_$ac_func=no" fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi if eval "test \"`echo '$gcc_cv_have_decl_'$ac_func`\" = yes"; then echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 ; cat >>confdefs.h <<_ACEOF #define $ac_tr_decl 1 _ACEOF else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 ; cat >>confdefs.h <<_ACEOF #define $ac_tr_decl 0 _ACEOF fi done if test x = y ; then cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_LDGETNAME 1 _ACEOF fi for ac_func in times do ac_tr_decl=HAVE_DECL_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` echo "$as_me:$LINENO: checking whether $ac_func is declared" >&5 echo $ECHO_N "checking whether $ac_func is declared... $ECHO_C" >&6 if eval "test \"\${gcc_cv_have_decl_$ac_func+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #undef $ac_tr_decl #define $ac_tr_decl 1 #include "ansidecl.h" #include "system.h" #ifdef HAVE_SYS_TIMES_H #include #endif int main () { #ifndef $ac_func char *(*pfn) = (char *(*)) $ac_func ; #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "gcc_cv_have_decl_$ac_func=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "gcc_cv_have_decl_$ac_func=no" fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi if eval "test \"`echo '$gcc_cv_have_decl_'$ac_func`\" = yes"; then echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 ; cat >>confdefs.h <<_ACEOF #define $ac_tr_decl 1 _ACEOF else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 ; cat >>confdefs.h <<_ACEOF #define $ac_tr_decl 0 _ACEOF fi done if test x = y ; then cat >>confdefs.h <<\_ACEOF #define HAVE_DECL_TIMES 1 _ACEOF fi # More time-related stuff. echo "$as_me:$LINENO: checking for struct tms" >&5 echo $ECHO_N "checking for struct tms... $ECHO_C" >&6 if test "${ac_cv_struct_tms+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include "ansidecl.h" #include "system.h" #ifdef HAVE_SYS_TIMES_H #include #endif int main () { struct tms tms; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_struct_tms=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_struct_tms=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_struct_tms" >&5 echo "${ECHO_T}$ac_cv_struct_tms" >&6 if test $ac_cv_struct_tms = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_STRUCT_TMS 1 _ACEOF fi # use gcc_cv_* here because this doesn't match the behavior of AC_CHECK_TYPE. # revisit after autoconf 2.50. echo "$as_me:$LINENO: checking for clock_t" >&5 echo $ECHO_N "checking for clock_t... $ECHO_C" >&6 if test "${gcc_cv_type_clock_t+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include "ansidecl.h" #include "system.h" int main () { clock_t x; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then gcc_cv_type_clock_t=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 gcc_cv_type_clock_t=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $gcc_cv_type_clock_t" >&5 echo "${ECHO_T}$gcc_cv_type_clock_t" >&6 if test $gcc_cv_type_clock_t = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_CLOCK_T 1 _ACEOF fi echo "$as_me:$LINENO: checking for uchar" >&5 echo $ECHO_N "checking for uchar... $ECHO_C" >&6 if test "${gcc_cv_type_uchar+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include "ansidecl.h" #include "system.h" int main () { if ((uchar *)0) return 0; if (sizeof(uchar)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_uchar=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_uchar=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $gcc_cv_type_uchar" >&5 echo "${ECHO_T}$gcc_cv_type_uchar" >&6 if test $ac_cv_type_uchar = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_UCHAR 1 _ACEOF fi # Restore CFLAGS from before the gcc_AC_NEED_DECLARATIONS tests. CFLAGS="$saved_CFLAGS" # Check whether --enable-initfini-array or --disable-initfini-array was given. if test "${enable_initfini_array+set}" = set; then enableval="$enable_initfini_array" else echo "$as_me:$LINENO: checking for .preinit_array/.init_array/.fini_array support" >&5 echo $ECHO_N "checking for .preinit_array/.init_array/.fini_array support... $ECHO_C" >&6 if test "${gcc_cv_initfini_array+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test "$cross_compiling" = yes; then gcc_cv_initfini_array=no else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ static int x = -1; int main (void) { return x; } int foo (void) { x = 0; } int (*fp) (void) __attribute__ ((section (".init_array"))) = foo; _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then gcc_cv_initfini_array=yes else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) gcc_cv_initfini_array=no fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi echo "$as_me:$LINENO: result: $gcc_cv_initfini_array" >&5 echo "${ECHO_T}$gcc_cv_initfini_array" >&6 enable_initfini_array=$gcc_cv_initfini_array fi; if test $enable_initfini_array = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_INITFINI_ARRAY 1 _ACEOF fi # mkdir takes a single argument on some systems. echo "$as_me:$LINENO: checking if mkdir takes one argument" >&5 echo $ECHO_N "checking if mkdir takes one argument... $ECHO_C" >&6 if test "${gcc_cv_mkdir_takes_one_arg+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_DIRECT_H # include #endif int main () { mkdir ("foo", 0); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then gcc_cv_mkdir_takes_one_arg=no else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 gcc_cv_mkdir_takes_one_arg=yes fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $gcc_cv_mkdir_takes_one_arg" >&5 echo "${ECHO_T}$gcc_cv_mkdir_takes_one_arg" >&6 if test $gcc_cv_mkdir_takes_one_arg = yes ; then cat >>confdefs.h <<\_ACEOF #define MKDIR_TAKES_ONE_ARG 1 _ACEOF fi # File extensions manext='.1' objext='.o' # With Setjmp/Longjmp based exception handling. # Check whether --enable-sjlj-exceptions or --disable-sjlj-exceptions was given. if test "${enable_sjlj_exceptions+set}" = set; then enableval="$enable_sjlj_exceptions" sjlj=`if test $enableval = yes; then echo 1; else echo 0; fi` cat >>confdefs.h <<_ACEOF #define CONFIG_SJLJ_EXCEPTIONS $sjlj _ACEOF fi; # For platforms with the unwind ABI which includes an unwind library, # libunwind, we can choose to use the system libunwind. # Check whether --with-system-libunwind or --without-system-libunwind was given. if test "${with_system_libunwind+set}" = set; then withval="$with_system_libunwind" fi; # -------------------------------------------------------- # Build, host, and target specific configuration fragments # -------------------------------------------------------- # Collect build-machine-specific information. . ${srcdir}/config.build # Collect host-machine-specific information. . ${srcdir}/config.host target_gtfiles= # Collect target-machine-specific information. . ${srcdir}/config.gcc extra_objs="${host_extra_objs} ${extra_objs}" extra_gcc_objs="${host_extra_gcc_objs} ${extra_gcc_objs}" # Default the target-machine variables that were not explicitly set. if test x"$tm_file" = x then tm_file=$cpu_type/$cpu_type.h; fi if test x"$extra_headers" = x then extra_headers=; fi if test x$md_file = x then md_file=$cpu_type/$cpu_type.md; fi if test x$out_file = x then out_file=$cpu_type/$cpu_type.c; fi if test x"$tmake_file" = x then tmake_file=$cpu_type/t-$cpu_type fi if test x"$dwarf2" = xyes then tm_file="$tm_file tm-dwarf2.h" fi # Say what files are being used for the output code and MD file. echo "Using \`$srcdir/config/$out_file' for machine-specific logic." echo "Using \`$srcdir/config/$md_file' as machine description file." # If any of the xm_file variables contain nonexistent files, warn # about them and drop them. bx= for x in $build_xm_file; do if test -f $srcdir/config/$x then bx="$bx $x" else { echo "$as_me:$LINENO: WARNING: $srcdir/config/$x does not exist." >&5 echo "$as_me: WARNING: $srcdir/config/$x does not exist." >&2;} fi done build_xm_file="$bx" hx= for x in $host_xm_file; do if test -f $srcdir/config/$x then hx="$hx $x" else { echo "$as_me:$LINENO: WARNING: $srcdir/config/$x does not exist." >&5 echo "$as_me: WARNING: $srcdir/config/$x does not exist." >&2;} fi done host_xm_file="$hx" tx= for x in $xm_file; do if test -f $srcdir/config/$x then tx="$tx $x" else { echo "$as_me:$LINENO: WARNING: $srcdir/config/$x does not exist." >&5 echo "$as_me: WARNING: $srcdir/config/$x does not exist." >&2;} fi done xm_file="$tx" count=a for f in $tm_file; do count=${count}x done if test $count = ax; then echo "Using \`$srcdir/config/$tm_file' as target machine macro file." else echo "Using the following target machine macro files:" for f in $tm_file; do echo " $srcdir/config/$f" done fi if test x$need_64bit_hwint = xyes; then cat >>confdefs.h <<\_ACEOF #define NEED_64BIT_HOST_WIDE_INT 1 _ACEOF fi count=a for f in $host_xm_file; do count=${count}x done if test $count = a; then : elif test $count = ax; then echo "Using \`$srcdir/config/$host_xm_file' as host machine macro file." else echo "Using the following host machine macro files:" for f in $host_xm_file; do echo " $srcdir/config/$f" done fi echo "Using ${out_host_hook_obj} for host machine hooks." if test "$host_xm_file" != "$build_xm_file"; then count=a for f in $build_xm_file; do count=${count}x done if test $count = a; then : elif test $count = ax; then echo "Using \`$srcdir/config/$build_xm_file' as build machine macro file." else echo "Using the following build machine macro files:" for f in $build_xm_file; do echo " $srcdir/config/$f" done fi fi # Check if a valid thread package case ${enable_threads_flag} in "" | no) # No threads target_thread_file='single' ;; yes) # default target_thread_file='single' ;; aix | dce | gnat | irix | posix | rtems | \ single | solaris | vxworks | win32 ) target_thread_file=${enable_threads_flag} ;; *) echo "${enable_threads_flag} is an unknown thread package" 1>&2 exit 1 ;; esac if test x${thread_file} = x; then # No thread file set by target-specific clauses in config.gcc, # so use file chosen by default logic above thread_file=${target_thread_file} fi if test x$enable___cxa_atexit = xyes || \ test x$enable___cxa_atexit = x -a x$default_use_cxa_atexit = xyes; then echo "$as_me:$LINENO: checking for __cxa_atexit" >&5 echo $ECHO_N "checking for __cxa_atexit... $ECHO_C" >&6 if test "${ac_cv_func___cxa_atexit+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define __cxa_atexit to an innocuous variant, in case declares __cxa_atexit. For example, HP-UX 11i declares gettimeofday. */ #define __cxa_atexit innocuous___cxa_atexit /* System header to define __stub macros and hopefully few prototypes, which can conflict with char __cxa_atexit (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef __cxa_atexit /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" { #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char __cxa_atexit (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub___cxa_atexit) || defined (__stub_____cxa_atexit) choke me #else char (*f) () = __cxa_atexit; #endif #ifdef __cplusplus } #endif int main () { return f != __cxa_atexit; ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_func___cxa_atexit=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_func___cxa_atexit=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_func___cxa_atexit" >&5 echo "${ECHO_T}$ac_cv_func___cxa_atexit" >&6 if test $ac_cv_func___cxa_atexit = yes; then cat >>confdefs.h <<\_ACEOF #define DEFAULT_USE_CXA_ATEXIT 1 _ACEOF else echo "__cxa_atexit can't be enabled on this target" fi fi # Look for a file containing extra machine modes. if test -n "$extra_modes" && test -f $srcdir/config/$extra_modes; then extra_modes_file='$(srcdir)'/config/${extra_modes} cat >>confdefs.h <<_ACEOF #define EXTRA_MODES_FILE "config/$extra_modes" _ACEOF fi # auto-host.h is the file containing items generated by autoconf and is # the first file included by config.h. # If host=build, it is correct to have bconfig include auto-host.h # as well. If host!=build, we are in error and need to do more # work to find out the build config parameters. if test x$host = x$build then build_auto=auto-host.h FORBUILD=.. else # We create a subdir, then run autoconf in the subdir. # To prevent recursion we set host and build for the new # invocation of configure to the build for this invocation # of configure. tempdir=build.$$ rm -rf $tempdir mkdir $tempdir cd $tempdir case ${srcdir} in /* | A-Za-z:\\/* ) realsrcdir=${srcdir};; *) realsrcdir=../${srcdir};; esac saved_CFLAGS="${CFLAGS}" CC="${CC_FOR_BUILD}" CFLAGS="${CFLAGS_FOR_BUILD}" \ ${realsrcdir}/configure \ --target=$target_alias --host=$build_alias --build=$build_alias CFLAGS="${saved_CFLAGS}" # We just finished tests for the build machine, so rename # the file auto-build.h in the gcc directory. mv auto-host.h ../auto-build.h cd .. rm -rf $tempdir build_auto=auto-build.h FORBUILD=../${build_subdir} fi tm_file="${tm_file} defaults.h" tm_p_file="${tm_p_file} tm-preds.h" host_xm_file="auto-host.h ansidecl.h ${host_xm_file}" build_xm_file="${build_auto} ansidecl.h ${build_xm_file}" # We don't want ansidecl.h in target files, write code there in ISO/GNU C. # put this back in temporarily. xm_file="ansidecl.h ${xm_file}" # -------- # UNSORTED # -------- # Get the version trigger filename from the toplevel if test "${with_gcc_version_trigger+set}" = set; then gcc_version_trigger=$with_gcc_version_trigger else gcc_version_trigger=${srcdir}/version.c fi gcc_version_full=`grep version_string ${gcc_version_trigger} | sed -e 's/.*"\([^"]*\)".*/\1/'` gcc_version=`echo ${gcc_version_full} | sed -e 's/\([^ ]*\) .*/\1/'` # Compile in configure arguments. if test -f configargs.h ; then # Being re-configured. gcc_config_arguments=`grep configuration_arguments configargs.h | sed -e 's/.*"\([^"]*\)".*/\1/'` gcc_config_arguments="$gcc_config_arguments : (reconfigured) $TOPLEVEL_CONFIGURE_ARGUMENTS" else gcc_config_arguments="$TOPLEVEL_CONFIGURE_ARGUMENTS" fi # Double all backslashes and backslash all quotes to turn # gcc_config_arguments into a C string. sed -e 's/\\/\\\\/g; s/"/\\"/g' <conftest.out $gcc_config_arguments EOF gcc_config_arguments_str=`cat conftest.out` rm -f conftest.out cat > configargs.h <&5 echo $ECHO_N "checking whether NLS is requested... $ECHO_C" >&6 if test x"$USE_NLS" != xyes; then echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 else echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 cat >>confdefs.h <<\_ACEOF #define ENABLE_NLS 1 _ACEOF echo "$as_me:$LINENO: checking for catalogs to be installed" >&5 echo $ECHO_N "checking for catalogs to be installed... $ECHO_C" >&6 # Look for .po and .gmo files in the source directory. CATALOGS= XLINGUAS= for cat in $srcdir/po/*.gmo $srcdir/po/*.po; do # If there aren't any .gmo files the shell will give us the # literal string "../path/to/srcdir/po/*.gmo" which has to be # weeded out. case "$cat" in *\**) continue;; esac # The quadruple backslash is collapsed to a double backslash # by the backticks, then collapsed again by the double quotes, # leaving us with one backslash in the sed expression (right # before the dot that mustn't act as a wildcard). cat=`echo $cat | sed -e "s!$srcdir/!!" -e "s!\\\\.po!.gmo!"` lang=`echo $cat | sed -e 's!po/!!' -e "s!\\\\.gmo!!"` # The user is allowed to set LINGUAS to a list of languages to # install catalogs for. If it's empty that means "all of them." if test "x$LINGUAS" = x; then CATALOGS="$CATALOGS $cat" XLINGUAS="$XLINGUAS $lang" else case "$LINGUAS" in *$lang*) CATALOGS="$CATALOGS $cat" XLINGUAS="$XLINGUAS $lang" ;; esac fi done LINGUAS="$XLINGUAS" echo "$as_me:$LINENO: result: $LINGUAS" >&5 echo "${ECHO_T}$LINGUAS" >&6 fi # If LIBINTL contains LIBICONV, then clear LIBICONV so we don't get # -liconv on the link line twice. case "$LIBINTL" in *$LIBICONV*) LIBICONV= ;; esac # Windows32 Registry support for specifying GCC installation paths. # Check whether --enable-win32-registry or --disable-win32-registry was given. if test "${enable_win32_registry+set}" = set; then enableval="$enable_win32_registry" fi; case $host_os in win32 | pe | cygwin* | mingw32* | uwin*) echo "$as_me:$LINENO: checking whether windows registry support is requested" >&5 echo $ECHO_N "checking whether windows registry support is requested... $ECHO_C" >&6 if test "x$enable_win32_registry" != xno; then cat >>confdefs.h <<\_ACEOF #define ENABLE_WIN32_REGISTRY 1 _ACEOF echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 echo "$as_me:$LINENO: checking for library containing RegOpenKeyExA" >&5 echo $ECHO_N "checking for library containing RegOpenKeyExA... $ECHO_C" >&6 if test "${ac_cv_search_RegOpenKeyExA+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_func_search_save_LIBS=$LIBS ac_cv_search_RegOpenKeyExA=no cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char RegOpenKeyExA (); int main () { RegOpenKeyExA (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_RegOpenKeyExA="none required" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "$ac_cv_search_RegOpenKeyExA" = no; then for ac_lib in advapi32; do LIBS="-l$ac_lib $ac_func_search_save_LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char RegOpenKeyExA (); int main () { RegOpenKeyExA (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_RegOpenKeyExA="-l$ac_lib" break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext done fi LIBS=$ac_func_search_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_search_RegOpenKeyExA" >&5 echo "${ECHO_T}$ac_cv_search_RegOpenKeyExA" >&6 if test "$ac_cv_search_RegOpenKeyExA" != no; then test "$ac_cv_search_RegOpenKeyExA" = "none required" || LIBS="$ac_cv_search_RegOpenKeyExA $LIBS" fi else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi # Check if user specified a different registry key. case "x${enable_win32_registry}" in x | xyes) # default. gcc_cv_win32_registry_key="$VERSION" ;; xno) # no registry lookup. gcc_cv_win32_registry_key='' ;; *) # user-specified key. gcc_cv_win32_registry_key="$enable_win32_registry" ;; esac if test "x$enable_win32_registry" != xno; then echo "$as_me:$LINENO: checking registry key on windows hosts" >&5 echo $ECHO_N "checking registry key on windows hosts... $ECHO_C" >&6 cat >>confdefs.h <<_ACEOF #define WIN32_REGISTRY_KEY "$gcc_cv_win32_registry_key" _ACEOF echo "$as_me:$LINENO: result: $gcc_cv_win32_registry_key" >&5 echo "${ECHO_T}$gcc_cv_win32_registry_key" >&6 fi ;; esac # Get an absolute path to the GCC top-level source directory holddir=`${PWDCMD-pwd}` cd $srcdir topdir=`${PWDCMD-pwd}` cd $holddir # Conditionalize the makefile for this host machine. xmake_file= for f in ${host_xmake_file} do if test -f ${srcdir}/config/$f then xmake_file="${xmake_file} \$(srcdir)/config/$f" fi done # Conditionalize the makefile for this target machine. tmake_file_= for f in ${tmake_file} do if test -f ${srcdir}/config/$f then tmake_file_="${tmake_file_} \$(srcdir)/config/$f" fi done tmake_file="${tmake_file_}" symbolic_link='ln -s' # If the host doesn't support symlinks, modify CC in # FLAGS_TO_PASS so CC="stage1/xgcc -Bstage1/" works. # Otherwise, we can use "CC=$(CC)". rm -f symtest.tem if $symbolic_link $srcdir/gcc.c symtest.tem 2>/dev/null then cc_set_by_configure="\$(CC)" quoted_cc_set_by_configure="\$(CC)" stage_prefix_set_by_configure="\$(STAGE_PREFIX)" quoted_stage_prefix_set_by_configure="\$(STAGE_PREFIX)" else rm -f symtest.tem if cp -p $srcdir/gcc.c symtest.tem 2>/dev/null then symbolic_link="cp -p" else symbolic_link="cp" fi cc_set_by_configure="\`case '\$(CC)' in stage*) echo '\$(CC)' | sed -e 's|stage|../stage|g';; *) echo '\$(CC)';; esac\`" quoted_cc_set_by_configure="\\\`case '\\\$(CC)' in stage*) echo '\\\$(CC)' | sed -e 's|stage|../stage|g';; *) echo '\\\$(CC)';; esac\\\`" stage_prefix_set_by_configure="\`case '\$(STAGE_PREFIX)' in stage*) echo '\$(STAGE_PREFIX)' | sed -e 's|stage|../stage|g';; *) echo '\$(STAGE_PREFIX)';; esac\`" quoted_stage_prefix_set_by_configure="\\\`case '\\\$(STAGE_PREFIX)' in stage*) echo '\\\$(STAGE_PREFIX)' | sed -e 's|stage|../stage|g';; *) echo '\\\$(STAGE_PREFIX)';; esac\\\`" fi rm -f symtest.tem out_object_file=`basename $out_file .c`.o tm_file_list= tm_include_list= for f in $tm_file; do case $f in defaults.h ) tm_file_list="${tm_file_list} \$(srcdir)/$f" tm_include_list="${tm_include_list} $f" ;; * ) tm_file_list="${tm_file_list} \$(srcdir)/config/$f" tm_include_list="${tm_include_list} config/$f" ;; esac done tm_p_file_list= tm_p_include_list= for f in $tm_p_file; do case $f in tm-preds.h ) tm_p_file_list="${tm_p_file_list} $f" tm_p_include_list="${tm_p_include_list} $f" ;; * ) tm_p_file_list="${tm_p_file_list} \$(srcdir)/config/$f" tm_p_include_list="${tm_p_include_list} config/$f" esac done xm_file_list= xm_include_list= for f in $xm_file; do case $f in ansidecl.h ) xm_file_list="${xm_file_list} \$(srcdir)/../include/$f" xm_include_list="${xm_include_list} $f" ;; auto-host.h ) xm_file_list="${xm_file_list} $f" xm_include_list="${xm_include_list} $f" ;; * ) xm_file_list="${xm_file_list} \$(srcdir)/config/$f" xm_include_list="${xm_include_list} config/$f" ;; esac done host_xm_file_list= host_xm_include_list= for f in $host_xm_file; do case $f in ansidecl.h ) host_xm_file_list="${host_xm_file_list} \$(srcdir)/../include/$f" host_xm_include_list="${host_xm_include_list} $f" ;; auto-host.h ) host_xm_file_list="${host_xm_file_list} $f" host_xm_include_list="${host_xm_include_list} $f" ;; * ) host_xm_file_list="${host_xm_file_list} \$(srcdir)/config/$f" host_xm_include_list="${host_xm_include_list} config/$f" ;; esac done build_xm_file_list= for f in $build_xm_file; do case $f in ansidecl.h ) build_xm_file_list="${build_xm_file_list} \$(srcdir)/../include/$f" build_xm_include_list="${build_xm_include_list} $f" ;; auto-build.h | auto-host.h ) build_xm_file_list="${build_xm_file_list} $f" build_xm_include_list="${build_xm_include_list} $f" ;; * ) build_xm_file_list="${build_xm_file_list} \$(srcdir)/config/$f" build_xm_include_list="${build_xm_include_list} config/$f" ;; esac done # Define macro CROSS_COMPILE in compilation if this is a cross-compiler. # Also use all.cross instead of all.internal and adjust SYSTEM_HEADER_DIR. CROSS= ALL=all.internal SYSTEM_HEADER_DIR='$(NATIVE_SYSTEM_HEADER_DIR)' if test x$host != x$target then CROSS="-DCROSS_COMPILE" ALL=all.cross SYSTEM_HEADER_DIR='$(CROSS_SYSTEM_HEADER_DIR)' case "$host","$target" in # Darwin crosses can use the host system's libraries and headers, # because of the fat library support. Of course, it must be the # same version of Darwin on both sides. Allow the user to # just say --target=foo-darwin without a version number to mean # "the version on this system". *-*-darwin*,*-*-darwin*) hostos=`echo $host | sed 's/.*-darwin/darwin/'` targetos=`echo $target | sed 's/.*-darwin/darwin/'` if test $hostos = $targetos -o $targetos = darwin ; then CROSS= SYSTEM_HEADER_DIR='$(NATIVE_SYSTEM_HEADER_DIR)' with_headers=yes fi ;; i?86-*-*,x86_64-*-* \ | powerpc*-*-*,powerpc64*-*-*) CROSS="$CROSS -DNATIVE_CROSS" ;; esac elif test "x$TARGET_SYSTEM_ROOT" != x; then # This is just $(TARGET_SYSTEM_ROOT)$(NATIVE_SYSTEM_HEADER_DIR) SYSTEM_HEADER_DIR='$(CROSS_SYSTEM_HEADER_DIR)' fi # If this is a cross-compiler that does not # have its own set of headers then define # inhibit_libc # If this is using newlib, without having the headers available now, # then define inhibit_libc in LIBGCC2_CFLAGS. # This prevents libgcc2 from containing any code which requires libc # support. inhibit_libc= if { { test x$host != x$target && test "x$with_sysroot" = x ; } || test x$with_newlib = xyes ; } && { test "x$with_headers" = x || test "x$with_headers" = xno ; } ; then inhibit_libc=-Dinhibit_libc fi # When building gcc with a cross-compiler, we need to adjust things so # that the generator programs are still built with the native compiler. # Also, we cannot run fixincludes or fix-header. # These are the normal (build=host) settings: BUILD_PREFIX= BUILD_PREFIX_1=ignore- CC_FOR_BUILD='$(CC)' BUILD_CFLAGS='$(ALL_CFLAGS)' STMP_FIXINC=stmp-fixinc # Possibly disable fixproto, on a per-target basis. case ${use_fixproto} in no) STMP_FIXPROTO= ;; yes) STMP_FIXPROTO=stmp-fixproto ;; esac # And these apply if build != host, or we are generating coverage data if test x$build != x$host || test "x$coverage_flags" != x then BUILD_PREFIX=build- BUILD_PREFIX_1=build- BUILD_CFLAGS='$(INTERNAL_CFLAGS) $(T_CFLAGS) $(CFLAGS_FOR_BUILD)' if test "x$TARGET_SYSTEM_ROOT" = x; then STMP_FIXINC= STMP_FIXPROTO= fi fi # Expand extra_headers to include complete path. # This substitutes for lots of t-* files. extra_headers_list= # Prepend $(srcdir)/config/${cpu_type}/ to every entry in extra_headers. for file in ${extra_headers} ; do extra_headers_list="${extra_headers_list} \$(srcdir)/config/${cpu_type}/${file}" done # Add a definition of USE_COLLECT2 if system wants one. case $use_collect2 in no) use_collect2= ;; "") ;; *) host_xm_defines="${host_xm_defines} USE_COLLECT2" xm_defines="${xm_defines} USE_COLLECT2" ;; esac # Identify the assembler which will work hand-in-glove with the newly # built GCC, so that we can examine its features. This is the assembler # which will be driven by the driver program. # # If build != host, and we aren't building gas in-tree, we identify a # build->target assembler and hope that it will have the same features # as the host->target assembler we'll be using. echo "$as_me:$LINENO: checking what assembler to use" >&5 echo $ECHO_N "checking what assembler to use... $ECHO_C" >&6 in_tree_gas=no gcc_cv_as= gcc_cv_gas_major_version= gcc_cv_gas_minor_version= gcc_cv_as_gas_srcdir=`echo $srcdir | sed -e 's,/gcc$,,'`/gas if test -x "$DEFAULT_ASSEMBLER"; then gcc_cv_as="$DEFAULT_ASSEMBLER" elif test -x "$AS"; then gcc_cv_as="$AS" elif test -x as$host_exeext; then # Build using assembler in the current directory. gcc_cv_as=./as$host_exeext elif test -f $gcc_cv_as_gas_srcdir/configure.in \ && test -f ../gas/Makefile; then # Single tree build which includes gas. in_tree_gas=yes gcc_cv_as_bfd_srcdir=`echo $srcdir | sed -e 's,/gcc$,,'`/bfd for f in $gcc_cv_as_bfd_srcdir/configure \ $gcc_cv_as_gas_srcdir/configure \ $gcc_cv_as_gas_srcdir/configure.in \ $gcc_cv_as_gas_srcdir/Makefile.in ; do gcc_cv_gas_version=`grep '^VERSION=[0-9]*\.[0-9]*' $f` if test x$gcc_cv_gas_version != x; then break fi done gcc_cv_gas_major_version=`expr "$gcc_cv_gas_version" : "VERSION=\([0-9]*\)"` gcc_cv_gas_minor_version=`expr "$gcc_cv_gas_version" : "VERSION=[0-9]*\.\([0-9]*\)"` gcc_cv_gas_patch_version=`expr "$gcc_cv_gas_version" : "VERSION=[0-9]*\.[0-9]*\.\([0-9]*\)"` case $gcc_cv_gas_patch_version in "") gcc_cv_gas_patch_version="0" ;; esac gcc_cv_gas_vers=`expr \( \( $gcc_cv_gas_major_version \* 1000 \) \ + $gcc_cv_gas_minor_version \) \* 1000 \ + $gcc_cv_gas_patch_version` rm -f as$host_exeext $symbolic_link ../gas/as-new$host_exeext as$host_exeext 2>/dev/null in_tree_gas_is_elf=no if grep 'obj_format = elf' ../gas/Makefile > /dev/null \ || (grep 'obj_format = multi' ../gas/Makefile \ && grep 'extra_objects =.* obj-elf' ../gas/Makefile) > /dev/null then in_tree_gas_is_elf=yes fi fi if test "x$gcc_cv_as" = x; then # Search the same directories that the installed compiler will # search. Else we may find the wrong assembler and lose. If we # do not find a suitable assembler binary, then try the user's # path. # # Also note we have to check MD_EXEC_PREFIX before checking the # user's path. Unfortunately, there is no good way to get at the # value of MD_EXEC_PREFIX here. So we do a brute force search # through all the known MD_EXEC_PREFIX values. Ugh. This needs # to be fixed as part of the make/configure rewrite too. if test "x$exec_prefix" = xNONE; then if test "x$prefix" = xNONE; then test_prefix=/usr/local else test_prefix=$prefix fi else test_prefix=$exec_prefix fi # If the loop below does not find an assembler, then use whatever # one we can find in the users's path. # user's path. if test "x$program_prefix" != xNONE; then gcc_cv_as=${program_prefix}as$host_exeext else gcc_cv_as=`echo as | sed ${program_transform_name}`$host_exeext fi test_dirs="$test_prefix/libexec/gcc/$target_noncanonical/$gcc_version \ $test_prefix/libexec/gcc/$target_noncanonical \ /usr/lib/gcc/$target_noncanonical/$gcc_version \ /usr/lib/gcc/$target_noncanonical \ $test_prefix/$target_noncanonical/bin/$target_noncanonical/$gcc_version \ $test_prefix/$target_noncanonical/bin" if test x$host = x$target; then test_dirs="$test_dirs \ /usr/libexec \ /usr/ccs/gcc \ /usr/ccs/bin \ /udk/usr/ccs/bin \ /bsd43/usr/lib/cmplrs/cc \ /usr/cross64/usr/bin \ /usr/lib/cmplrs/cc \ /sysv/usr/lib/cmplrs/cc \ /svr4/usr/lib/cmplrs/cc \ /usr/bin" fi for dir in $test_dirs; do if test -x $dir/as$host_exeext; then gcc_cv_as=$dir/as$host_exeext break; fi done fi case $in_tree_gas in yes) echo "$as_me:$LINENO: result: \"newly built gas\"" >&5 echo "${ECHO_T}\"newly built gas\"" >&6 ;; no) echo "$as_me:$LINENO: result: $gcc_cv_as" >&5 echo "${ECHO_T}$gcc_cv_as" >&6 ;; esac # Identify the linker which will work hand-in-glove with the newly # built GCC, so that we can examine its features. This is the linker # which will be driven by the driver program. # # If build != host, and we aren't building gas in-tree, we identify a # build->target linker and hope that it will have the same features # as the host->target linker we'll be using. echo "$as_me:$LINENO: checking what linker to use" >&5 echo $ECHO_N "checking what linker to use... $ECHO_C" >&6 in_tree_ld=no gcc_cv_ld= gcc_cv_gld_major_version= gcc_cv_gld_minor_version= gcc_cv_ld_gld_srcdir=`echo $srcdir | sed -e 's,/gcc$,,'`/ld gcc_cv_ld_bfd_srcdir=`echo $srcdir | sed -e 's,/gcc$,,'`/bfd if test -x "$DEFAULT_LINKER"; then gcc_cv_ld="$DEFAULT_LINKER" elif test -x "$LD"; then gcc_cv_ld="$LD" elif test -x collect-ld$host_exeext; then # Build using linker in the current directory. gcc_cv_ld=./collect-ld$host_exeext elif test -f $gcc_cv_ld_gld_srcdir/configure.in \ && test -f ../ld/Makefile; then # Single tree build which includes ld. in_tree_ld=yes in_tree_ld_is_elf=no if (grep 'EMUL = .*elf' ../ld/Makefile \ || grep 'EMUL = .*linux' ../ld/Makefile) > /dev/null; then in_tree_ld_is_elf=yes fi for f in $gcc_cv_ld_bfd_srcdir/configure $gcc_cv_ld_gld_srcdir/configure $gcc_cv_ld_gld_srcdir/configure.in $gcc_cv_ld_gld_srcdir/Makefile.in do gcc_cv_gld_version=`grep '^VERSION=[0-9]*\.[0-9]*' $f` if test x$gcc_cv_gld_version != x; then break fi done gcc_cv_gld_major_version=`expr "$gcc_cv_gld_version" : "VERSION=\([0-9]*\)"` gcc_cv_gld_minor_version=`expr "$gcc_cv_gld_version" : "VERSION=[0-9]*\.\([0-9]*\)"` rm -f collect-ld$host_exeext $symbolic_link ../ld/ld-new$host_exeext collect-ld$host_exeext \ 2>/dev/null fi if test "x$gcc_cv_ld" = x; then # Search the same directories that the installed compiler will # search. Else we may find the wrong linker and lose. If we # do not find a suitable linker binary, then try the user's # path. # # Also note we have to check MD_EXEC_PREFIX before checking the # user's path. Unfortunately, there is no good way to get at the # value of MD_EXEC_PREFIX here. So we do a brute force search # through all the known MD_EXEC_PREFIX values. Ugh. This needs # to be fixed as part of the make/configure rewrite too. if test "x$exec_prefix" = xNONE; then if test "x$prefix" = xNONE; then test_prefix=/usr/local else test_prefix=$prefix fi else test_prefix=$exec_prefix fi # If the loop below does not find an linker, then use whatever # one we can find in the users's path. # user's path. if test "x$program_prefix" != xNONE; then gcc_cv_ld=${program_prefix}ld$host_exeext else gcc_cv_ld=`echo ld | sed ${program_transform_name}`$host_exeext fi test_dirs="$test_prefix/libexec/gcc/$target_noncanonical/$gcc_version \ $test_prefix/libexec/gcc/$target_noncanonical \ /usr/lib/gcc/$target_noncanonical/$gcc_version \ /usr/lib/gcc/$target_noncanonical \ $test_prefix/$target_noncanonical/bin/$target_noncanonical/$gcc_version \ $test_prefix/$target_noncanonical/bin" if test x$host = x$target; then test_dirs="$test_dirs \ /usr/libexec \ /usr/ccs/gcc \ /usr/ccs/bin \ /udk/usr/ccs/bin \ /bsd43/usr/lib/cmplrs/cc \ /usr/cross64/usr/bin \ /usr/lib/cmplrs/cc \ /sysv/usr/lib/cmplrs/cc \ /svr4/usr/lib/cmplrs/cc \ /usr/bin" fi for dir in $test_dirs; do if test -x $dir/ld$host_exeext; then gcc_cv_ld=$dir/ld$host_exeext break; fi done fi case $in_tree_ld in yes) echo "$as_me:$LINENO: result: \"newly built ld\"" >&5 echo "${ECHO_T}\"newly built ld\"" >&6 ;; no) echo "$as_me:$LINENO: result: $gcc_cv_ld" >&5 echo "${ECHO_T}$gcc_cv_ld" >&6 ;; esac # Figure out what nm we will be using. gcc_cv_binutils_srcdir=`echo $srcdir | sed -e 's,/gcc$,,'`/binutils echo "$as_me:$LINENO: checking what nm to use" >&5 echo $ECHO_N "checking what nm to use... $ECHO_C" >&6 in_tree_nm=no if test -x nm$host_exeext; then gcc_cv_nm=./nm$host_exeext elif test -f $gcc_cv_binutils_srcdir/configure.in \ && test -f ../binutils/Makefile; then # Single tree build which includes binutils. in_tree_nm=yes gcc_cv_nm=./nm$host_exeext rm -f nm$host_exeext $symbolic_link ../binutils/nm-new$host_exeext nm$host_exeext 2>/dev/null elif test "x$program_prefix" != xNONE; then gcc_cv_nm=${program_prefix}nm$host_exeext else gcc_cv_nm=`echo nm | sed ${program_transform_name}`$host_exeext fi case $in_tree_nm in yes) echo "$as_me:$LINENO: result: \"newly built nm\"" >&5 echo "${ECHO_T}\"newly built nm\"" >&6 ;; no) echo "$as_me:$LINENO: result: $gcc_cv_nm" >&5 echo "${ECHO_T}$gcc_cv_nm" >&6 ;; esac # Figure out what objdump we will be using. echo "$as_me:$LINENO: checking what objdump to use" >&5 echo $ECHO_N "checking what objdump to use... $ECHO_C" >&6 in_tree_objdump=no if test -x objdump$host_exeext; then gcc_cv_objdump=./objdump$host_exeext elif test -f $gcc_cv_binutils_srcdir/configure.in \ && test -f ../binutils/Makefile; then # Single tree build which includes binutils. in_tree_objdump=yes gcc_cv_objdump=./objdump$host_exeext rm -f objdump$host_exeext $symbolic_link ../binutils/objdump$host_exeext \ objdump$host_exeext 2>/dev/null elif test "x$program_prefix" != xNONE; then gcc_cv_objdump=${program_prefix}objdump$host_exeext else gcc_cv_objdump=`echo objdump | \ sed ${program_transform_name}`$host_exeext fi case $in_tree_objdump in yes) echo "$as_me:$LINENO: result: \"newly built objdump\"" >&5 echo "${ECHO_T}\"newly built objdump\"" >&6 ;; no) echo "$as_me:$LINENO: result: $gcc_cv_objdump" >&5 echo "${ECHO_T}$gcc_cv_objdump" >&6 ;; esac # Figure out what assembler alignment features are present. echo "$as_me:$LINENO: checking assembler for .balign and .p2align" >&5 echo $ECHO_N "checking assembler for .balign and .p2align... $ECHO_C" >&6 if test "${gcc_cv_as_balign_and_p2align+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_balign_and_p2align=no if test $in_tree_gas = yes; then if test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 6 \) \* 1000 + 0` then gcc_cv_as_balign_and_p2align=yes fi elif test x$gcc_cv_as != x; then echo '.balign 4 .p2align 2' > conftest.s if { ac_try='$gcc_cv_as -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then gcc_cv_as_balign_and_p2align=yes else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_balign_and_p2align" >&5 echo "${ECHO_T}$gcc_cv_as_balign_and_p2align" >&6 if test $gcc_cv_as_balign_and_p2align = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_GAS_BALIGN_AND_P2ALIGN 1 _ACEOF fi echo "$as_me:$LINENO: checking assembler for .p2align with maximum skip" >&5 echo $ECHO_N "checking assembler for .p2align with maximum skip... $ECHO_C" >&6 if test "${gcc_cv_as_max_skip_p2align+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_max_skip_p2align=no if test $in_tree_gas = yes; then if test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 8 \) \* 1000 + 0` then gcc_cv_as_max_skip_p2align=yes fi elif test x$gcc_cv_as != x; then echo '.p2align 4,,7' > conftest.s if { ac_try='$gcc_cv_as -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then gcc_cv_as_max_skip_p2align=yes else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_max_skip_p2align" >&5 echo "${ECHO_T}$gcc_cv_as_max_skip_p2align" >&6 if test $gcc_cv_as_max_skip_p2align = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_GAS_MAX_SKIP_P2ALIGN 1 _ACEOF fi echo "$as_me:$LINENO: checking assembler for working .subsection -1" >&5 echo $ECHO_N "checking assembler for working .subsection -1... $ECHO_C" >&6 if test "${gcc_cv_as_subsection_m1+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_subsection_m1=no if test $in_tree_gas = yes; then if test $in_tree_gas_is_elf = yes \ && test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 9 \) \* 1000 + 0` then gcc_cv_as_subsection_m1=yes fi elif test x$gcc_cv_as != x; then echo 'conftest_label1: .word 0 .subsection -1 conftest_label2: .word 0 .previous' > conftest.s if { ac_try='$gcc_cv_as -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then if test x$gcc_cv_nm != x; then $gcc_cv_nm conftest.o | grep conftest_label1 > conftest.nm1 $gcc_cv_nm conftest.o | grep conftest_label2 | sed -e 's/label2/label1/' > conftest.nm2 if cmp conftest.nm1 conftest.nm2 > /dev/null 2>&1 then : else gcc_cv_as_subsection_m1=yes fi rm -f conftest.nm1 conftest.nm2 fi else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_subsection_m1" >&5 echo "${ECHO_T}$gcc_cv_as_subsection_m1" >&6 if test $gcc_cv_as_subsection_m1 = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_GAS_SUBSECTION_ORDERING 1 _ACEOF fi echo "$as_me:$LINENO: checking assembler for .weak" >&5 echo $ECHO_N "checking assembler for .weak... $ECHO_C" >&6 if test "${gcc_cv_as_weak+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_weak=no if test $in_tree_gas = yes; then if test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 2 \) \* 1000 + 0` then gcc_cv_as_weak=yes fi elif test x$gcc_cv_as != x; then echo ' .weak foobar' > conftest.s if { ac_try='$gcc_cv_as -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then gcc_cv_as_weak=yes else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_weak" >&5 echo "${ECHO_T}$gcc_cv_as_weak" >&6 if test $gcc_cv_as_weak = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_GAS_WEAK 1 _ACEOF fi echo "$as_me:$LINENO: checking assembler for .nsubspa comdat" >&5 echo $ECHO_N "checking assembler for .nsubspa comdat... $ECHO_C" >&6 if test "${gcc_cv_as_nsubspa_comdat+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_nsubspa_comdat=no if test $in_tree_gas = yes; then if test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 15 \) \* 1000 + 91` then gcc_cv_as_nsubspa_comdat=yes fi elif test x$gcc_cv_as != x; then echo ' .SPACE $TEXT$ .NSUBSPA $CODE$,COMDAT' > conftest.s if { ac_try='$gcc_cv_as -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then gcc_cv_as_nsubspa_comdat=yes else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_nsubspa_comdat" >&5 echo "${ECHO_T}$gcc_cv_as_nsubspa_comdat" >&6 if test $gcc_cv_as_nsubspa_comdat = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_GAS_NSUBSPA_COMDAT 1 _ACEOF fi # .hidden needs to be supported in both the assembler and the linker, # because GNU LD versions before 2.12.1 have buggy support for STV_HIDDEN. # This is irritatingly difficult to feature test for; we have to check the # date string after the version number. If we've got an in-tree # ld, we don't know its patchlevel version, so we set the baseline at 2.13 # to be safe. # The gcc_GAS_CHECK_FEATURE call just sets a cache variable. echo "$as_me:$LINENO: checking assembler for .hidden" >&5 echo $ECHO_N "checking assembler for .hidden... $ECHO_C" >&6 if test "${gcc_cv_as_hidden+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_hidden=no if test $in_tree_gas = yes; then if test $in_tree_gas_is_elf = yes \ && test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 13 \) \* 1000 + 0` then gcc_cv_as_hidden=yes fi elif test x$gcc_cv_as != x; then echo ' .hidden foobar foobar:' > conftest.s if { ac_try='$gcc_cv_as -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then gcc_cv_as_hidden=yes else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_hidden" >&5 echo "${ECHO_T}$gcc_cv_as_hidden" >&6 echo "$as_me:$LINENO: checking linker for .hidden support" >&5 echo $ECHO_N "checking linker for .hidden support... $ECHO_C" >&6 if test "${gcc_cv_ld_hidden+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test $in_tree_ld = yes ; then gcc_cv_ld_hidden=no if test "$gcc_cv_gld_major_version" -eq 2 -a "$gcc_cv_gld_minor_version" -ge 13 -o "$gcc_cv_gld_major_version" -gt 2 \ && test $in_tree_ld_is_elf = yes; then gcc_cv_ld_hidden=yes fi else gcc_cv_ld_hidden=yes ld_ver=`$gcc_cv_ld --version 2>/dev/null | sed 1q` if echo "$ld_ver" | grep GNU > /dev/null; then ld_vers=`echo $ld_ver | sed -n \ -e 's,^.*[ ]\([0-9][0-9]*\.[0-9][0-9]*\)$,\1,p' \ -e 's,^.*[ ]\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)$,\1,p' \ -e 's,^.*[ ]\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)$,\1,p' \ -e 's,^.*[ ]\([0-9][0-9]*\.[0-9][0-9]*\)[ ].*$,\1,p' \ -e 's,^.*[ ]\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)[ ].*$,\1,p' \ -e 's,^.*[ ]\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)[ ].*$,\1,p'` ld_date=`echo $ld_ver | sed -n 's,^.*\([2-9][0-9][0-9][0-9]\)[-]*\([01][0-9]\)[-]*\([0-3][0-9]\).*$,\1\2\3,p'` if test 0"$ld_date" -lt 20020404; then if test -n "$ld_date"; then # If there was date string, but was earlier than 2002-04-04, fail gcc_cv_ld_hidden=no elif test -z "$ld_vers"; then # If there was no date string nor ld version number, something is wrong gcc_cv_ld_hidden=no else ld_vers_major=`expr "$ld_vers" : '\([0-9]*\)'` ld_vers_minor=`expr "$ld_vers" : '[0-9]*\.\([0-9]*\)'` ld_vers_patch=`expr "$ld_vers" : '[0-9]*\.[0-9]*\.\([0-9]*\)'` test -z "$ld_vers_patch" && ld_vers_patch=0 if test "$ld_vers_major" -lt 2; then gcc_cv_ld_hidden=no elif test "$ld_vers_major" -eq 2 -a "$ld_vers_minor" -lt 12; then gcc_cv_ld_hidden="no" elif test "$ld_vers_major" -eq 2 -a "$ld_vers_minor" -eq 12 -a "$ld_vers_patch" -eq 0; then gcc_cv_ld_hidden=no fi fi fi else case "${target}" in hppa64*-*-hpux* | ia64*-*-hpux*) gcc_cv_ld_hidden=yes ;; *) gcc_cv_ld_hidden=no ;; esac fi fi fi echo "$as_me:$LINENO: result: $gcc_cv_ld_hidden" >&5 echo "${ECHO_T}$gcc_cv_ld_hidden" >&6 libgcc_visibility=no if test $gcc_cv_as_hidden = yes && test $gcc_cv_ld_hidden = yes; then libgcc_visibility=yes cat >>confdefs.h <<\_ACEOF #define HAVE_GAS_HIDDEN 1 _ACEOF fi # Check if we have .[us]leb128, and support symbol arithmetic with it. echo "$as_me:$LINENO: checking assembler for .sleb128 and .uleb128" >&5 echo $ECHO_N "checking assembler for .sleb128 and .uleb128... $ECHO_C" >&6 if test "${gcc_cv_as_leb128+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_leb128=no if test $in_tree_gas = yes; then if test $in_tree_gas_is_elf = yes \ && test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 11 \) \* 1000 + 0` then gcc_cv_as_leb128=yes fi elif test x$gcc_cv_as != x; then echo ' .data .uleb128 L2 - L1 L1: .uleb128 1280 .sleb128 -1010 L2:' > conftest.s if { ac_try='$gcc_cv_as -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then # GAS versions before 2.11 do not support uleb128, # despite appearing to. # ??? There exists an elf-specific test that will crash # the assembler. Perhaps it's better to figure out whether # arbitrary sections are supported and try the test. as_ver=`$gcc_cv_as --version 2>/dev/null | sed 1q` if echo "$as_ver" | grep GNU > /dev/null; then as_ver=`echo $as_ver | sed -e 's/GNU assembler \([0-9.][0-9.]*\).*/\1/'` as_major=`echo $as_ver | sed 's/\..*//'` as_minor=`echo $as_ver | sed 's/[^.]*\.\([0-9]*\).*/\1/'` if test $as_major -eq 2 && test $as_minor -lt 11 then : else gcc_cv_as_leb128=yes fi fi else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_leb128" >&5 echo "${ECHO_T}$gcc_cv_as_leb128" >&6 if test $gcc_cv_as_leb128 = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_AS_LEB128 1 _ACEOF fi # GAS versions up to and including 2.11.0 may mis-optimize # .eh_frame data. echo "$as_me:$LINENO: checking assembler for eh_frame optimization" >&5 echo $ECHO_N "checking assembler for eh_frame optimization... $ECHO_C" >&6 if test "${gcc_cv_as_eh_frame+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_eh_frame=no if test $in_tree_gas = yes; then if test $in_tree_gas_is_elf = yes \ && test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 12 \) \* 1000 + 0` then gcc_cv_as_eh_frame=yes fi elif test x$gcc_cv_as != x; then echo ' .text .LFB1: .4byte 0 .L1: .4byte 0 .LFE1: .section .eh_frame,"aw",@progbits __FRAME_BEGIN__: .4byte .LECIE1-.LSCIE1 .LSCIE1: .4byte 0x0 .byte 0x1 .ascii "z\0" .byte 0x1 .byte 0x78 .byte 0x1a .byte 0x0 .byte 0x4 .4byte 1 .p2align 1 .LECIE1: .LSFDE1: .4byte .LEFDE1-.LASFDE1 .LASFDE1: .4byte .LASFDE1-__FRAME_BEGIN__ .4byte .LFB1 .4byte .LFE1-.LFB1 .byte 0x4 .4byte .LFE1-.LFB1 .byte 0x4 .4byte .L1-.LFB1 .LEFDE1:' > conftest.s if { ac_try='$gcc_cv_as -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then cat > conftest.lit < conftest.big </dev/null \ | tail -3 > conftest.got \ && { cmp conftest.lit conftest.got > /dev/null 2>&1 \ || cmp conftest.big conftest.got > /dev/null 2>&1; } then gcc_cv_as_eh_frame=yes elif { ac_try='$gcc_cv_as -o conftest.o --traditional-format /dev/null' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then gcc_cv_as_eh_frame=buggy else # Uh oh, what do we do now? gcc_cv_as_eh_frame=no fi else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_eh_frame" >&5 echo "${ECHO_T}$gcc_cv_as_eh_frame" >&6 if test $gcc_cv_as_eh_frame = buggy; then cat >>confdefs.h <<\_ACEOF #define USE_AS_TRADITIONAL_FORMAT 1 _ACEOF fi echo "$as_me:$LINENO: checking assembler for section merging support" >&5 echo $ECHO_N "checking assembler for section merging support... $ECHO_C" >&6 if test "${gcc_cv_as_shf_merge+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_shf_merge=no if test $in_tree_gas = yes; then if test $in_tree_gas_is_elf = yes \ && test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 12 \) \* 1000 + 0` then gcc_cv_as_shf_merge=yes fi elif test x$gcc_cv_as != x; then echo '.section .rodata.str, "aMS", @progbits, 1' > conftest.s if { ac_try='$gcc_cv_as --fatal-warnings -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then gcc_cv_as_shf_merge=yes else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_shf_merge" >&5 echo "${ECHO_T}$gcc_cv_as_shf_merge" >&6 cat >>confdefs.h <<_ACEOF #define HAVE_GAS_SHF_MERGE `if test $gcc_cv_as_shf_merge = yes; then echo 1; else echo 0; fi` _ACEOF # Thread-local storage - the check is heavily parametrized. conftest_s= tls_first_major= tls_first_minor= tls_as_opt= case "$target" in alpha*-*-*) conftest_s=' .section ".tdata","awT",@progbits foo: .long 25 .text ldq $27,__tls_get_addr($29) !literal!1 lda $16,foo($29) !tlsgd!1 jsr $26,($27),__tls_get_addr !lituse_tlsgd!1 ldq $27,__tls_get_addr($29) !literal!2 lda $16,foo($29) !tlsldm!2 jsr $26,($27),__tls_get_addr !lituse_tlsldm!2 ldq $1,foo($29) !gotdtprel ldah $2,foo($29) !dtprelhi lda $3,foo($2) !dtprello lda $4,foo($29) !dtprel ldq $1,foo($29) !gottprel ldah $2,foo($29) !tprelhi lda $3,foo($2) !tprello lda $4,foo($29) !tprel' tls_first_major=2 tls_first_minor=13 tls_as_opt=--fatal-warnings ;; i[34567]86-*-*) conftest_s=' .section ".tdata","awT",@progbits foo: .long 25 .text movl %gs:0, %eax leal foo@TLSGD(,%ebx,1), %eax leal foo@TLSLDM(%ebx), %eax leal foo@DTPOFF(%eax), %edx movl foo@GOTTPOFF(%ebx), %eax subl foo@GOTTPOFF(%ebx), %eax addl foo@GOTNTPOFF(%ebx), %eax movl foo@INDNTPOFF, %eax movl $foo@TPOFF, %eax subl $foo@TPOFF, %eax leal foo@NTPOFF(%ecx), %eax' tls_first_major=2 tls_first_minor=14 tls_as_opt=--fatal-warnings ;; x86_64-*-*) conftest_s=' .section ".tdata","awT",@progbits foo: .long 25 .text movq %fs:0, %rax leaq foo@TLSGD(%rip), %rdi leaq foo@TLSLD(%rip), %rdi leaq foo@DTPOFF(%rax), %rdx movq foo@GOTTPOFF(%rip), %rax movq $foo@TPOFF, %rax' tls_first_major=2 tls_first_minor=14 tls_as_opt=--fatal-warnings ;; ia64-*-*) conftest_s=' .section ".tdata","awT",@progbits foo: data8 25 .text addl r16 = @ltoff(@dtpmod(foo#)), gp addl r17 = @ltoff(@dtprel(foo#)), gp addl r18 = @ltoff(@tprel(foo#)), gp addl r19 = @dtprel(foo#), gp adds r21 = @dtprel(foo#), r13 movl r23 = @dtprel(foo#) addl r20 = @tprel(foo#), gp adds r22 = @tprel(foo#), r13 movl r24 = @tprel(foo#)' tls_first_major=2 tls_first_minor=13 tls_as_opt=--fatal-warnings ;; powerpc-*-*) conftest_s=' .section ".tdata","awT",@progbits .align 2 ld0: .space 4 ld1: .space 4 x1: .space 4 x2: .space 4 x3: .space 4 .text addi 3,31,ld0@got@tlsgd bl __tls_get_addr addi 3,31,x1@got@tlsld bl __tls_get_addr addi 9,3,x1@dtprel addis 9,3,x2@dtprel@ha addi 9,9,x2@dtprel@l lwz 9,x3@got@tprel(31) add 9,9,x@tls addi 9,2,x1@tprel addis 9,2,x2@tprel@ha addi 9,9,x2@tprel@l' tls_first_major=2 tls_first_minor=14 tls_as_opt="-a32 --fatal-warnings" ;; powerpc64-*-*) conftest_s=' .section ".tdata","awT",@progbits .align 3 ld0: .space 8 ld1: .space 8 x1: .space 8 x2: .space 8 x3: .space 8 .text addi 3,2,ld0@got@tlsgd bl .__tls_get_addr nop addi 3,2,ld1@toc bl .__tls_get_addr nop addi 3,2,x1@got@tlsld bl .__tls_get_addr nop addi 9,3,x1@dtprel bl .__tls_get_addr nop addis 9,3,x2@dtprel@ha addi 9,9,x2@dtprel@l bl .__tls_get_addr nop ld 9,x3@got@dtprel(2) add 9,9,3 bl .__tls_get_addr nop' tls_first_major=2 tls_first_minor=14 tls_as_opt="-a64 --fatal-warnings" ;; s390-*-*) conftest_s=' .section ".tdata","awT",@progbits foo: .long 25 .text .long foo@TLSGD .long foo@TLSLDM .long foo@DTPOFF .long foo@NTPOFF .long foo@GOTNTPOFF .long foo@INDNTPOFF l %r1,foo@GOTNTPOFF(%r12) l %r1,0(%r1):tls_load:foo bas %r14,0(%r1,%r13):tls_gdcall:foo bas %r14,0(%r1,%r13):tls_ldcall:foo' tls_first_major=2 tls_first_minor=14 tls_as_opt="-m31 --fatal-warnings" ;; s390x-*-*) conftest_s=' .section ".tdata","awT",@progbits foo: .long 25 .text .quad foo@TLSGD .quad foo@TLSLDM .quad foo@DTPOFF .quad foo@NTPOFF .quad foo@GOTNTPOFF lg %r1,foo@GOTNTPOFF(%r12) larl %r1,foo@INDNTPOFF brasl %r14,__tls_get_offset@PLT:tls_gdcall:foo brasl %r14,__tls_get_offset@PLT:tls_ldcall:foo' tls_first_major=2 tls_first_minor=14 tls_as_opt="-m64 -Aesame --fatal-warnings" ;; sh-*-* | sh[34]-*-*) conftest_s=' .section ".tdata","awT",@progbits foo: .long 25 .text .long foo@TLSGD .long foo@TLSLDM .long foo@DTPOFF .long foo@GOTTPOFF .long foo@TPOFF' tls_first_major=2 tls_first_minor=13 tls_as_opt=--fatal-warnings ;; sparc*-*-*) case "$target" in sparc*-sun-solaris2.*) on_solaris=yes ;; *) on_solaris=no ;; esac if test x$on_solaris = xyes && test x$gas_flag = xno; then conftest_s=' .section ".tdata",#alloc,#write,#tls foo: .long 25 .text sethi %tgd_hi22(foo), %o0 add %o0, %tgd_lo10(foo), %o1 add %l7, %o1, %o0, %tgd_add(foo) call __tls_get_addr, %tgd_call(foo) sethi %tldm_hi22(foo), %l1 add %l1, %tldm_lo10(foo), %l2 add %l7, %l2, %o0, %tldm_add(foo) call __tls_get_addr, %tldm_call(foo) sethi %tldo_hix22(foo), %l3 xor %l3, %tldo_lox10(foo), %l4 add %o0, %l4, %l5, %tldo_add(foo) sethi %tie_hi22(foo), %o3 add %o3, %tie_lo10(foo), %o3 ld [%l7 + %o3], %o2, %tie_ld(foo) add %g7, %o2, %o4, %tie_add(foo) sethi %tle_hix22(foo), %l1 xor %l1, %tle_lox10(foo), %o5 ld [%g7 + %o5], %o1' tls_first_major=0 tls_first_minor=0 else conftest_s=' .section ".tdata","awT",@progbits foo: .long 25 .text sethi %tgd_hi22(foo), %o0 add %o0, %tgd_lo10(foo), %o1 add %l7, %o1, %o0, %tgd_add(foo) call __tls_get_addr, %tgd_call(foo) sethi %tldm_hi22(foo), %l1 add %l1, %tldm_lo10(foo), %l2 add %l7, %l2, %o0, %tldm_add(foo) call __tls_get_addr, %tldm_call(foo) sethi %tldo_hix22(foo), %l3 xor %l3, %tldo_lox10(foo), %l4 add %o0, %l4, %l5, %tldo_add(foo) sethi %tie_hi22(foo), %o3 add %o3, %tie_lo10(foo), %o3 ld [%l7 + %o3], %o2, %tie_ld(foo) add %g7, %o2, %o4, %tie_add(foo) sethi %tle_hix22(foo), %l1 xor %l1, %tle_lox10(foo), %o5 ld [%g7 + %o5], %o1' tls_first_major=2 tls_first_minor=14 tls_as_opt="-32 --fatal-warnings" fi ;; esac if test -z "$tls_first_major"; then : # If we don't have a check, assume no support. else echo "$as_me:$LINENO: checking assembler for thread-local storage support" >&5 echo $ECHO_N "checking assembler for thread-local storage support... $ECHO_C" >&6 if test "${gcc_cv_as_tls+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_tls=no if test $in_tree_gas = yes; then if test $gcc_cv_gas_vers -ge `expr \( \( $tls_first_major \* 1000 \) + $tls_first_minor \) \* 1000 + 0` then gcc_cv_as_tls=yes fi elif test x$gcc_cv_as != x; then echo "$conftest_s" > conftest.s if { ac_try='$gcc_cv_as $tls_as_opt -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then gcc_cv_as_tls=yes else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_tls" >&5 echo "${ECHO_T}$gcc_cv_as_tls" >&6 if test $gcc_cv_as_tls = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_AS_TLS 1 _ACEOF fi fi # Target-specific assembler checks. case "$target" in # All TARGET_ABI_OSF targets. alpha*-*-osf* | alpha*-*-linux* | alpha*-*-*bsd*) echo "$as_me:$LINENO: checking assembler for explicit relocation support" >&5 echo $ECHO_N "checking assembler for explicit relocation support... $ECHO_C" >&6 if test "${gcc_cv_as_alpha_explicit_relocs+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_alpha_explicit_relocs=no if test $in_tree_gas = yes; then if test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 12 \) \* 1000 + 0` then gcc_cv_as_alpha_explicit_relocs=yes fi elif test x$gcc_cv_as != x; then echo ' .set nomacro .text extbl $3, $2, $3 !lituse_bytoff!1 ldq $2, a($29) !literal!1 ldq $4, b($29) !literal!2 ldq_u $3, 0($2) !lituse_base!1 ldq $27, f($29) !literal!5 jsr $26, ($27), f !lituse_jsr!5 ldah $29, 0($26) !gpdisp!3 lda $0, c($29) !gprel ldah $1, d($29) !gprelhigh lda $1, d($1) !gprellow lda $29, 0($29) !gpdisp!3' > conftest.s if { ac_try='$gcc_cv_as -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then gcc_cv_as_alpha_explicit_relocs=yes else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_alpha_explicit_relocs" >&5 echo "${ECHO_T}$gcc_cv_as_alpha_explicit_relocs" >&6 if test $gcc_cv_as_alpha_explicit_relocs = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_AS_EXPLICIT_RELOCS 1 _ACEOF +fi + echo "$as_me:$LINENO: checking assembler for jsrdirect relocation support" >&5 +echo $ECHO_N "checking assembler for jsrdirect relocation support... $ECHO_C" >&6 +if test "${gcc_cv_as_alpha_jsrdirect_relocs+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + gcc_cv_as_alpha_jsrdirect_relocs=no + if test $in_tree_gas = yes; then + if test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 16 \) \* 1000 + 90` + then gcc_cv_as_alpha_jsrdirect_relocs=yes +fi + elif test x$gcc_cv_as != x; then + echo ' .set nomacro + .text + ldq $27, a($29) !literal!1 + jsr $26, ($27), a !lituse_jsrdirect!1' > conftest.s + if { ac_try='$gcc_cv_as -o conftest.o conftest.s >&5' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } + then + gcc_cv_as_alpha_jsrdirect_relocs=yes + else + echo "configure: failed program was" >&5 + cat conftest.s >&5 + fi + rm -f conftest.o conftest.s + fi +fi +echo "$as_me:$LINENO: result: $gcc_cv_as_alpha_jsrdirect_relocs" >&5 +echo "${ECHO_T}$gcc_cv_as_alpha_jsrdirect_relocs" >&6 +if test $gcc_cv_as_alpha_jsrdirect_relocs = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_AS_JSRDIRECT_RELOCS 1 +_ACEOF + fi ;; cris-*-*) echo "$as_me:$LINENO: checking assembler for -no-mul-bug-abort option" >&5 echo $ECHO_N "checking assembler for -no-mul-bug-abort option... $ECHO_C" >&6 if test "${gcc_cv_as_cris_no_mul_bug+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_cris_no_mul_bug=no if test $in_tree_gas = yes; then if test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 15 \) \* 1000 + 91` then gcc_cv_as_cris_no_mul_bug=yes fi elif test x$gcc_cv_as != x; then echo '.text' > conftest.s if { ac_try='$gcc_cv_as -no-mul-bug-abort -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then gcc_cv_as_cris_no_mul_bug=yes else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_cris_no_mul_bug" >&5 echo "${ECHO_T}$gcc_cv_as_cris_no_mul_bug" >&6 if test $gcc_cv_as_cris_no_mul_bug = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_AS_NO_MUL_BUG_ABORT_OPTION 1 _ACEOF fi ;; sparc*-*-*) echo "$as_me:$LINENO: checking assembler for .register" >&5 echo $ECHO_N "checking assembler for .register... $ECHO_C" >&6 if test "${gcc_cv_as_sparc_register_op+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_sparc_register_op=no if test x$gcc_cv_as != x; then echo '.register %g2, #scratch' > conftest.s if { ac_try='$gcc_cv_as -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then gcc_cv_as_sparc_register_op=yes else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_sparc_register_op" >&5 echo "${ECHO_T}$gcc_cv_as_sparc_register_op" >&6 if test $gcc_cv_as_sparc_register_op = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_AS_REGISTER_PSEUDO_OP 1 _ACEOF fi echo "$as_me:$LINENO: checking assembler for -relax option" >&5 echo $ECHO_N "checking assembler for -relax option... $ECHO_C" >&6 if test "${gcc_cv_as_sparc_relax+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_sparc_relax=no if test x$gcc_cv_as != x; then echo '.text' > conftest.s if { ac_try='$gcc_cv_as -relax -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then gcc_cv_as_sparc_relax=yes else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_sparc_relax" >&5 echo "${ECHO_T}$gcc_cv_as_sparc_relax" >&6 if test $gcc_cv_as_sparc_relax = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_AS_RELAX_OPTION 1 _ACEOF fi echo "$as_me:$LINENO: checking assembler for unaligned pcrel relocs" >&5 echo $ECHO_N "checking assembler for unaligned pcrel relocs... $ECHO_C" >&6 if test "${gcc_cv_as_sparc_ua_pcrel+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_sparc_ua_pcrel=no if test x$gcc_cv_as != x; then echo '.text foo: nop .data .align 4 .byte 0 .uaword %r_disp32(foo)' > conftest.s if { ac_try='$gcc_cv_as -K PIC -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then if test x$gcc_cv_ld != x \ && $gcc_cv_ld -o conftest conftest.o -G > /dev/null 2>&1; then gcc_cv_as_sparc_ua_pcrel=yes fi rm -f conftest else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_sparc_ua_pcrel" >&5 echo "${ECHO_T}$gcc_cv_as_sparc_ua_pcrel" >&6 if test $gcc_cv_as_sparc_ua_pcrel = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_AS_SPARC_UA_PCREL 1 _ACEOF echo "$as_me:$LINENO: checking assembler for unaligned pcrel relocs against hidden symbols" >&5 echo $ECHO_N "checking assembler for unaligned pcrel relocs against hidden symbols... $ECHO_C" >&6 if test "${gcc_cv_as_sparc_ua_pcrel_hidden+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_sparc_ua_pcrel_hidden=no if test x$gcc_cv_as != x; then echo '.data .align 4 .byte 0x31 .uaword %r_disp32(foo) .byte 0x32, 0x33, 0x34 .global foo .hidden foo foo: .skip 4' > conftest.s if { ac_try='$gcc_cv_as -K PIC -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then if test x$gcc_cv_ld != x && test x$gcc_cv_objdump != x \ && $gcc_cv_ld -o conftest conftest.o -G > /dev/null 2>&1 \ && $gcc_cv_objdump -s -j .data conftest 2> /dev/null \ | grep ' 31000000 07323334' > /dev/null 2>&1; then if $gcc_cv_objdump -R conftest 2> /dev/null \ | grep 'DISP32' > /dev/null 2>&1; then : else gcc_cv_as_sparc_ua_pcrel_hidden=yes fi fi rm -f conftest else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_sparc_ua_pcrel_hidden" >&5 echo "${ECHO_T}$gcc_cv_as_sparc_ua_pcrel_hidden" >&6 if test $gcc_cv_as_sparc_ua_pcrel_hidden = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_AS_SPARC_UA_PCREL_HIDDEN 1 _ACEOF fi fi # unaligned pcrel relocs echo "$as_me:$LINENO: checking assembler for offsetable %lo()" >&5 echo $ECHO_N "checking assembler for offsetable %lo()... $ECHO_C" >&6 if test "${gcc_cv_as_sparc_offsetable_lo10+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_sparc_offsetable_lo10=no if test x$gcc_cv_as != x; then echo '.text or %g1, %lo(ab) + 12, %g1 or %g1, %lo(ab + 12), %g1' > conftest.s if { ac_try='$gcc_cv_as -xarch=v9 -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then if test x$gcc_cv_objdump != x \ && $gcc_cv_objdump -s -j .text conftest.o 2> /dev/null \ | grep ' 82106000 82106000' > /dev/null 2>&1; then gcc_cv_as_sparc_offsetable_lo10=yes fi else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_sparc_offsetable_lo10" >&5 echo "${ECHO_T}$gcc_cv_as_sparc_offsetable_lo10" >&6 if test $gcc_cv_as_sparc_offsetable_lo10 = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_AS_OFFSETABLE_LO10 1 _ACEOF fi ;; i[34567]86-*-* | x86_64-*-*) echo "$as_me:$LINENO: checking assembler for filds and fists mnemonics" >&5 echo $ECHO_N "checking assembler for filds and fists mnemonics... $ECHO_C" >&6 if test "${gcc_cv_as_ix86_filds_fists+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_ix86_filds_fists=no if test $in_tree_gas = yes; then if test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 9 \) \* 1000 + 0` then gcc_cv_as_ix86_filds_fists=yes fi elif test x$gcc_cv_as != x; then echo 'filds mem; fists mem' > conftest.s if { ac_try='$gcc_cv_as -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then gcc_cv_as_ix86_filds_fists=yes else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_ix86_filds_fists" >&5 echo "${ECHO_T}$gcc_cv_as_ix86_filds_fists" >&6 if test $gcc_cv_as_ix86_filds_fists = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_GAS_FILDS_FISTS 1 _ACEOF fi echo "$as_me:$LINENO: checking assembler for cmov syntax" >&5 echo $ECHO_N "checking assembler for cmov syntax... $ECHO_C" >&6 if test "${gcc_cv_as_ix86_cmov_sun_syntax+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_ix86_cmov_sun_syntax=no if test x$gcc_cv_as != x; then echo 'cmovl.l %edx, %eax' > conftest.s if { ac_try='$gcc_cv_as -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then gcc_cv_as_ix86_cmov_sun_syntax=yes else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_ix86_cmov_sun_syntax" >&5 echo "${ECHO_T}$gcc_cv_as_ix86_cmov_sun_syntax" >&6 if test $gcc_cv_as_ix86_cmov_sun_syntax = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_AS_IX86_CMOV_SUN_SYNTAX 1 _ACEOF fi # This one is used unconditionally by i386.[ch]; it is to be defined # to 1 if the feature is present, 0 otherwise. echo "$as_me:$LINENO: checking assembler for GOTOFF in data" >&5 echo $ECHO_N "checking assembler for GOTOFF in data... $ECHO_C" >&6 if test "${gcc_cv_as_ix86_gotoff_in_data+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_ix86_gotoff_in_data=no if test $in_tree_gas = yes; then if test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 11 \) \* 1000 + 0` then gcc_cv_as_ix86_gotoff_in_data=yes fi elif test x$gcc_cv_as != x; then echo ' .text .L0: nop .data .long .L0@GOTOFF' > conftest.s if { ac_try='$gcc_cv_as -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then gcc_cv_as_ix86_gotoff_in_data=yes else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_ix86_gotoff_in_data" >&5 echo "${ECHO_T}$gcc_cv_as_ix86_gotoff_in_data" >&6 cat >>confdefs.h <<_ACEOF #define HAVE_AS_GOTOFF_IN_DATA `if test $gcc_cv_as_ix86_gotoff_in_data = yes; then echo 1; else echo 0; fi` _ACEOF ;; ia64*-*-*) echo "$as_me:$LINENO: checking assembler for ltoffx and ldxmov relocs" >&5 echo $ECHO_N "checking assembler for ltoffx and ldxmov relocs... $ECHO_C" >&6 if test "${gcc_cv_as_ia64_ltoffx_ldxmov_relocs+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_ia64_ltoffx_ldxmov_relocs=no if test $in_tree_gas = yes; then if test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 14 \) \* 1000 + 0` then gcc_cv_as_ia64_ltoffx_ldxmov_relocs=yes fi elif test x$gcc_cv_as != x; then echo ' .text addl r15 = @ltoffx(x#), gp ;; ld8.mov r16 = [r15], x#' > conftest.s if { ac_try='$gcc_cv_as -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then gcc_cv_as_ia64_ltoffx_ldxmov_relocs=yes else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_ia64_ltoffx_ldxmov_relocs" >&5 echo "${ECHO_T}$gcc_cv_as_ia64_ltoffx_ldxmov_relocs" >&6 if test $gcc_cv_as_ia64_ltoffx_ldxmov_relocs = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_AS_LTOFFX_LDXMOV_RELOCS 1 _ACEOF fi ;; powerpc*-*-*) case $target in *-*-aix*) conftest_s=' .csect .text[PR] mfcr 3,128';; *-*-darwin*) conftest_s=' .text mfcr r3,128';; *) conftest_s=' .text mfcr 3,128';; esac echo "$as_me:$LINENO: checking assembler for mfcr field support" >&5 echo $ECHO_N "checking assembler for mfcr field support... $ECHO_C" >&6 if test "${gcc_cv_as_powerpc_mfcrf+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_powerpc_mfcrf=no if test $in_tree_gas = yes; then if test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 14 \) \* 1000 + 0` then gcc_cv_as_powerpc_mfcrf=yes fi elif test x$gcc_cv_as != x; then echo "$conftest_s" > conftest.s if { ac_try='$gcc_cv_as -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then gcc_cv_as_powerpc_mfcrf=yes else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_powerpc_mfcrf" >&5 echo "${ECHO_T}$gcc_cv_as_powerpc_mfcrf" >&6 if test $gcc_cv_as_powerpc_mfcrf = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_AS_MFCRF 1 _ACEOF fi ;; mips*-*-*) echo "$as_me:$LINENO: checking assembler for explicit relocation support" >&5 echo $ECHO_N "checking assembler for explicit relocation support... $ECHO_C" >&6 if test "${gcc_cv_as_mips_explicit_relocs+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_mips_explicit_relocs=no if test $in_tree_gas = yes; then if test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 14 \) \* 1000 + 0` then gcc_cv_as_mips_explicit_relocs=yes fi elif test x$gcc_cv_as != x; then echo ' lw $4,%gp_rel(foo)($4)' > conftest.s if { ac_try='$gcc_cv_as -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then gcc_cv_as_mips_explicit_relocs=yes else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_mips_explicit_relocs" >&5 echo "${ECHO_T}$gcc_cv_as_mips_explicit_relocs" >&6 if test $gcc_cv_as_mips_explicit_relocs = yes; then if test x$target_cpu_default = x then target_cpu_default=MASK_EXPLICIT_RELOCS else target_cpu_default="($target_cpu_default)|MASK_EXPLICIT_RELOCS" fi fi ;; esac # ??? Not all targets support dwarf2 debug_line, even within a version # of gas. Moreover, we need to emit a valid instruction to trigger any # info to the output file. So, as supported targets are added to gas 2.11, # add some instruction here to (also) show we expect this might work. # ??? Once 2.11 is released, probably need to add first known working # version to the per-target configury. case "$target" in i?86*-*-* | mips*-*-* | alpha*-*-* | powerpc*-*-* | sparc*-*-* | m68*-*-* \ | x86_64*-*-* | hppa*-*-* | arm*-*-* | strongarm*-*-* | xscale*-*-* \ | xstormy16*-*-* | cris-*-* | xtensa-*-*) insn="nop" ;; ia64*-*-* | s390*-*-*) insn="nop 0" ;; mmix-*-*) insn="swym 0" ;; esac if test x"$insn" != x; then conftest_s="\ .file 1 \"conftest.s\" .loc 1 3 0 $insn" echo "$as_me:$LINENO: checking assembler for dwarf2 debug_line support" >&5 echo $ECHO_N "checking assembler for dwarf2 debug_line support... $ECHO_C" >&6 if test "${gcc_cv_as_dwarf2_debug_line+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_dwarf2_debug_line=no if test $in_tree_gas = yes; then if test $in_tree_gas_is_elf = yes \ && test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 11 \) \* 1000 + 0` then gcc_cv_as_dwarf2_debug_line=yes fi elif test x$gcc_cv_as != x; then echo "$conftest_s" > conftest.s if { ac_try='$gcc_cv_as -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then # ??? This fails with non-gnu grep. Maybe use objdump? if grep debug_line conftest.o > /dev/null 2>&1; then gcc_cv_as_dwarf2_debug_line=yes fi else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_dwarf2_debug_line" >&5 echo "${ECHO_T}$gcc_cv_as_dwarf2_debug_line" >&6 # The .debug_line file table must be in the exact order that # we specified the files, since these indices are also used # by DW_AT_decl_file. Approximate this test by testing if # the assembler bitches if the same index is assigned twice. echo "$as_me:$LINENO: checking assembler for buggy dwarf2 .file directive" >&5 echo $ECHO_N "checking assembler for buggy dwarf2 .file directive... $ECHO_C" >&6 if test "${gcc_cv_as_dwarf2_file_buggy+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_dwarf2_file_buggy=no if test x$gcc_cv_as != x; then echo ' .file 1 "foo.s" .file 1 "bar.s"' > conftest.s if { ac_try='$gcc_cv_as -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then gcc_cv_as_dwarf2_file_buggy=yes else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_dwarf2_file_buggy" >&5 echo "${ECHO_T}$gcc_cv_as_dwarf2_file_buggy" >&6 if test $gcc_cv_as_dwarf2_debug_line = yes \ && test $gcc_cv_as_dwarf2_file_buggy = no; then cat >>confdefs.h <<\_ACEOF #define HAVE_AS_DWARF2_DEBUG_LINE 1 _ACEOF fi echo "$as_me:$LINENO: checking assembler for --gdwarf2 option" >&5 echo $ECHO_N "checking assembler for --gdwarf2 option... $ECHO_C" >&6 if test "${gcc_cv_as_gdwarf2_flag+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_gdwarf2_flag=no if test $in_tree_gas = yes; then if test $in_tree_gas_is_elf = yes \ && test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 11 \) \* 1000 + 0` then gcc_cv_as_gdwarf2_flag=yes fi elif test x$gcc_cv_as != x; then echo "$insn" > conftest.s if { ac_try='$gcc_cv_as --gdwarf2 -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then gcc_cv_as_gdwarf2_flag=yes else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_gdwarf2_flag" >&5 echo "${ECHO_T}$gcc_cv_as_gdwarf2_flag" >&6 if test $gcc_cv_as_gdwarf2_flag = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_AS_GDWARF2_DEBUG_FLAG 1 _ACEOF fi echo "$as_me:$LINENO: checking assembler for --gstabs option" >&5 echo $ECHO_N "checking assembler for --gstabs option... $ECHO_C" >&6 if test "${gcc_cv_as_gstabs_flag+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_as_gstabs_flag=no if test $in_tree_gas = yes; then if test $in_tree_gas_is_elf = yes \ && test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 11 \) \* 1000 + 0` then gcc_cv_as_gstabs_flag=yes fi elif test x$gcc_cv_as != x; then echo "$insn" > conftest.s if { ac_try='$gcc_cv_as --gstabs -o conftest.o conftest.s >&5' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then # The native Solaris 9/Intel assembler doesn't understand --gstabs # and warns about it, but still exits successfully. So check for # this. if { ac_try='$gcc_cv_as --gstabs -o conftest.o conftest.s 2>&1 | grep -i warning > /dev/null' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } then : else gcc_cv_as_gstabs_flag=yes fi else echo "configure: failed program was" >&5 cat conftest.s >&5 fi rm -f conftest.o conftest.s fi fi echo "$as_me:$LINENO: result: $gcc_cv_as_gstabs_flag" >&5 echo "${ECHO_T}$gcc_cv_as_gstabs_flag" >&6 if test $gcc_cv_as_gstabs_flag = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_AS_GSTABS_DEBUG_FLAG 1 _ACEOF fi fi echo "$as_me:$LINENO: checking linker read-only and read-write section mixing" >&5 echo $ECHO_N "checking linker read-only and read-write section mixing... $ECHO_C" >&6 gcc_cv_ld_ro_rw_mix=unknown if test $in_tree_ld = yes ; then if test "$gcc_cv_gld_major_version" -eq 2 -a "$gcc_cv_gld_minor_version" -ge 10 -o "$gcc_cv_gld_major_version" -gt 2 \ && test $in_tree_ld_is_elf = yes; then gcc_cv_ld_ro_rw_mix=read-write fi elif test x$gcc_cv_as != x -a x$gcc_cv_ld != x -a x$gcc_cv_objdump != x ; then echo '.section myfoosect, "a"' > conftest1.s echo '.section myfoosect, "aw"' > conftest2.s echo '.byte 1' >> conftest2.s echo '.section myfoosect, "a"' > conftest3.s echo '.byte 0' >> conftest3.s if $gcc_cv_as -o conftest1.o conftest1.s > /dev/null 2>&1 \ && $gcc_cv_as -o conftest2.o conftest2.s > /dev/null 2>&1 \ && $gcc_cv_as -o conftest3.o conftest3.s > /dev/null 2>&1 \ && $gcc_cv_ld -shared -o conftest1.so conftest1.o \ conftest2.o conftest3.o > /dev/null 2>&1; then gcc_cv_ld_ro_rw_mix=`$gcc_cv_objdump -h conftest1.so \ | sed -e '/myfoosect/!d' -e N` if echo "$gcc_cv_ld_ro_rw_mix" | grep CONTENTS > /dev/null; then if echo "$gcc_cv_ld_ro_rw_mix" | grep READONLY > /dev/null; then gcc_cv_ld_ro_rw_mix=read-only else gcc_cv_ld_ro_rw_mix=read-write fi fi fi rm -f conftest.* conftest[123].* fi if test x$gcc_cv_ld_ro_rw_mix = xread-write; then cat >>confdefs.h <<\_ACEOF #define HAVE_LD_RO_RW_SECTION_MIXING 1 _ACEOF fi echo "$as_me:$LINENO: result: $gcc_cv_ld_ro_rw_mix" >&5 echo "${ECHO_T}$gcc_cv_ld_ro_rw_mix" >&6 echo "$as_me:$LINENO: checking linker PT_GNU_EH_FRAME support" >&5 echo $ECHO_N "checking linker PT_GNU_EH_FRAME support... $ECHO_C" >&6 gcc_cv_ld_eh_frame_hdr=no if test $in_tree_ld = yes ; then if test "$gcc_cv_gld_major_version" -eq 2 -a "$gcc_cv_gld_minor_version" -ge 12 -o "$gcc_cv_gld_major_version" -gt 2 \ && test $in_tree_ld_is_elf = yes; then gcc_cv_ld_eh_frame_hdr=yes fi elif test x$gcc_cv_ld != x; then # Check if linker supports --eh-frame-hdr option if $gcc_cv_ld --help 2>/dev/null | grep eh-frame-hdr > /dev/null; then gcc_cv_ld_eh_frame_hdr=yes fi fi if test x"$gcc_cv_ld_eh_frame_hdr" = xyes; then cat >>confdefs.h <<\_ACEOF #define HAVE_LD_EH_FRAME_HDR 1 _ACEOF fi echo "$as_me:$LINENO: result: $gcc_cv_ld_eh_frame_hdr" >&5 echo "${ECHO_T}$gcc_cv_ld_eh_frame_hdr" >&6 echo "$as_me:$LINENO: checking linker position independent executable support" >&5 echo $ECHO_N "checking linker position independent executable support... $ECHO_C" >&6 gcc_cv_ld_pie=no if test $in_tree_ld = yes ; then if test "$gcc_cv_gld_major_version" -eq 2 -a "$gcc_cv_gld_minor_version" -ge 15 -o "$gcc_cv_gld_major_version" -gt 2 \ && test $in_tree_ld_is_elf = yes; then gcc_cv_ld_pie=yes fi elif test x$gcc_cv_ld != x; then # Check if linker supports -pie option if $gcc_cv_ld --help 2>/dev/null | grep -- -pie > /dev/null; then gcc_cv_ld_pie=yes fi fi if test x"$gcc_cv_ld_pie" = xyes; then cat >>confdefs.h <<\_ACEOF #define HAVE_LD_PIE 1 _ACEOF fi echo "$as_me:$LINENO: result: $gcc_cv_ld_pie" >&5 echo "${ECHO_T}$gcc_cv_ld_pie" >&6 echo "$as_me:$LINENO: checking linker -Bstatic/-Bdynamic option" >&5 echo $ECHO_N "checking linker -Bstatic/-Bdynamic option... $ECHO_C" >&6 gcc_cv_ld_static_dynamic=no if test $in_tree_ld = yes ; then if test "$gcc_cv_gld_major_version" -eq 2 -a "$gcc_cv_gld_minor_version" -ge 10; then gcc_cv_ld_static_dynamic=yes fi elif test x$gcc_cv_ld != x; then # Check if linker supports -Bstatic/-Bdynamic option if $gcc_cv_ld --help 2>/dev/null | grep -- -Bstatic > /dev/null \ && $gcc_cv_ld --help 2>/dev/null | grep -- -Bdynamic > /dev/null; then gcc_cv_ld_static_dynamic=yes fi fi if test x"$gcc_cv_ld_static_dynamic" = xyes; then cat >>confdefs.h <<\_ACEOF #define HAVE_LD_STATIC_DYNAMIC 1 _ACEOF fi echo "$as_me:$LINENO: result: $gcc_cv_ld_static_dynamic" >&5 echo "${ECHO_T}$gcc_cv_ld_static_dynamic" >&6 case "$target" in *-*-linux*) echo "$as_me:$LINENO: checking linker --as-needed support" >&5 echo $ECHO_N "checking linker --as-needed support... $ECHO_C" >&6 if test "${gcc_cv_ld_as_needed+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else gcc_cv_ld_as_needed=no if test $in_tree_ld = yes ; then if test "$gcc_cv_gld_major_version" -eq 2 -a "$gcc_cv_gld_minor_version" -ge 16 -o "$gcc_cv_gld_major_version" -gt 2 \ && test $in_tree_ld_is_elf = yes; then gcc_cv_ld_as_needed=yes fi elif test x$gcc_cv_ld != x; then # Check if linker supports --as-needed and --no-as-needed options if $gcc_cv_ld --help 2>/dev/null | grep as-needed > /dev/null; then gcc_cv_ld_as_needed=yes fi fi fi echo "$as_me:$LINENO: result: $gcc_cv_ld_as_needed" >&5 echo "${ECHO_T}$gcc_cv_ld_as_needed" >&6 if test x"$gcc_cv_ld_as_needed" = xyes; then cat >>confdefs.h <<\_ACEOF #define HAVE_LD_AS_NEEDED 1 _ACEOF fi ;; esac if test x$with_sysroot = x && test x$host = x$target \ && test "$prefix" != "/usr" && test "x$prefix" != "x$local_prefix" ; then cat >>confdefs.h <<_ACEOF #define PREFIX_INCLUDE_DIR "$prefix/include" _ACEOF fi # Figure out what language subdirectories are present. # Look if the user specified --enable-languages="..."; if not, use # the environment variable $LANGUAGES if defined. $LANGUAGES might # go away some day. # NB: embedded tabs in this IF block -- do not untabify if test x"${enable_languages+set}" != xset; then if test x"${LANGUAGES+set}" = xset; then enable_languages="${LANGUAGES}" { echo "$as_me:$LINENO: WARNING: setting LANGUAGES is deprecated, use --enable-languages instead" >&5 echo "$as_me: WARNING: setting LANGUAGES is deprecated, use --enable-languages instead" >&2;} else enable_languages=all fi else if test x"${enable_languages}" = x \ || test x"${enable_languages}" = xyes; then { { echo "$as_me:$LINENO: error: --enable-languages needs at least one language argument" >&5 echo "$as_me: error: --enable-languages needs at least one language argument" >&2;} { (exit 1); exit 1; }; } fi fi enable_languages=`echo "${enable_languages}" | sed -e 's/[ ,][ ,]*/,/g' -e 's/,$//'` # First scan to see if an enabled language requires some other language. # We assume that a given config-lang.in will list all the language # front ends it requires, even if some are required indirectly. for lang in ${srcdir}/*/config-lang.in do case $lang in # The odd quoting in the next line works around # an apparent bug in bash 1.12 on linux. ${srcdir}/[*]/config-lang.in) ;; *) lang_alias=`sed -n -e 's,^language=['"'"'"'"]\(.*\)["'"'"'"'].*$,\1,p' -e 's,^language=\([^ ]*\).*$,\1,p' $lang` this_lang_requires=`sed -n -e 's,^lang_requires=['"'"'"'"]\(.*\)["'"'"'"'].*$,\1,p' -e 's,^lang_requires=\([^ ]*\).*$,\1,p' $lang` for other in $this_lang_requires do case ,${enable_languages}, in *,$other,*) ;; *,all,*) ;; *,$lang_alias,*) enable_languages="$enable_languages,$other" ;; esac done ;; esac done expected_languages=`echo ,${enable_languages}, | sed -e 's:,: :g' -e 's: *: :g' -e 's: *: :g' -e 's:^ ::' -e 's: $::'` found_languages= subdirs= for lang in ${srcdir}/*/config-lang.in do case $lang in # The odd quoting in the next line works around # an apparent bug in bash 1.12 on linux. ${srcdir}/[*]/config-lang.in) ;; *) lang_alias=`sed -n -e 's,^language=['"'"'"'"]\(.*\)["'"'"'"'].*$,\1,p' -e 's,^language=\([^ ]*\).*$,\1,p' $lang` this_lang_libs=`sed -n -e 's,^target_libs=['"'"'"'"]\(.*\)["'"'"'"'].*$,\1,p' -e 's,^target_libs=\([^ ]*\).*$,\1,p' $lang` build_by_default=`sed -n -e 's,^build_by_default=['"'"'"'"]\(.*\)["'"'"'"'].*$,\1,p' -e 's,^build_by_default=\([^ ]*\).*$,\1,p' $lang` if test "x$lang_alias" = x then echo "$lang doesn't set \$language." 1>&2 exit 1 fi case ${build_by_default},${enable_languages}, in *,$lang_alias,*) add_this_lang=yes ;; no,*) add_this_lang=no ;; *,all,*) add_this_lang=yes ;; *) add_this_lang=no ;; esac found_languages="${found_languages} ${lang_alias}" if test x"${add_this_lang}" = xyes; then case $lang in ${srcdir}/ada/config-lang.in) if test x$have_gnat = xyes ; then subdirs="$subdirs `echo $lang | sed -e 's,^.*/\([^/]*\)/config-lang.in$,\1,'`" fi ;; *) subdirs="$subdirs `echo $lang | sed -e 's,^.*/\([^/]*\)/config-lang.in$,\1,'`" ;; esac fi ;; esac done missing_languages= for expected_language in ${expected_languages} .. do if test "${expected_language}" != ..; then missing_language="${expected_language}" if test "${expected_language}" = "c" \ || test "${expected_language}" = "all"; then missing_language= fi for found_language in ${found_languages} .. do if test "${found_language}" != ..; then if test "${expected_language}" = "${found_language}"; then missing_language= fi fi done if test "x${missing_language}" != x; then missing_languages="${missing_languages} ${missing_language}" fi fi done if test "x$missing_languages" != x; then { { echo "$as_me:$LINENO: error: The following requested languages were not found:${missing_languages} The following languages were available: c${found_languages}" >&5 echo "$as_me: error: The following requested languages were not found:${missing_languages} The following languages were available: c${found_languages}" >&2;} { (exit 1); exit 1; }; } fi # Make gthr-default.h if we have a thread file. gthread_flags= if test $thread_file != single; then rm -f gthr-default.h echo "#include \"gthr-${thread_file}.h\"" > gthr-default.h gthread_flags=-DHAVE_GTHR_DEFAULT fi # Find out what GC implementation we want, or may, use. # Check whether --with-gc or --without-gc was given. if test "${with_gc+set}" = set; then withval="$with_gc" case "$withval" in simple | page | zone) GGC=ggc-$withval ;; *) { { echo "$as_me:$LINENO: error: $withval is an invalid option to --with-gc" >&5 echo "$as_me: error: $withval is an invalid option to --with-gc" >&2;} { (exit 1); exit 1; }; } ;; esac else GGC=ggc-page fi; echo "Using $GGC for garbage collection." # Use the system's zlib library. zlibdir=-L../zlib zlibinc="-I\$(srcdir)/../zlib" # Check whether --with-system-zlib or --without-system-zlib was given. if test "${with_system_zlib+set}" = set; then withval="$with_system_zlib" zlibdir= zlibinc= fi; echo "$as_me:$LINENO: checking whether to enable maintainer-specific portions of Makefiles" >&5 echo $ECHO_N "checking whether to enable maintainer-specific portions of Makefiles... $ECHO_C" >&6 # Check whether --enable-maintainer-mode or --disable-maintainer-mode was given. if test "${enable_maintainer_mode+set}" = set; then enableval="$enable_maintainer_mode" maintainer_mode=$enableval else maintainer_mode=no fi; echo "$as_me:$LINENO: result: $maintainer_mode" >&5 echo "${ECHO_T}$maintainer_mode" >&6 if test "$maintainer_mode" = "yes"; then MAINT='' else MAINT='#' fi # Make empty files to contain the specs and options for each language. # Then add #include lines to for a compiler that has specs and/or options. lang_opt_files= lang_specs_files= lang_tree_files= for subdir in . $subdirs do if test -f $srcdir/$subdir/lang.opt; then lang_opt_files="$lang_opt_files $srcdir/$subdir/lang.opt" fi if test -f $srcdir/$subdir/lang-specs.h; then lang_specs_files="$lang_specs_files $srcdir/$subdir/lang-specs.h" fi if test -f $srcdir/$subdir/$subdir-tree.def; then lang_tree_files="$lang_tree_files $srcdir/$subdir/$subdir-tree.def" fi done # These (without "all_") are set in each config-lang.in. # `language' must be a single word so is spelled singularly. all_languages= all_boot_languages= all_compilers= all_stagestuff= all_outputs='Makefile fixinc/Makefile gccbug mklibgcc mkheaders' # List of language makefile fragments. all_lang_makefrags= # List of language subdirectory makefiles. Deprecated. all_lang_makefiles= # Files for gengtype all_gtfiles="$target_gtfiles" # Files for gengtype with language all_gtfiles_files_langs= all_gtfiles_files_files= # Add the language fragments. # Languages are added via two mechanisms. Some information must be # recorded in makefile variables, these are defined in config-lang.in. # We accumulate them and plug them into the main Makefile. # The other mechanism is a set of hooks for each of the main targets # like `clean', `install', etc. language_hooks="Make-hooks" for s in $subdirs do language= boot_language= compilers= stagestuff= outputs= gtfiles= . ${srcdir}/$s/config-lang.in if test "x$language" = x then echo "${srcdir}/$s/config-lang.in doesn't set \$language." 1>&2 exit 1 fi all_lang_makefrags="$all_lang_makefrags \$(srcdir)/$s/Make-lang.in" if test -f ${srcdir}/$s/Makefile.in then all_lang_makefiles="$s/Makefile" fi all_languages="$all_languages $language" if test "x$boot_language" = xyes then all_boot_languages="$all_boot_languages $language" fi all_compilers="$all_compilers $compilers" all_stagestuff="$all_stagestuff $stagestuff" all_outputs="$all_outputs $outputs" all_gtfiles="$all_gtfiles $gtfiles" for f in $gtfiles do all_gtfiles_files_langs="$all_gtfiles_files_langs ${s} " all_gtfiles_files_files="$all_gtfiles_files_files ${f} " done done # Pick up gtfiles for c gtfiles= s="c" . ${srcdir}/c-config-lang.in all_gtfiles="$all_gtfiles $gtfiles" for f in $gtfiles do all_gtfiles_files_langs="$all_gtfiles_files_langs ${s} " all_gtfiles_files_files="$all_gtfiles_files_files ${f} " done check_languages= for language in $all_languages do check_languages="$check_languages check-$language" done # We link each language in with a set of hooks, reached indirectly via # lang.${target}. rm -f Make-hooks touch Make-hooks target_list="all.build all.cross start.encap rest.encap tags \ install-normal install-common install-man \ uninstall info man srcextra srcman srcinfo \ mostlyclean clean distclean maintainer-clean \ stage1 stage2 stage3 stage4 stageprofile stagefeedback" for t in $target_list do x= for lang in $all_languages do x="$x $lang.$t" done echo "lang.$t: $x" >> Make-hooks done # Create .gdbinit. echo "dir ." > .gdbinit echo "dir ${srcdir}" >> .gdbinit if test x$gdb_needs_out_file_path = xyes then echo "dir ${srcdir}/config/"`dirname ${out_file}` >> .gdbinit fi if test "x$subdirs" != x; then for s in $subdirs do echo "dir ${srcdir}/$s" >> .gdbinit done fi echo "source ${srcdir}/gdbinit.in" >> .gdbinit # If $(exec_prefix) exists and is not the same as $(prefix), then compute an # absolute path for gcc_tooldir based on inserting the number of up-directory # movements required to get from $(exec_prefix) to $(prefix) into the basic # $(libsubdir)/@(unlibsubdir) based path. # Don't set gcc_tooldir to tooldir since that's only passed in by the toplevel # make and thus we'd get different behavior depending on where we built the # sources. if test x$exec_prefix = xNONE -o x$exec_prefix = x$prefix; then gcc_tooldir='$(libsubdir)/$(unlibsubdir)/../$(target_noncanonical)' else # An explanation of the sed strings: # -e 's|^\$(prefix)||' matches and eliminates 'prefix' from 'exec_prefix' # -e 's|/$||' match a trailing forward slash and eliminates it # -e 's|^[^/]|/|' forces the string to start with a forward slash (*) # -e 's|/[^/]*|../|g' replaces each occurrence of / with ../ # # (*) Note this pattern overwrites the first character of the string # with a forward slash if one is not already present. This is not a # problem because the exact names of the sub-directories concerned is # unimportant, just the number of them matters. # # The practical upshot of these patterns is like this: # # prefix exec_prefix result # ------ ----------- ------ # /foo /foo/bar ../ # /foo/ /foo/bar ../ # /foo /foo/bar/ ../ # /foo/ /foo/bar/ ../ # /foo /foo/bar/ugg ../../ # dollar='$$' gcc_tooldir="\$(libsubdir)/\$(unlibsubdir)/\`echo \$(exec_prefix) | sed -e 's|^\$(prefix)||' -e 's|/\$(dollar)||' -e 's|^[^/]|/|' -e 's|/[^/]*|../|g'\`\$(target_noncanonical)" fi # Find a directory in which to install a shared libgcc. # Check whether --enable-version-specific-runtime-libs or --disable-version-specific-runtime-libs was given. if test "${enable_version_specific_runtime_libs+set}" = set; then enableval="$enable_version_specific_runtime_libs" fi; # Check whether --with-slibdir or --without-slibdir was given. if test "${with_slibdir+set}" = set; then withval="$with_slibdir" slibdir="$with_slibdir" else if test "${enable_version_specific_runtime_libs+set}" = set; then slibdir='$(libsubdir)' elif test "$host" != "$target"; then slibdir='$(build_tooldir)/lib' else slibdir='$(libdir)' fi fi; objdir=`${PWDCMD-pwd}` # Substitute configuration variables if test x"$SET_GCC_LIB_PATH_CMD" != x; then # SET_GCC_LIB_PATH_CMD is "XXX=path; export XXX;". It is expanded to # # eval "set_gcc_lib_path=XXX=path; export XXX;" # eval "set_gcc_lib_path=$SET_GCC_LIB_PATH_CMD" # It will set set_gcc_lib_path to "export XXX=path" for GNU make. set_gcc_lib_path="export $set_gcc_lib_path" else set_gcc_lib_path= fi # If it doesn't already exist, create document directory echo "checking for the document directory." 1>&2 if test -d doc ; then true else mkdir doc fi # Echo link setup. if test x${build} = x${host} ; then if test x${host} = x${target} ; then echo "Links are now set up to build a native compiler for ${target}." 1>&2 else echo "Links are now set up to build a cross-compiler" 1>&2 echo " from ${host} to ${target}." 1>&2 fi else if test x${host} = x${target} ; then echo "Links are now set up to build (on ${build}) a native compiler" 1>&2 echo " for ${target}." 1>&2 else echo "Links are now set up to build (on ${build}) a cross-compiler" 1>&2 echo " from ${host} to ${target}." 1>&2 fi fi # Configure the subdirectories # AC_CONFIG_SUBDIRS($subdirs) # Create the Makefile # and configure language subdirectories ac_config_files="$ac_config_files $all_outputs" ac_config_commands="$ac_config_commands default" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, don't put newlines in cache variables' values. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. { (set) 2>&1 | case `(ac_space=' '; set | grep ac_space) 2>&1` in *ac_space=\ *) # `set' does not quote correctly, so add quotes (double-quote # substitution turns \\\\ into \\, and sed turns \\ into \). sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n \ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" ;; esac; } | sed ' t clear : clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ : end' >>confcache if diff $cache_file confcache >/dev/null 2>&1; then :; else if test -w $cache_file; then test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" cat confcache >$cache_file else echo "not updating unwritable cache $cache_file" fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # VPATH may cause trouble with some makes, so we remove $(srcdir), # ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=/{ s/:*\$(srcdir):*/:/; s/:*\${srcdir}:*/:/; s/:*@srcdir@:*/:/; s/^\([^=]*=[ ]*\):*/\1/; s/:*$//; s/^[^=]*=[ ]*$//; }' fi DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_i=`echo "$ac_i" | sed 's/\$U\././;s/\.o$//;s/\.obj$//'` # 2. Add them. ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : ${CONFIG_STATUS=./config.status} ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 echo "$as_me: creating $CONFIG_STATUS" >&6;} cat >$CONFIG_STATUS <<_ACEOF #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then set -o posix fi DUALCASE=1; export DUALCASE # for MKS sh # Support unset when possible. if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # Work around bugs in pre-3.0 UWIN ksh. $as_unset ENV MAIL MAILPATH PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)$' \| \ . : '\(.\)' 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } /^X\/\(\/\/\)$/{ s//\1/; q; } /^X\/\(\/\).*/{ s//\1/; q; } s/.*/./; q'` # PATH needs CR, and LINENO needs CR and PATH. # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" || { # Find who we are. Look in the path if we contain no path at all # relative or not. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} { (exit 1); exit 1; }; } fi case $CONFIG_SHELL in '') as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for as_base in sh bash ksh sh5; do case $as_dir in /*) if ("$as_dir/$as_base" -c ' as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } CONFIG_SHELL=$as_dir/$as_base export CONFIG_SHELL exec "$CONFIG_SHELL" "$0" ${1+"$@"} fi;; esac done done ;; esac # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line before each line; the second 'sed' does the real # work. The second script uses 'N' to pair each line-number line # with the numbered line, and appends trailing '-' during # substitution so that $LINENO is not a special case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) sed '=' <$as_myself | sed ' N s,$,-, : loop s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, t loop s,-$,, s,^['$as_cr_digits']*\n,, ' >$as_me.lineno && chmod +x $as_me.lineno || { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensible to this). . ./$as_me.lineno # Exit status is that of the last command. exit } case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in *c*,-n*) ECHO_N= ECHO_C=' ' ECHO_T=' ' ;; *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; *) ECHO_N= ECHO_C='\c' ECHO_T= ;; esac if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then # We could just check for DJGPP; but this test a) works b) is more generic # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). if test -f conf$$.exe; then # Don't use ln at all; we don't have any links as_ln_s='cp -p' else as_ln_s='ln -s' fi elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.file if mkdir -p . 2>/dev/null; then as_mkdir_p=: else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_executable_p="test -f" # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" # IFS # We need space, tab and new line, in precisely that order. as_nl=' ' IFS=" $as_nl" # CDPATH. $as_unset CDPATH exec 6>&1 # Open the log real soon, to keep \$[0] and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. Logging --version etc. is OK. exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX } >&5 cat >&5 <<_CSEOF This file was extended by $as_me, which was generated by GNU Autoconf 2.59. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ _CSEOF echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 echo >&5 _ACEOF # Files that config.status was made for. if test -n "$ac_config_files"; then echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS fi if test -n "$ac_config_headers"; then echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS fi if test -n "$ac_config_links"; then echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS fi if test -n "$ac_config_commands"; then echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS fi cat >>$CONFIG_STATUS <<\_ACEOF ac_cs_usage="\ \`$as_me' instantiates files from templates according to the current configuration. Usage: $0 [OPTIONS] [FILE]... -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Configuration commands: $config_commands Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ config.status configured by $0, generated by GNU Autoconf 2.59, with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" Copyright (C) 2003 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." srcdir=$srcdir _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # If no file are specified by the user, then we need to provide default # value. By we need to know if files were specified by the user. ac_need_defaults=: while test $# != 0 do case $1 in --*=*) ac_option=`expr "x$1" : 'x\([^=]*\)='` ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` ac_shift=: ;; -*) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; *) # This is not an option, so the user has probably given explicit # arguments. ac_option=$1 ac_need_defaults=false;; esac case $ac_option in # Handling of the options. _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --vers* | -V ) echo "$ac_cs_version"; exit 0 ;; --he | --h) # Conflict between --help and --header { { echo "$as_me:$LINENO: error: ambiguous option: $1 Try \`$0 --help' for more information." >&5 echo "$as_me: error: ambiguous option: $1 Try \`$0 --help' for more information." >&2;} { (exit 1); exit 1; }; };; --help | --hel | -h ) echo "$ac_cs_usage"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift CONFIG_FILES="$CONFIG_FILES $ac_optarg" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" ac_need_defaults=false;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 Try \`$0 --help' for more information." >&5 echo "$as_me: error: unrecognized option: $1 Try \`$0 --help' for more information." >&2;} { (exit 1); exit 1; }; } ;; *) ac_config_targets="$ac_config_targets $1" ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF if \$ac_cs_recheck; then echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF # # INIT-COMMANDS section. # subdirs='$subdirs' symbolic_link='$symbolic_link' _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF for ac_config_target in $ac_config_targets do case "$ac_config_target" in # Handling of arguments. "$all_outputs" ) CONFIG_FILES="$CONFIG_FILES $all_outputs" ;; "default" ) CONFIG_COMMANDS="$CONFIG_COMMANDS default" ;; "auto-host.h" ) CONFIG_HEADERS="$CONFIG_HEADERS auto-host.h:config.in" ;; *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 echo "$as_me: error: invalid argument: $ac_config_target" >&2;} { (exit 1); exit 1; }; };; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason to put it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Create a temporary directory, and hook for its removal unless debugging. $debug || { trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 trap '{ (exit 1); exit 1; }' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" } || { tmp=./confstat$$-$RANDOM (umask 077 && mkdir $tmp) } || { echo "$me: cannot create a temporary directory in ." >&2 { (exit 1); exit 1; } } _ACEOF cat >>$CONFIG_STATUS <<_ACEOF # # CONFIG_FILES section. # # No need to generate the scripts if there are no CONFIG_FILES. # This happens for instance when ./config.status config.h if test -n "\$CONFIG_FILES"; then # Protect against being on the right side of a sed subst in config.status. sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF s,@SHELL@,$SHELL,;t t s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t s,@exec_prefix@,$exec_prefix,;t t s,@prefix@,$prefix,;t t s,@program_transform_name@,$program_transform_name,;t t s,@bindir@,$bindir,;t t s,@sbindir@,$sbindir,;t t s,@libexecdir@,$libexecdir,;t t s,@datadir@,$datadir,;t t s,@sysconfdir@,$sysconfdir,;t t s,@sharedstatedir@,$sharedstatedir,;t t s,@localstatedir@,$localstatedir,;t t s,@libdir@,$libdir,;t t s,@includedir@,$includedir,;t t s,@oldincludedir@,$oldincludedir,;t t s,@infodir@,$infodir,;t t s,@mandir@,$mandir,;t t s,@build_alias@,$build_alias,;t t s,@host_alias@,$host_alias,;t t s,@target_alias@,$target_alias,;t t s,@DEFS@,$DEFS,;t t s,@ECHO_C@,$ECHO_C,;t t s,@ECHO_N@,$ECHO_N,;t t s,@ECHO_T@,$ECHO_T,;t t s,@LIBS@,$LIBS,;t t s,@build@,$build,;t t s,@build_cpu@,$build_cpu,;t t s,@build_vendor@,$build_vendor,;t t s,@build_os@,$build_os,;t t s,@host@,$host,;t t s,@host_cpu@,$host_cpu,;t t s,@host_vendor@,$host_vendor,;t t s,@host_os@,$host_os,;t t s,@target@,$target,;t t s,@target_cpu@,$target_cpu,;t t s,@target_vendor@,$target_vendor,;t t s,@target_os@,$target_os,;t t s,@build_subdir@,$build_subdir,;t t s,@host_subdir@,$host_subdir,;t t s,@target_subdir@,$target_subdir,;t t s,@GENINSRC@,$GENINSRC,;t t s,@CC@,$CC,;t t s,@CFLAGS@,$CFLAGS,;t t s,@LDFLAGS@,$LDFLAGS,;t t s,@CPPFLAGS@,$CPPFLAGS,;t t s,@ac_ct_CC@,$ac_ct_CC,;t t s,@EXEEXT@,$EXEEXT,;t t s,@OBJEXT@,$OBJEXT,;t t s,@NO_MINUS_C_MINUS_O@,$NO_MINUS_C_MINUS_O,;t t s,@OUTPUT_OPTION@,$OUTPUT_OPTION,;t t s,@CPP@,$CPP,;t t s,@GNATBIND@,$GNATBIND,;t t s,@ac_ct_GNATBIND@,$ac_ct_GNATBIND,;t t s,@strict1_warn@,$strict1_warn,;t t s,@warn_cflags@,$warn_cflags,;t t s,@WERROR@,$WERROR,;t t s,@nocommon_flag@,$nocommon_flag,;t t s,@EGREP@,$EGREP,;t t s,@valgrind_path@,$valgrind_path,;t t s,@valgrind_path_defines@,$valgrind_path_defines,;t t s,@valgrind_command@,$valgrind_command,;t t s,@coverage_flags@,$coverage_flags,;t t s,@enable_multilib@,$enable_multilib,;t t s,@enable_shared@,$enable_shared,;t t s,@TARGET_SYSTEM_ROOT@,$TARGET_SYSTEM_ROOT,;t t s,@TARGET_SYSTEM_ROOT_DEFINE@,$TARGET_SYSTEM_ROOT_DEFINE,;t t s,@CROSS_SYSTEM_HEADER_DIR@,$CROSS_SYSTEM_HEADER_DIR,;t t s,@onestep@,$onestep,;t t s,@SET_MAKE@,$SET_MAKE,;t t s,@AWK@,$AWK,;t t s,@LN@,$LN,;t t s,@LN_S@,$LN_S,;t t s,@RANLIB@,$RANLIB,;t t s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t s,@INSTALL@,$INSTALL,;t t s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t s,@INSTALL_DATA@,$INSTALL_DATA,;t t s,@make_compare_target@,$make_compare_target,;t t s,@have_mktemp_command@,$have_mktemp_command,;t t s,@MAKEINFO@,$MAKEINFO,;t t s,@BUILD_INFO@,$BUILD_INFO,;t t s,@GENERATED_MANPAGES@,$GENERATED_MANPAGES,;t t s,@FLEX@,$FLEX,;t t s,@BISON@,$BISON,;t t s,@stage1_cflags@,$stage1_cflags,;t t s,@COLLECT2_LIBS@,$COLLECT2_LIBS,;t t s,@GNAT_LIBEXC@,$GNAT_LIBEXC,;t t s,@LDEXP_LIB@,$LDEXP_LIB,;t t s,@TARGET_GETGROUPS_T@,$TARGET_GETGROUPS_T,;t t s,@LIBICONV@,$LIBICONV,;t t s,@LIBICONV_DEP@,$LIBICONV_DEP,;t t s,@manext@,$manext,;t t s,@objext@,$objext,;t t s,@extra_modes_file@,$extra_modes_file,;t t s,@FORBUILD@,$FORBUILD,;t t s,@PACKAGE@,$PACKAGE,;t t s,@VERSION@,$VERSION,;t t s,@USE_NLS@,$USE_NLS,;t t s,@LIBINTL@,$LIBINTL,;t t s,@LIBINTL_DEP@,$LIBINTL_DEP,;t t s,@INCINTL@,$INCINTL,;t t s,@XGETTEXT@,$XGETTEXT,;t t s,@GMSGFMT@,$GMSGFMT,;t t s,@POSUB@,$POSUB,;t t s,@CATALOGS@,$CATALOGS,;t t s,@CROSS@,$CROSS,;t t s,@ALL@,$ALL,;t t s,@SYSTEM_HEADER_DIR@,$SYSTEM_HEADER_DIR,;t t s,@inhibit_libc@,$inhibit_libc,;t t s,@BUILD_PREFIX@,$BUILD_PREFIX,;t t s,@BUILD_PREFIX_1@,$BUILD_PREFIX_1,;t t s,@CC_FOR_BUILD@,$CC_FOR_BUILD,;t t s,@BUILD_CFLAGS@,$BUILD_CFLAGS,;t t s,@STMP_FIXINC@,$STMP_FIXINC,;t t s,@STMP_FIXPROTO@,$STMP_FIXPROTO,;t t s,@libgcc_visibility@,$libgcc_visibility,;t t s,@gthread_flags@,$gthread_flags,;t t s,@GGC@,$GGC,;t t s,@zlibdir@,$zlibdir,;t t s,@zlibinc@,$zlibinc,;t t s,@MAINT@,$MAINT,;t t s,@gcc_tooldir@,$gcc_tooldir,;t t s,@dollar@,$dollar,;t t s,@slibdir@,$slibdir,;t t s,@objdir@,$objdir,;t t s,@subdirs@,$subdirs,;t t s,@srcdir@,$srcdir,;t t s,@all_boot_languages@,$all_boot_languages,;t t s,@all_compilers@,$all_compilers,;t t s,@all_gtfiles@,$all_gtfiles,;t t s,@all_gtfiles_files_langs@,$all_gtfiles_files_langs,;t t s,@all_gtfiles_files_files@,$all_gtfiles_files_files,;t t s,@all_lang_makefrags@,$all_lang_makefrags,;t t s,@all_lang_makefiles@,$all_lang_makefiles,;t t s,@all_languages@,$all_languages,;t t s,@all_stagestuff@,$all_stagestuff,;t t s,@build_exeext@,$build_exeext,;t t s,@build_install_headers_dir@,$build_install_headers_dir,;t t s,@build_xm_file_list@,$build_xm_file_list,;t t s,@build_xm_include_list@,$build_xm_include_list,;t t s,@build_xm_defines@,$build_xm_defines,;t t s,@check_languages@,$check_languages,;t t s,@cc_set_by_configure@,$cc_set_by_configure,;t t s,@quoted_cc_set_by_configure@,$quoted_cc_set_by_configure,;t t s,@cpp_install_dir@,$cpp_install_dir,;t t s,@xmake_file@,$xmake_file,;t t s,@tmake_file@,$tmake_file,;t t s,@extra_gcc_objs@,$extra_gcc_objs,;t t s,@extra_headers_list@,$extra_headers_list,;t t s,@extra_objs@,$extra_objs,;t t s,@extra_parts@,$extra_parts,;t t s,@extra_passes@,$extra_passes,;t t s,@extra_programs@,$extra_programs,;t t s,@float_h_file@,$float_h_file,;t t s,@gcc_config_arguments@,$gcc_config_arguments,;t t s,@gcc_gxx_include_dir@,$gcc_gxx_include_dir,;t t s,@libstdcxx_incdir@,$libstdcxx_incdir,;t t s,@gcc_version@,$gcc_version,;t t s,@gcc_version_full@,$gcc_version_full,;t t s,@gcc_version_trigger@,$gcc_version_trigger,;t t s,@host_exeext@,$host_exeext,;t t s,@host_xm_file_list@,$host_xm_file_list,;t t s,@host_xm_include_list@,$host_xm_include_list,;t t s,@host_xm_defines@,$host_xm_defines,;t t s,@out_host_hook_obj@,$out_host_hook_obj,;t t s,@install@,$install,;t t s,@lang_opt_files@,$lang_opt_files,;t t s,@lang_specs_files@,$lang_specs_files,;t t s,@lang_tree_files@,$lang_tree_files,;t t s,@local_prefix@,$local_prefix,;t t s,@md_file@,$md_file,;t t s,@objc_boehm_gc@,$objc_boehm_gc,;t t s,@out_file@,$out_file,;t t s,@out_object_file@,$out_object_file,;t t s,@stage_prefix_set_by_configure@,$stage_prefix_set_by_configure,;t t s,@quoted_stage_prefix_set_by_configure@,$quoted_stage_prefix_set_by_configure,;t t s,@symbolic_link@,$symbolic_link,;t t s,@thread_file@,$thread_file,;t t s,@tm_file_list@,$tm_file_list,;t t s,@tm_include_list@,$tm_include_list,;t t s,@tm_defines@,$tm_defines,;t t s,@tm_p_file_list@,$tm_p_file_list,;t t s,@tm_p_include_list@,$tm_p_include_list,;t t s,@xm_file_list@,$xm_file_list,;t t s,@xm_include_list@,$xm_include_list,;t t s,@xm_defines@,$xm_defines,;t t s,@target_noncanonical@,$target_noncanonical,;t t s,@c_target_objs@,$c_target_objs,;t t s,@cxx_target_objs@,$cxx_target_objs,;t t s,@target_cpu_default@,$target_cpu_default,;t t s,@set_gcc_lib_path@,$set_gcc_lib_path,;t t s,@LIBOBJS@,$LIBOBJS,;t t s,@LTLIBOBJS@,$LTLIBOBJS,;t t /@language_hooks@/r $language_hooks s,@language_hooks@,,;t t CEOF _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # Split the substitutions into bite-sized pieces for seds with # small command number limits, like on Digital OSF/1 and HP-UX. ac_max_sed_lines=48 ac_sed_frag=1 # Number of current file. ac_beg=1 # First line for current file. ac_end=$ac_max_sed_lines # Line after last line for current file. ac_more_lines=: ac_sed_cmds= while $ac_more_lines; do if test $ac_beg -gt 1; then sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag else sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag fi if test ! -s $tmp/subs.frag; then ac_more_lines=false else # The purpose of the label and of the branching condition is to # speed up the sed processing (if there are no `@' at all, there # is no need to browse any of the substitutions). # These are the two extra sed commands mentioned above. (echo ':t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed if test -z "$ac_sed_cmds"; then ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" else ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" fi ac_sed_frag=`expr $ac_sed_frag + 1` ac_beg=$ac_end ac_end=`expr $ac_end + $ac_max_sed_lines` fi done if test -z "$ac_sed_cmds"; then ac_sed_cmds=cat fi fi # test -n "$CONFIG_FILES" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". case $ac_file in - | *:- | *:-:* ) # input from stdin cat >$tmp/stdin ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; * ) ac_file_in=$ac_file.in ;; esac # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. ac_dir=`(dirname "$ac_file") 2>/dev/null || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` { if $as_mkdir_p; then mkdir -p "$ac_dir" else as_dir="$ac_dir" as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} { (exit 1); exit 1; }; }; } ac_builddir=. if test "$ac_dir" != .; then ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A "../" for each directory in $ac_dir_suffix. ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` else ac_dir_suffix= ac_top_builddir= fi case $srcdir in .) # No --srcdir option. We are building in place. ac_srcdir=. if test -z "$ac_top_builddir"; then ac_top_srcdir=. else ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` fi ;; [\\/]* | ?:[\\/]* ) # Absolute path. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ;; *) # Relative path. ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_builddir$srcdir ;; esac # Do not use `cd foo && pwd` to compute absolute paths, because # the directories may not exist. case `pwd` in .) ac_abs_builddir="$ac_dir";; *) case "$ac_dir" in .) ac_abs_builddir=`pwd`;; [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; *) ac_abs_builddir=`pwd`/"$ac_dir";; esac;; esac case $ac_abs_builddir in .) ac_abs_top_builddir=${ac_top_builddir}.;; *) case ${ac_top_builddir}. in .) ac_abs_top_builddir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; esac;; esac case $ac_abs_builddir in .) ac_abs_srcdir=$ac_srcdir;; *) case $ac_srcdir in .) ac_abs_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; esac;; esac case $ac_abs_builddir in .) ac_abs_top_srcdir=$ac_top_srcdir;; *) case $ac_top_srcdir in .) ac_abs_top_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; esac;; esac if test x"$ac_file" != x-; then { echo "$as_me:$LINENO: creating $ac_file" >&5 echo "$as_me: creating $ac_file" >&6;} rm -f "$ac_file" fi # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ if test x"$ac_file" = x-; then configure_input= else configure_input="$ac_file. " fi configure_input=$configure_input"Generated from `echo $ac_file_in | sed 's,.*/,,'` by configure." # First look for the input files in the build tree, otherwise in the # src tree. ac_file_inputs=`IFS=: for f in $ac_file_in; do case $f in -) echo $tmp/stdin ;; [\\/$]*) # Absolute (can't be DOS-style, as IFS=:) test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } echo "$f";; *) # Relative if test -f "$f"; then # Build tree echo "$f" elif test -f "$srcdir/$f"; then # Source tree echo "$srcdir/$f" else # /dev/null tree { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } fi;; esac done` || { (exit 1); exit 1; } _ACEOF cat >>$CONFIG_STATUS <<_ACEOF sed "$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s,@configure_input@,$configure_input,;t t s,@srcdir@,$ac_srcdir,;t t s,@abs_srcdir@,$ac_abs_srcdir,;t t s,@top_srcdir@,$ac_top_srcdir,;t t s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t s,@builddir@,$ac_builddir,;t t s,@abs_builddir@,$ac_abs_builddir,;t t s,@top_builddir@,$ac_top_builddir,;t t s,@abs_top_builddir@,$ac_abs_top_builddir,;t t " $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out rm -f $tmp/stdin if test x"$ac_file" != x-; then mv $tmp/out $ac_file else cat $tmp/out rm -f $tmp/out fi done _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # # CONFIG_HEADER section. # # These sed commands are passed to sed as "A NAME B NAME C VALUE D", where # NAME is the cpp macro being defined and VALUE is the value it is being given. # # ac_d sets the value in "#define NAME VALUE" lines. ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' ac_dB='[ ].*$,\1#\2' ac_dC=' ' ac_dD=',;t' # ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' ac_uB='$,\1#\2define\3' ac_uC=' ' ac_uD=',;t' for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". case $ac_file in - | *:- | *:-:* ) # input from stdin cat >$tmp/stdin ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; * ) ac_file_in=$ac_file.in ;; esac test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5 echo "$as_me: creating $ac_file" >&6;} # First look for the input files in the build tree, otherwise in the # src tree. ac_file_inputs=`IFS=: for f in $ac_file_in; do case $f in -) echo $tmp/stdin ;; [\\/$]*) # Absolute (can't be DOS-style, as IFS=:) test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } # Do quote $f, to prevent DOS paths from being IFS'd. echo "$f";; *) # Relative if test -f "$f"; then # Build tree echo "$f" elif test -f "$srcdir/$f"; then # Source tree echo "$srcdir/$f" else # /dev/null tree { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } fi;; esac done` || { (exit 1); exit 1; } # Remove the trailing spaces. sed 's/[ ]*$//' $ac_file_inputs >$tmp/in _ACEOF # Transform confdefs.h into two sed scripts, `conftest.defines' and # `conftest.undefs', that substitutes the proper values into # config.h.in to produce config.h. The first handles `#define' # templates, and the second `#undef' templates. # And first: Protect against being on the right side of a sed subst in # config.status. Protect against being in an unquoted here document # in config.status. rm -f conftest.defines conftest.undefs # Using a here document instead of a string reduces the quoting nightmare. # Putting comments in sed scripts is not portable. # # `end' is used to avoid that the second main sed command (meant for # 0-ary CPP macros) applies to n-ary macro definitions. # See the Autoconf documentation for `clear'. cat >confdef2sed.sed <<\_ACEOF s/[\\&,]/\\&/g s,[\\$`],\\&,g t clear : clear s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp t end s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp : end _ACEOF # If some macros were called several times there might be several times # the same #defines, which is useless. Nevertheless, we may not want to # sort them, since we want the *last* AC-DEFINE to be honored. uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs rm -f confdef2sed.sed # This sed command replaces #undef with comments. This is necessary, for # example, in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. cat >>conftest.undefs <<\_ACEOF s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, _ACEOF # Break up conftest.defines because some shells have a limit on the size # of here documents, and old seds have small limits too (100 cmds). echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS echo ' :' >>$CONFIG_STATUS rm -f conftest.tail while grep . conftest.defines >/dev/null do # Write a limited-size here document to $tmp/defines.sed. echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS # Speed up: don't consider the non `#define' lines. echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS # Work around the forget-to-reset-the-flag bug. echo 't clr' >>$CONFIG_STATUS echo ': clr' >>$CONFIG_STATUS sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS echo 'CEOF sed -f $tmp/defines.sed $tmp/in >$tmp/out rm -f $tmp/in mv $tmp/out $tmp/in ' >>$CONFIG_STATUS sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail rm -f conftest.defines mv conftest.tail conftest.defines done rm -f conftest.defines echo ' fi # grep' >>$CONFIG_STATUS echo >>$CONFIG_STATUS # Break up conftest.undefs because some shells have a limit on the size # of here documents, and old seds have small limits too (100 cmds). echo ' # Handle all the #undef templates' >>$CONFIG_STATUS rm -f conftest.tail while grep . conftest.undefs >/dev/null do # Write a limited-size here document to $tmp/undefs.sed. echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS # Speed up: don't consider the non `#undef' echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS # Work around the forget-to-reset-the-flag bug. echo 't clr' >>$CONFIG_STATUS echo ': clr' >>$CONFIG_STATUS sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS echo 'CEOF sed -f $tmp/undefs.sed $tmp/in >$tmp/out rm -f $tmp/in mv $tmp/out $tmp/in ' >>$CONFIG_STATUS sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail rm -f conftest.undefs mv conftest.tail conftest.undefs done rm -f conftest.undefs cat >>$CONFIG_STATUS <<\_ACEOF # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ if test x"$ac_file" = x-; then echo "/* Generated by configure. */" >$tmp/config.h else echo "/* $ac_file. Generated by configure. */" >$tmp/config.h fi cat $tmp/in >>$tmp/config.h rm -f $tmp/in if test x"$ac_file" != x-; then if diff $ac_file $tmp/config.h >/dev/null 2>&1; then { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 echo "$as_me: $ac_file is unchanged" >&6;} else ac_dir=`(dirname "$ac_file") 2>/dev/null || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` { if $as_mkdir_p; then mkdir -p "$ac_dir" else as_dir="$ac_dir" as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} { (exit 1); exit 1; }; }; } rm -f $ac_file mv $tmp/config.h $ac_file fi else cat $tmp/config.h rm -f $tmp/config.h fi done _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # # CONFIG_COMMANDS section. # for ac_file in : $CONFIG_COMMANDS; do test "x$ac_file" = x: && continue ac_dest=`echo "$ac_file" | sed 's,:.*,,'` ac_source=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_dir=`(dirname "$ac_dest") 2>/dev/null || $as_expr X"$ac_dest" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_dest" : 'X\(//\)[^/]' \| \ X"$ac_dest" : 'X\(//\)$' \| \ X"$ac_dest" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$ac_dest" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` { if $as_mkdir_p; then mkdir -p "$ac_dir" else as_dir="$ac_dir" as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} { (exit 1); exit 1; }; }; } ac_builddir=. if test "$ac_dir" != .; then ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A "../" for each directory in $ac_dir_suffix. ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` else ac_dir_suffix= ac_top_builddir= fi case $srcdir in .) # No --srcdir option. We are building in place. ac_srcdir=. if test -z "$ac_top_builddir"; then ac_top_srcdir=. else ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` fi ;; [\\/]* | ?:[\\/]* ) # Absolute path. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ;; *) # Relative path. ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_builddir$srcdir ;; esac # Do not use `cd foo && pwd` to compute absolute paths, because # the directories may not exist. case `pwd` in .) ac_abs_builddir="$ac_dir";; *) case "$ac_dir" in .) ac_abs_builddir=`pwd`;; [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; *) ac_abs_builddir=`pwd`/"$ac_dir";; esac;; esac case $ac_abs_builddir in .) ac_abs_top_builddir=${ac_top_builddir}.;; *) case ${ac_top_builddir}. in .) ac_abs_top_builddir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; esac;; esac case $ac_abs_builddir in .) ac_abs_srcdir=$ac_srcdir;; *) case $ac_srcdir in .) ac_abs_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; esac;; esac case $ac_abs_builddir in .) ac_abs_top_srcdir=$ac_top_srcdir;; *) case $ac_top_srcdir in .) ac_abs_top_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; esac;; esac { echo "$as_me:$LINENO: executing $ac_dest commands" >&5 echo "$as_me: executing $ac_dest commands" >&6;} case $ac_dest in default ) case ${CONFIG_HEADERS} in *auto-host.h:config.in*) echo > cstamp-h ;; esac # Make sure all the subdirs exist. for d in $subdirs do test -d $d || mkdir $d done # If the host supports symlinks, point stage[1234] at ../stage[1234] so # bootstrapping and the installation procedure can still use # CC="stage1/xgcc -Bstage1/". If the host doesn't support symlinks, # FLAGS_TO_PASS has been modified to solve the problem there. # This is virtually a duplicate of what happens in configure.lang; we do # an extra check to make sure this only happens if ln -s can be used. if test "$symbolic_link" = "ln -s"; then for d in ${subdirs} fixinc ; do STARTDIR=`${PWDCMD-pwd}` cd $d for t in stage1 stage2 stage3 stage4 stageprofile stagefeedback include do rm -f $t $symbolic_link ../$t $t 2>/dev/null done cd $STARTDIR done else true ; fi ;; esac done _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF { (exit 0); exit 0; } _ACEOF chmod +x $CONFIG_STATUS ac_clean_files=$ac_clean_files_save # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || { (exit 1); exit 1; } fi diff --git a/contrib/gcc/cp/decl.c b/contrib/gcc/cp/decl.c index 6c7cece8eee1..451993eb492a 100644 --- a/contrib/gcc/cp/decl.c +++ b/contrib/gcc/cp/decl.c @@ -1,11350 +1,11370 @@ /* Process declarations and variables for C++ compiler. Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. Contributed by Michael Tiemann (tiemann@cygnus.com) This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Process declarations and symbol lookup for C++ front end. Also constructs types; the standard scalar types at initialization, and structure, union, array and enum types when they are declared. */ /* ??? not all decl nodes are given the most useful possible line numbers. For example, the CONST_DECLs for enum values. */ #include "config.h" #include "system.h" #include "coretypes.h" #include "tm.h" #include "tree.h" #include "rtl.h" #include "expr.h" #include "flags.h" #include "cp-tree.h" #include "tree-inline.h" #include "decl.h" #include "lex.h" #include "output.h" #include "except.h" #include "toplev.h" #include "hashtab.h" #include "tm_p.h" #include "target.h" #include "c-common.h" #include "c-pragma.h" #include "diagnostic.h" #include "debug.h" #include "timevar.h" static tree grokparms (tree, tree *); static const char *redeclaration_error_message (tree, tree); static int decl_jump_unsafe (tree); static void require_complete_types_for_parms (tree); static int ambi_op_p (enum tree_code); static int unary_op_p (enum tree_code); static void push_local_name (tree); static tree grok_reference_init (tree, tree, tree, tree *); static tree grokfndecl (tree, tree, tree, tree, tree, int, enum overload_flags, tree, tree, int, int, int, int, int, int, tree); static tree grokvardecl (tree, tree, RID_BIT_TYPE *, int, int, tree); static void record_unknown_type (tree, const char *); static tree builtin_function_1 (const char *, tree, tree, int, enum built_in_class, const char *, tree); static tree build_library_fn_1 (tree, enum tree_code, tree); static int member_function_or_else (tree, tree, enum overload_flags); static void bad_specifiers (tree, const char *, int, int, int, int, int); static void check_for_uninitialized_const_var (tree); static hashval_t typename_hash (const void *); static int typename_compare (const void *, const void *); static tree local_variable_p_walkfn (tree *, int *, void *); static tree record_builtin_java_type (const char *, int); static const char *tag_name (enum tag_types code); static int walk_namespaces_r (tree, walk_namespaces_fn, void *); static int walk_globals_r (tree, void*); static int walk_vtables_r (tree, void*); static tree make_label_decl (tree, int); static void use_label (tree); static void check_previous_goto_1 (tree, struct cp_binding_level *, tree, const location_t *); static void check_previous_goto (struct named_label_use_list *); static void check_switch_goto (struct cp_binding_level *); static void check_previous_gotos (tree); static void pop_label (tree, tree); static void pop_labels (tree); static void maybe_deduce_size_from_array_init (tree, tree); static void layout_var_decl (tree); static void maybe_commonize_var (tree); static tree check_initializer (tree, tree, int, tree *); static void make_rtl_for_nonlocal_decl (tree, tree, const char *); static void save_function_data (tree); static void check_function_type (tree, tree); static void begin_constructor_body (void); static void finish_constructor_body (void); static void begin_destructor_body (void); static void finish_destructor_body (void); static tree create_array_type_for_decl (tree, tree, tree); static tree get_atexit_node (void); static tree get_dso_handle_node (void); static tree start_cleanup_fn (void); static void end_cleanup_fn (void); static tree cp_make_fname_decl (tree, int); static void initialize_predefined_identifiers (void); static tree check_special_function_return_type (special_function_kind, tree, tree); static tree push_cp_library_fn (enum tree_code, tree); static tree build_cp_library_fn (tree, enum tree_code, tree); static void store_parm_decls (tree); static int cp_missing_noreturn_ok_p (tree); static void initialize_local_var (tree, tree); static void expand_static_init (tree, tree); static tree next_initializable_field (tree); static tree reshape_init (tree, tree *); static bool reshape_init_array (tree, tree, tree *, tree); static tree build_typename_type (tree, tree, tree); /* Erroneous argument lists can use this *IFF* they do not modify it. */ tree error_mark_list; /* The following symbols are subsumed in the cp_global_trees array, and listed here individually for documentation purposes. C++ extensions tree wchar_decl_node; tree vtable_entry_type; tree delta_type_node; tree __t_desc_type_node; tree ti_desc_type_node; tree bltn_desc_type_node, ptr_desc_type_node; tree ary_desc_type_node, func_desc_type_node, enum_desc_type_node; tree class_desc_type_node, si_class_desc_type_node, vmi_class_desc_type_node; tree ptm_desc_type_node; tree base_desc_type_node; tree class_type_node; tree unknown_type_node; Array type `vtable_entry_type[]' tree vtbl_type_node; tree vtbl_ptr_type_node; Namespaces, tree std_node; tree abi_node; A FUNCTION_DECL which can call `abort'. Not necessarily the one that the user will declare, but sufficient to be called by routines that want to abort the program. tree abort_fndecl; The FUNCTION_DECL for the default `::operator delete'. tree global_delete_fndecl; Used by RTTI tree type_info_type_node, tinfo_decl_id, tinfo_decl_type; tree tinfo_var_id; */ tree cp_global_trees[CPTI_MAX]; /* Indicates that there is a type value in some namespace, although that is not necessarily in scope at the moment. */ tree global_type_node; /* The node that holds the "name" of the global scope. */ tree global_scope_name; /* Used only for jumps to as-yet undefined labels, since jumps to defined labels can have their validity checked immediately. */ struct named_label_use_list GTY(()) { struct cp_binding_level *binding_level; tree names_in_scope; tree label_decl; location_t o_goto_locus; struct named_label_use_list *next; }; #define named_label_uses cp_function_chain->x_named_label_uses #define local_names cp_function_chain->x_local_names /* A list of objects which have constructors or destructors which reside in the global scope. The decl is stored in the TREE_VALUE slot and the initializer is stored in the TREE_PURPOSE slot. */ tree static_aggregates; /* -- end of C++ */ /* A node for the integer constants 2, and 3. */ tree integer_two_node, integer_three_node; /* A list of all LABEL_DECLs in the function that have names. Here so we can clear out their names' definitions at the end of the function, and so we can check the validity of jumps to these labels. */ struct named_label_list GTY(()) { struct cp_binding_level *binding_level; tree names_in_scope; tree old_value; tree label_decl; tree bad_decls; struct named_label_list *next; unsigned int in_try_scope : 1; unsigned int in_catch_scope : 1; }; #define named_labels cp_function_chain->x_named_labels /* The number of function bodies which we are currently processing. (Zero if we are at namespace scope, one inside the body of a function, two inside the body of a function in a local class, etc.) */ int function_depth; /* States indicating how grokdeclarator() should handle declspecs marked with __attribute__((deprecated)). An object declared as __attribute__((deprecated)) suppresses warnings of uses of other deprecated items. */ enum deprecated_states { DEPRECATED_NORMAL, DEPRECATED_SUPPRESS }; static enum deprecated_states deprecated_state = DEPRECATED_NORMAL; /* Set by add_implicitly_declared_members() to keep those members from being flagged as deprecated or reported as using deprecated types. */ int adding_implicit_members = 0; /* True if a declaration with an `extern' linkage specifier is being processed. */ bool have_extern_spec; /* A TREE_LIST of VAR_DECLs. The TREE_PURPOSE is a RECORD_TYPE or UNION_TYPE; the TREE_VALUE is a VAR_DECL with that type. At the time the VAR_DECL was declared, the type was incomplete. */ static GTY(()) tree incomplete_vars; /* Returns the kind of template specialization we are currently processing, given that it's declaration contained N_CLASS_SCOPES explicit scope qualifications. */ tmpl_spec_kind current_tmpl_spec_kind (int n_class_scopes) { int n_template_parm_scopes = 0; int seen_specialization_p = 0; int innermost_specialization_p = 0; struct cp_binding_level *b; /* Scan through the template parameter scopes. */ for (b = current_binding_level; b->kind == sk_template_parms; b = b->level_chain) { /* If we see a specialization scope inside a parameter scope, then something is wrong. That corresponds to a declaration like: template template <> ... which is always invalid since [temp.expl.spec] forbids the specialization of a class member template if the enclosing class templates are not explicitly specialized as well. */ if (b->explicit_spec_p) { if (n_template_parm_scopes == 0) innermost_specialization_p = 1; else seen_specialization_p = 1; } else if (seen_specialization_p == 1) return tsk_invalid_member_spec; ++n_template_parm_scopes; } /* Handle explicit instantiations. */ if (processing_explicit_instantiation) { if (n_template_parm_scopes != 0) /* We've seen a template parameter list during an explicit instantiation. For example: template template void f(int); This is erroneous. */ return tsk_invalid_expl_inst; else return tsk_expl_inst; } if (n_template_parm_scopes < n_class_scopes) /* We've not seen enough template headers to match all the specialized classes present. For example: template void R::S::f(int); This is invalid; there needs to be one set of template parameters for each class. */ return tsk_insufficient_parms; else if (n_template_parm_scopes == n_class_scopes) /* We're processing a non-template declaration (even though it may be a member of a template class.) For example: template void S::f(int); The `class T' maches the `S', leaving no template headers corresponding to the `f'. */ return tsk_none; else if (n_template_parm_scopes > n_class_scopes + 1) /* We've got too many template headers. For example: template <> template void f (T); There need to be more enclosing classes. */ return tsk_excessive_parms; else /* This must be a template. It's of the form: template template void S::f(U); This is a specialization if the innermost level was a specialization; otherwise it's just a definition of the template. */ return innermost_specialization_p ? tsk_expl_spec : tsk_template; } /* Exit the current scope. */ void finish_scope (void) { poplevel (0, 0, 0); } /* When a label goes out of scope, check to see if that label was used in a valid manner, and issue any appropriate warnings or errors. */ static void pop_label (tree label, tree old_value) { if (!processing_template_decl) { if (DECL_INITIAL (label) == NULL_TREE) { location_t location; cp_error_at ("label `%D' used but not defined", label); location.file = input_filename; location.line = 0; /* Avoid crashing later. */ define_label (location, DECL_NAME (label)); } else if (warn_unused_label && !TREE_USED (label)) cp_warning_at ("label `%D' defined but not used", label); } SET_IDENTIFIER_LABEL_VALUE (DECL_NAME (label), old_value); } /* At the end of a function, all labels declared within the function go out of scope. BLOCK is the top-level block for the function. */ static void pop_labels (tree block) { struct named_label_list *link; /* Clear out the definitions of all label names, since their scopes end here. */ for (link = named_labels; link; link = link->next) { pop_label (link->label_decl, link->old_value); /* Put the labels into the "variables" of the top-level block, so debugger can see them. */ TREE_CHAIN (link->label_decl) = BLOCK_VARS (block); BLOCK_VARS (block) = link->label_decl; } named_labels = NULL; } /* Exit a binding level. Pop the level off, and restore the state of the identifier-decl mappings that were in effect when this level was entered. If KEEP == 1, this level had explicit declarations, so and create a "block" (a BLOCK node) for the level to record its declarations and subblocks for symbol table output. If FUNCTIONBODY is nonzero, this level is the body of a function, so create a block as if KEEP were set and also clear out all label names. If REVERSE is nonzero, reverse the order of decls before putting them into the BLOCK. */ tree poplevel (int keep, int reverse, int functionbody) { tree link; /* The chain of decls was accumulated in reverse order. Put it into forward order, just for cleanliness. */ tree decls; int tmp = functionbody; int real_functionbody; tree subblocks; tree block = NULL_TREE; tree decl; int leaving_for_scope; scope_kind kind; timevar_push (TV_NAME_LOOKUP); my_friendly_assert (current_binding_level->kind != sk_class, 19990916); real_functionbody = (current_binding_level->kind == sk_cleanup ? ((functionbody = 0), tmp) : functionbody); subblocks = functionbody >= 0 ? current_binding_level->blocks : 0; my_friendly_assert (!current_binding_level->class_shadowed, 19990414); /* We used to use KEEP == 2 to indicate that the new block should go at the beginning of the list of blocks at this binding level, rather than the end. This hack is no longer used. */ my_friendly_assert (keep == 0 || keep == 1, 0); if (current_binding_level->keep) keep = 1; /* Any uses of undefined labels, and any defined labels, now operate under constraints of next binding contour. */ if (cfun && !functionbody) { struct cp_binding_level *level_chain; level_chain = current_binding_level->level_chain; if (level_chain) { struct named_label_use_list *uses; struct named_label_list *labels; for (labels = named_labels; labels; labels = labels->next) if (labels->binding_level == current_binding_level) { tree decl; if (current_binding_level->kind == sk_try) labels->in_try_scope = 1; if (current_binding_level->kind == sk_catch) labels->in_catch_scope = 1; for (decl = labels->names_in_scope; decl; decl = TREE_CHAIN (decl)) if (decl_jump_unsafe (decl)) labels->bad_decls = tree_cons (NULL_TREE, decl, labels->bad_decls); labels->binding_level = level_chain; labels->names_in_scope = level_chain->names; } for (uses = named_label_uses; uses; uses = uses->next) if (uses->binding_level == current_binding_level) { uses->binding_level = level_chain; uses->names_in_scope = level_chain->names; } } } /* Get the decls in the order they were written. Usually current_binding_level->names is in reverse order. But parameter decls were previously put in forward order. */ if (reverse) current_binding_level->names = decls = nreverse (current_binding_level->names); else decls = current_binding_level->names; /* Output any nested inline functions within this block if they weren't already output. */ for (decl = decls; decl; decl = TREE_CHAIN (decl)) if (TREE_CODE (decl) == FUNCTION_DECL && ! TREE_ASM_WRITTEN (decl) && DECL_INITIAL (decl) != NULL_TREE && TREE_ADDRESSABLE (decl) && decl_function_context (decl) == current_function_decl) { /* If this decl was copied from a file-scope decl on account of a block-scope extern decl, propagate TREE_ADDRESSABLE to the file-scope decl. */ if (DECL_ABSTRACT_ORIGIN (decl) != NULL_TREE) TREE_ADDRESSABLE (DECL_ABSTRACT_ORIGIN (decl)) = 1; else { push_function_context (); output_inline_function (decl); pop_function_context (); } } /* When not in function-at-a-time mode, expand_end_bindings will warn about unused variables. But, in function-at-a-time mode expand_end_bindings is not passed the list of variables in the current scope, and therefore no warning is emitted. So, we explicitly warn here. */ if (!processing_template_decl) warn_about_unused_variables (getdecls ()); /* If there were any declarations or structure tags in that level, or if this level is a function body, create a BLOCK to record them for the life of this function. */ block = NULL_TREE; if (keep == 1 || functionbody) block = make_node (BLOCK); if (block != NULL_TREE) { BLOCK_VARS (block) = decls; BLOCK_SUBBLOCKS (block) = subblocks; } /* In each subblock, record that this is its superior. */ if (keep >= 0) for (link = subblocks; link; link = TREE_CHAIN (link)) BLOCK_SUPERCONTEXT (link) = block; /* We still support the old for-scope rules, whereby the variables in a for-init statement were in scope after the for-statement ended. We only use the new rules if flag_new_for_scope is nonzero. */ leaving_for_scope = current_binding_level->kind == sk_for && flag_new_for_scope == 1; /* Remove declarations for all the DECLs in this level. */ for (link = decls; link; link = TREE_CHAIN (link)) { if (leaving_for_scope && TREE_CODE (link) == VAR_DECL && DECL_NAME (link)) { cxx_binding *outer_binding = IDENTIFIER_BINDING (DECL_NAME (link))->previous; tree ns_binding; if (!outer_binding) ns_binding = IDENTIFIER_NAMESPACE_VALUE (DECL_NAME (link)); else ns_binding = NULL_TREE; if (outer_binding && outer_binding->scope == current_binding_level->level_chain) /* We have something like: int i; for (int i; ;); and we are leaving the `for' scope. There's no reason to keep the binding of the inner `i' in this case. */ pop_binding (DECL_NAME (link), link); else if ((outer_binding && (TREE_CODE (outer_binding->value) == TYPE_DECL)) || (ns_binding && TREE_CODE (ns_binding) == TYPE_DECL)) /* Here, we have something like: typedef int I; void f () { for (int I; ;); } We must pop the for-scope binding so we know what's a type and what isn't. */ pop_binding (DECL_NAME (link), link); else { /* Mark this VAR_DECL as dead so that we can tell we left it there only for backward compatibility. */ DECL_DEAD_FOR_LOCAL (link) = 1; /* Keep track of what should have happened when we popped the binding. */ if (outer_binding && outer_binding->value) DECL_SHADOWED_FOR_VAR (link) = outer_binding->value; /* Add it to the list of dead variables in the next outermost binding to that we can remove these when we leave that binding. */ current_binding_level->level_chain->dead_vars_from_for = tree_cons (NULL_TREE, link, current_binding_level->level_chain-> dead_vars_from_for); /* Although we don't pop the cxx_binding, we do clear its SCOPE since the scope is going away now. */ IDENTIFIER_BINDING (DECL_NAME (link))->scope = NULL; } } else { /* Remove the binding. */ decl = link; if (TREE_CODE (decl) == TREE_LIST) decl = TREE_VALUE (decl); if (DECL_P (decl)) pop_binding (DECL_NAME (decl), decl); else if (TREE_CODE (decl) == OVERLOAD) pop_binding (DECL_NAME (OVL_FUNCTION (decl)), decl); else abort (); } } /* Remove declarations for any `for' variables from inner scopes that we kept around. */ for (link = current_binding_level->dead_vars_from_for; link; link = TREE_CHAIN (link)) pop_binding (DECL_NAME (TREE_VALUE (link)), TREE_VALUE (link)); /* Restore the IDENTIFIER_TYPE_VALUEs. */ for (link = current_binding_level->type_shadowed; link; link = TREE_CHAIN (link)) SET_IDENTIFIER_TYPE_VALUE (TREE_PURPOSE (link), TREE_VALUE (link)); /* Restore the IDENTIFIER_LABEL_VALUEs for local labels. */ for (link = current_binding_level->shadowed_labels; link; link = TREE_CHAIN (link)) pop_label (TREE_VALUE (link), TREE_PURPOSE (link)); /* There may be OVERLOADs (wrapped in TREE_LISTs) on the BLOCK_VARs list if a `using' declaration put them there. The debugging back-ends won't understand OVERLOAD, so we remove them here. Because the BLOCK_VARS are (temporarily) shared with CURRENT_BINDING_LEVEL->NAMES we must do this fixup after we have popped all the bindings. */ if (block) { tree* d; for (d = &BLOCK_VARS (block); *d; ) { if (TREE_CODE (*d) == TREE_LIST) *d = TREE_CHAIN (*d); else d = &TREE_CHAIN (*d); } } /* If the level being exited is the top level of a function, check over all the labels. */ if (functionbody) { /* Since this is the top level block of a function, the vars are the function's parameters. Don't leave them in the BLOCK because they are found in the FUNCTION_DECL instead. */ BLOCK_VARS (block) = 0; pop_labels (block); } kind = current_binding_level->kind; leave_scope (); if (functionbody) DECL_INITIAL (current_function_decl) = block; else if (block) current_binding_level->blocks = chainon (current_binding_level->blocks, block); /* If we did not make a block for the level just exited, any blocks made for inner levels (since they cannot be recorded as subblocks in that level) must be carried forward so they will later become subblocks of something else. */ else if (subblocks) current_binding_level->blocks = chainon (current_binding_level->blocks, subblocks); /* Each and every BLOCK node created here in `poplevel' is important (e.g. for proper debugging information) so if we created one earlier, mark it as "used". */ if (block) TREE_USED (block) = 1; /* Take care of compiler's internal binding structures. */ if (kind == sk_cleanup) { tree scope_stmts; scope_stmts = add_scope_stmt (/*begin_p=*/0, /*partial_p=*/1); if (block) { SCOPE_STMT_BLOCK (TREE_PURPOSE (scope_stmts)) = block; SCOPE_STMT_BLOCK (TREE_VALUE (scope_stmts)) = block; } block = poplevel (keep, reverse, functionbody); } POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, block); } /* Delete the node BLOCK from the current binding level. This is used for the block inside a stmt expr ({...}) so that the block can be reinserted where appropriate. */ void delete_block (tree block) { tree t; if (current_binding_level->blocks == block) current_binding_level->blocks = TREE_CHAIN (block); for (t = current_binding_level->blocks; t;) { if (TREE_CHAIN (t) == block) TREE_CHAIN (t) = TREE_CHAIN (block); else t = TREE_CHAIN (t); } TREE_CHAIN (block) = NULL_TREE; /* Clear TREE_USED which is always set by poplevel. The flag is set again if insert_block is called. */ TREE_USED (block) = 0; } /* Insert BLOCK at the end of the list of subblocks of the current binding level. This is used when a BIND_EXPR is expanded, to handle the BLOCK node inside the BIND_EXPR. */ void insert_block (tree block) { TREE_USED (block) = 1; current_binding_level->blocks = chainon (current_binding_level->blocks, block); } /* Set the BLOCK node for the innermost scope (the one we are currently in). */ void set_block (tree block ATTRIBUTE_UNUSED ) { /* The RTL expansion machinery requires us to provide this callback, but it is not applicable in function-at-a-time mode. */ } /* Returns nonzero if T is a virtual function table. */ int vtable_decl_p (tree t, void* data ATTRIBUTE_UNUSED ) { return (TREE_CODE (t) == VAR_DECL && DECL_VIRTUAL_P (t)); } /* Returns nonzero if T is a TYPE_DECL for a type with virtual functions. */ int vtype_decl_p (tree t, void *data ATTRIBUTE_UNUSED ) { return (TREE_CODE (t) == TYPE_DECL && TREE_CODE (TREE_TYPE (t)) == RECORD_TYPE && TYPE_POLYMORPHIC_P (TREE_TYPE (t))); } struct walk_globals_data { walk_globals_pred p; walk_globals_fn f; void *data; }; /* Walk the vtable declarations in NAMESPACE. Whenever one is found for which P returns nonzero, call F with its address. If any call to F returns a nonzero value, return a nonzero value. */ static int walk_vtables_r (tree namespace, void* data) { struct walk_globals_data* wgd = (struct walk_globals_data *) data; walk_globals_fn f = wgd->f; void *d = wgd->data; tree decl = NAMESPACE_LEVEL (namespace)->vtables; int result = 0; for (; decl ; decl = TREE_CHAIN (decl)) result |= (*f) (&decl, d); return result; } /* Walk the vtable declarations. Whenever one is found for which P returns nonzero, call F with its address. If any call to F returns a nonzero value, return a nonzero value. */ bool walk_vtables (walk_globals_pred p, walk_globals_fn f, void *data) { struct walk_globals_data wgd; wgd.p = p; wgd.f = f; wgd.data = data; return walk_namespaces (walk_vtables_r, &wgd); } /* Walk all the namespaces contained NAMESPACE, including NAMESPACE itself, calling F for each. The DATA is passed to F as well. */ static int walk_namespaces_r (tree namespace, walk_namespaces_fn f, void* data) { int result = 0; tree current = NAMESPACE_LEVEL (namespace)->namespaces; result |= (*f) (namespace, data); for (; current; current = TREE_CHAIN (current)) result |= walk_namespaces_r (current, f, data); return result; } /* Walk all the namespaces, calling F for each. The DATA is passed to F as well. */ int walk_namespaces (walk_namespaces_fn f, void* data) { return walk_namespaces_r (global_namespace, f, data); } /* Walk the global declarations in NAMESPACE. Whenever one is found for which P returns nonzero, call F with its address. If any call to F returns a nonzero value, return a nonzero value. */ static int walk_globals_r (tree namespace, void* data) { struct walk_globals_data* wgd = (struct walk_globals_data *) data; walk_globals_pred p = wgd->p; walk_globals_fn f = wgd->f; void *d = wgd->data; tree *t; int result = 0; t = &NAMESPACE_LEVEL (namespace)->names; while (*t) { tree glbl = *t; if ((*p) (glbl, d)) result |= (*f) (t, d); /* If F changed *T, then *T still points at the next item to examine. */ if (*t == glbl) t = &TREE_CHAIN (*t); } return result; } /* Walk the global declarations. Whenever one is found for which P returns true, call F with its address. If any call to F returns true, return true. */ bool walk_globals (walk_globals_pred p, walk_globals_fn f, void *data) { struct walk_globals_data wgd; wgd.p = p; wgd.f = f; wgd.data = data; return walk_namespaces (walk_globals_r, &wgd); } /* Call wrapup_globals_declarations for the globals in NAMESPACE. If DATA is non-NULL, this is the last time we will call wrapup_global_declarations for this NAMESPACE. */ int wrapup_globals_for_namespace (tree namespace, void* data) { struct cp_binding_level *level = NAMESPACE_LEVEL (namespace); varray_type statics = level->static_decls; tree *vec = &VARRAY_TREE (statics, 0); int len = VARRAY_ACTIVE_SIZE (statics); int last_time = (data != 0); if (last_time) { check_global_declarations (vec, len); return 0; } /* Write out any globals that need to be output. */ return wrapup_global_declarations (vec, len); } /* In C++, you don't have to write `struct S' to refer to `S'; you can just use `S'. We accomplish this by creating a TYPE_DECL as if the user had written `typedef struct S S'. Create and return the TYPE_DECL for TYPE. */ tree create_implicit_typedef (tree name, tree type) { tree decl; decl = build_decl (TYPE_DECL, name, type); DECL_ARTIFICIAL (decl) = 1; /* There are other implicit type declarations, like the one *within* a class that allows you to write `S::S'. We must distinguish amongst these. */ SET_DECL_IMPLICIT_TYPEDEF_P (decl); TYPE_NAME (type) = decl; return decl; } /* Remember a local name for name-mangling purposes. */ static void push_local_name (tree decl) { size_t i, nelts; tree t, name; timevar_push (TV_NAME_LOOKUP); if (!local_names) VARRAY_TREE_INIT (local_names, 8, "local_names"); name = DECL_NAME (decl); nelts = VARRAY_ACTIVE_SIZE (local_names); for (i = 0; i < nelts; i++) { t = VARRAY_TREE (local_names, i); if (DECL_NAME (t) == name) { if (!DECL_LANG_SPECIFIC (decl)) retrofit_lang_decl (decl); DECL_LANG_SPECIFIC (decl)->decl_flags.u2sel = 1; if (DECL_LANG_SPECIFIC (t)) DECL_DISCRIMINATOR (decl) = DECL_DISCRIMINATOR (t) + 1; else DECL_DISCRIMINATOR (decl) = 1; VARRAY_TREE (local_names, i) = decl; timevar_pop (TV_NAME_LOOKUP); return; } } VARRAY_PUSH_TREE (local_names, decl); timevar_pop (TV_NAME_LOOKUP); } /* Subroutine of duplicate_decls: return truthvalue of whether or not types of these decls match. For C++, we must compare the parameter list so that `int' can match `int&' in a parameter position, but `int&' is not confused with `const int&'. */ int decls_match (tree newdecl, tree olddecl) { int types_match; if (newdecl == olddecl) return 1; if (TREE_CODE (newdecl) != TREE_CODE (olddecl)) /* If the two DECLs are not even the same kind of thing, we're not interested in their types. */ return 0; if (TREE_CODE (newdecl) == FUNCTION_DECL) { tree f1 = TREE_TYPE (newdecl); tree f2 = TREE_TYPE (olddecl); tree p1 = TYPE_ARG_TYPES (f1); tree p2 = TYPE_ARG_TYPES (f2); if (CP_DECL_CONTEXT (newdecl) != CP_DECL_CONTEXT (olddecl) && ! (DECL_EXTERN_C_P (newdecl) && DECL_EXTERN_C_P (olddecl))) return 0; if (TREE_CODE (f1) != TREE_CODE (f2)) return 0; if (same_type_p (TREE_TYPE (f1), TREE_TYPE (f2))) { if (p2 == NULL_TREE && DECL_EXTERN_C_P (olddecl) && (DECL_BUILT_IN (olddecl) #ifndef NO_IMPLICIT_EXTERN_C || (DECL_IN_SYSTEM_HEADER (newdecl) && !DECL_CLASS_SCOPE_P (newdecl)) || (DECL_IN_SYSTEM_HEADER (olddecl) && !DECL_CLASS_SCOPE_P (olddecl)) #endif )) { types_match = self_promoting_args_p (p1); if (p1 == void_list_node) TREE_TYPE (newdecl) = TREE_TYPE (olddecl); } #ifndef NO_IMPLICIT_EXTERN_C else if (p1 == NULL_TREE && (DECL_EXTERN_C_P (olddecl) && DECL_IN_SYSTEM_HEADER (olddecl) && !DECL_CLASS_SCOPE_P (olddecl)) && (DECL_EXTERN_C_P (newdecl) && DECL_IN_SYSTEM_HEADER (newdecl) && !DECL_CLASS_SCOPE_P (newdecl))) { types_match = self_promoting_args_p (p2); TREE_TYPE (newdecl) = TREE_TYPE (olddecl); } #endif else types_match = compparms (p1, p2); } else types_match = 0; } else if (TREE_CODE (newdecl) == TEMPLATE_DECL) { if (TREE_CODE (DECL_TEMPLATE_RESULT (newdecl)) != TREE_CODE (DECL_TEMPLATE_RESULT (olddecl))) return 0; if (!comp_template_parms (DECL_TEMPLATE_PARMS (newdecl), DECL_TEMPLATE_PARMS (olddecl))) return 0; if (TREE_CODE (DECL_TEMPLATE_RESULT (newdecl)) == TYPE_DECL) types_match = same_type_p (TREE_TYPE (DECL_TEMPLATE_RESULT (olddecl)), TREE_TYPE (DECL_TEMPLATE_RESULT (newdecl))); else types_match = decls_match (DECL_TEMPLATE_RESULT (olddecl), DECL_TEMPLATE_RESULT (newdecl)); } else { /* Need to check scope for variable declaration (VAR_DECL). For typedef (TYPE_DECL), scope is ignored. */ if (TREE_CODE (newdecl) == VAR_DECL && CP_DECL_CONTEXT (newdecl) != CP_DECL_CONTEXT (olddecl)) return 0; if (TREE_TYPE (newdecl) == error_mark_node) types_match = TREE_TYPE (olddecl) == error_mark_node; else if (TREE_TYPE (olddecl) == NULL_TREE) types_match = TREE_TYPE (newdecl) == NULL_TREE; else if (TREE_TYPE (newdecl) == NULL_TREE) types_match = 0; else types_match = comptypes (TREE_TYPE (newdecl), TREE_TYPE (olddecl), COMPARE_REDECLARATION); } return types_match; } /* If NEWDECL is `static' and an `extern' was seen previously, warn about it. OLDDECL is the previous declaration. Note that this does not apply to the C++ case of declaring a variable `extern const' and then later `const'. Don't complain about built-in functions, since they are beyond the user's control. */ void warn_extern_redeclared_static (tree newdecl, tree olddecl) { static const char *const explicit_extern_static_warning = "`%D' was declared `extern' and later `static'"; static const char *const implicit_extern_static_warning = "`%D' was declared implicitly `extern' and later `static'"; tree name; if (TREE_CODE (newdecl) == TYPE_DECL || TREE_CODE (newdecl) == TEMPLATE_DECL || TREE_CODE (newdecl) == CONST_DECL || TREE_CODE (newdecl) == NAMESPACE_DECL) return; /* Don't get confused by static member functions; that's a different use of `static'. */ if (TREE_CODE (newdecl) == FUNCTION_DECL && DECL_STATIC_FUNCTION_P (newdecl)) return; /* If the old declaration was `static', or the new one isn't, then then everything is OK. */ if (DECL_THIS_STATIC (olddecl) || !DECL_THIS_STATIC (newdecl)) return; /* It's OK to declare a builtin function as `static'. */ if (TREE_CODE (olddecl) == FUNCTION_DECL && DECL_ARTIFICIAL (olddecl)) return; name = DECL_ASSEMBLER_NAME (newdecl); pedwarn (IDENTIFIER_IMPLICIT_DECL (name) ? implicit_extern_static_warning : explicit_extern_static_warning, newdecl); cp_pedwarn_at ("previous declaration of `%D'", olddecl); } /* If NEWDECL is a redeclaration of OLDDECL, merge the declarations. If the redeclaration is invalid, a diagnostic is issued, and the error_mark_node is returned. Otherwise, OLDDECL is returned. If NEWDECL is not a redeclaration of OLDDECL, NULL_TREE is returned. */ tree duplicate_decls (tree newdecl, tree olddecl) { unsigned olddecl_uid = DECL_UID (olddecl); int olddecl_friend = 0, types_match = 0; int new_defines_function = 0; if (newdecl == olddecl) return olddecl; types_match = decls_match (newdecl, olddecl); /* If either the type of the new decl or the type of the old decl is an error_mark_node, then that implies that we have already issued an error (earlier) for some bogus type specification, and in that case, it is rather pointless to harass the user with yet more error message about the same declaration, so just pretend the types match here. */ if (TREE_TYPE (newdecl) == error_mark_node || TREE_TYPE (olddecl) == error_mark_node) types_match = 1; if (DECL_P (olddecl) && TREE_CODE (newdecl) == FUNCTION_DECL && TREE_CODE (olddecl) == FUNCTION_DECL && (DECL_UNINLINABLE (newdecl) || DECL_UNINLINABLE (olddecl))) { if (DECL_DECLARED_INLINE_P (newdecl) && DECL_UNINLINABLE (newdecl) && lookup_attribute ("noinline", DECL_ATTRIBUTES (newdecl))) /* Already warned elsewhere. */; else if (DECL_DECLARED_INLINE_P (olddecl) && DECL_UNINLINABLE (olddecl) && lookup_attribute ("noinline", DECL_ATTRIBUTES (olddecl))) /* Already warned. */; else if (DECL_DECLARED_INLINE_P (newdecl) && DECL_UNINLINABLE (olddecl) && lookup_attribute ("noinline", DECL_ATTRIBUTES (olddecl))) { warning ("%Jfunction '%D' redeclared as inline", newdecl, newdecl); warning ("%Jprevious declaration of '%D' with attribute noinline", olddecl, olddecl); } else if (DECL_DECLARED_INLINE_P (olddecl) && DECL_UNINLINABLE (newdecl) && lookup_attribute ("noinline", DECL_ATTRIBUTES (newdecl))) { warning ("%Jfunction '%D' redeclared with attribute noinline", newdecl, newdecl); warning ("%Jprevious declaration of '%D' was inline", olddecl, olddecl); } } /* Check for redeclaration and other discrepancies. */ if (TREE_CODE (olddecl) == FUNCTION_DECL && DECL_ARTIFICIAL (olddecl)) { if (TREE_CODE (newdecl) != FUNCTION_DECL) { /* Avoid warnings redeclaring anticipated built-ins. */ if (DECL_ANTICIPATED (olddecl)) return NULL_TREE; /* If you declare a built-in or predefined function name as static, the old definition is overridden, but optionally warn this was a bad choice of name. */ if (! TREE_PUBLIC (newdecl)) { if (warn_shadow) warning ("shadowing %s function `%#D'", DECL_BUILT_IN (olddecl) ? "built-in" : "library", olddecl); /* Discard the old built-in function. */ return NULL_TREE; } /* If the built-in is not ansi, then programs can override it even globally without an error. */ else if (! DECL_BUILT_IN (olddecl)) warning ("library function `%#D' redeclared as non-function `%#D'", olddecl, newdecl); else { error ("declaration of `%#D'", newdecl); error ("conflicts with built-in declaration `%#D'", olddecl); } return NULL_TREE; } else if (!types_match) { /* Avoid warnings redeclaring anticipated built-ins. */ if (DECL_ANTICIPATED (olddecl)) ; /* Do nothing yet. */ else if ((DECL_EXTERN_C_P (newdecl) && DECL_EXTERN_C_P (olddecl)) || compparms (TYPE_ARG_TYPES (TREE_TYPE (newdecl)), TYPE_ARG_TYPES (TREE_TYPE (olddecl)))) { /* A near match; override the builtin. */ if (TREE_PUBLIC (newdecl)) { warning ("new declaration `%#D'", newdecl); warning ("ambiguates built-in declaration `%#D'", olddecl); } else if (warn_shadow) warning ("shadowing %s function `%#D'", DECL_BUILT_IN (olddecl) ? "built-in" : "library", olddecl); } else /* Discard the old built-in function. */ return NULL_TREE; /* Replace the old RTL to avoid problems with inlining. */ SET_DECL_RTL (olddecl, DECL_RTL (newdecl)); } /* Even if the types match, prefer the new declarations type for anticipated built-ins, for exception lists, etc... */ else if (DECL_ANTICIPATED (olddecl)) { tree type = TREE_TYPE (newdecl); tree attribs = (*targetm.merge_type_attributes) (TREE_TYPE (olddecl), type); type = cp_build_type_attribute_variant (type, attribs); TREE_TYPE (newdecl) = TREE_TYPE (olddecl) = type; } /* Whether or not the builtin can throw exceptions has no bearing on this declarator. */ TREE_NOTHROW (olddecl) = 0; if (DECL_THIS_STATIC (newdecl) && !DECL_THIS_STATIC (olddecl)) { /* If a builtin function is redeclared as `static', merge the declarations, but make the original one static. */ DECL_THIS_STATIC (olddecl) = 1; TREE_PUBLIC (olddecl) = 0; /* Make the old declaration consistent with the new one so that all remnants of the builtin-ness of this function will be banished. */ SET_DECL_LANGUAGE (olddecl, DECL_LANGUAGE (newdecl)); SET_DECL_RTL (olddecl, DECL_RTL (newdecl)); } } else if (TREE_CODE (olddecl) != TREE_CODE (newdecl)) { if ((TREE_CODE (olddecl) == TYPE_DECL && DECL_ARTIFICIAL (olddecl) && TREE_CODE (newdecl) != TYPE_DECL && ! (TREE_CODE (newdecl) == TEMPLATE_DECL && TREE_CODE (DECL_TEMPLATE_RESULT (newdecl)) == TYPE_DECL)) || (TREE_CODE (newdecl) == TYPE_DECL && DECL_ARTIFICIAL (newdecl) && TREE_CODE (olddecl) != TYPE_DECL && ! (TREE_CODE (olddecl) == TEMPLATE_DECL && (TREE_CODE (DECL_TEMPLATE_RESULT (olddecl)) == TYPE_DECL)))) { /* We do nothing special here, because C++ does such nasty things with TYPE_DECLs. Instead, just let the TYPE_DECL get shadowed, and know that if we need to find a TYPE_DECL for a given name, we can look in the IDENTIFIER_TYPE_VALUE slot of the identifier. */ return NULL_TREE; } if ((TREE_CODE (newdecl) == FUNCTION_DECL && DECL_FUNCTION_TEMPLATE_P (olddecl)) || (TREE_CODE (olddecl) == FUNCTION_DECL && DECL_FUNCTION_TEMPLATE_P (newdecl))) return NULL_TREE; error ("`%#D' redeclared as different kind of symbol", newdecl); if (TREE_CODE (olddecl) == TREE_LIST) olddecl = TREE_VALUE (olddecl); cp_error_at ("previous declaration of `%#D'", olddecl); /* New decl is completely inconsistent with the old one => tell caller to replace the old one. */ return NULL_TREE; } else if (!types_match) { if (CP_DECL_CONTEXT (newdecl) != CP_DECL_CONTEXT (olddecl)) /* These are certainly not duplicate declarations; they're from different scopes. */ return NULL_TREE; if (TREE_CODE (newdecl) == TEMPLATE_DECL) { /* The name of a class template may not be declared to refer to any other template, class, function, object, namespace, value, or type in the same scope. */ if (TREE_CODE (DECL_TEMPLATE_RESULT (olddecl)) == TYPE_DECL || TREE_CODE (DECL_TEMPLATE_RESULT (newdecl)) == TYPE_DECL) { error ("declaration of template `%#D'", newdecl); cp_error_at ("conflicts with previous declaration `%#D'", olddecl); } else if (TREE_CODE (DECL_TEMPLATE_RESULT (olddecl)) == FUNCTION_DECL && TREE_CODE (DECL_TEMPLATE_RESULT (newdecl)) == FUNCTION_DECL && compparms (TYPE_ARG_TYPES (TREE_TYPE (DECL_TEMPLATE_RESULT (olddecl))), TYPE_ARG_TYPES (TREE_TYPE (DECL_TEMPLATE_RESULT (newdecl)))) && comp_template_parms (DECL_TEMPLATE_PARMS (newdecl), DECL_TEMPLATE_PARMS (olddecl)) /* Template functions can be disambiguated by return type. */ && same_type_p (TREE_TYPE (TREE_TYPE (newdecl)), TREE_TYPE (TREE_TYPE (olddecl)))) { error ("new declaration `%#D'", newdecl); cp_error_at ("ambiguates old declaration `%#D'", olddecl); } return NULL_TREE; } if (TREE_CODE (newdecl) == FUNCTION_DECL) { if (DECL_EXTERN_C_P (newdecl) && DECL_EXTERN_C_P (olddecl)) { error ("declaration of C function `%#D' conflicts with", newdecl); cp_error_at ("previous declaration `%#D' here", olddecl); } else if (compparms (TYPE_ARG_TYPES (TREE_TYPE (newdecl)), TYPE_ARG_TYPES (TREE_TYPE (olddecl)))) { error ("new declaration `%#D'", newdecl); cp_error_at ("ambiguates old declaration `%#D'", olddecl); } else return NULL_TREE; } - - /* Already complained about this, so don't do so again. */ - else if (current_class_type == NULL_TREE - || IDENTIFIER_ERROR_LOCUS (DECL_ASSEMBLER_NAME (newdecl)) != current_class_type) + else { error ("conflicting declaration '%#D'", newdecl); cp_error_at ("'%D' has a previous declaration as `%#D'", olddecl, olddecl); return NULL_TREE; } } else if (TREE_CODE (newdecl) == FUNCTION_DECL && ((DECL_TEMPLATE_SPECIALIZATION (olddecl) && (!DECL_TEMPLATE_INFO (newdecl) || (DECL_TI_TEMPLATE (newdecl) != DECL_TI_TEMPLATE (olddecl)))) || (DECL_TEMPLATE_SPECIALIZATION (newdecl) && (!DECL_TEMPLATE_INFO (olddecl) || (DECL_TI_TEMPLATE (olddecl) != DECL_TI_TEMPLATE (newdecl)))))) /* It's OK to have a template specialization and a non-template with the same type, or to have specializations of two different templates with the same type. Note that if one is a specialization, and the other is an instantiation of the same template, that we do not exit at this point. That situation can occur if we instantiate a template class, and then specialize one of its methods. This situation is valid, but the declarations must be merged in the usual way. */ return NULL_TREE; else if (TREE_CODE (newdecl) == FUNCTION_DECL && ((DECL_TEMPLATE_INSTANTIATION (olddecl) && !DECL_USE_TEMPLATE (newdecl)) || (DECL_TEMPLATE_INSTANTIATION (newdecl) && !DECL_USE_TEMPLATE (olddecl)))) /* One of the declarations is a template instantiation, and the other is not a template at all. That's OK. */ return NULL_TREE; else if (TREE_CODE (newdecl) == NAMESPACE_DECL) { /* In [namespace.alias] we have: In a declarative region, a namespace-alias-definition can be used to redefine a namespace-alias declared in that declarative region to refer only to the namespace to which it already refers. Therefore, if we encounter a second alias directive for the same alias, we can just ignore the second directive. */ if (DECL_NAMESPACE_ALIAS (newdecl) && (DECL_NAMESPACE_ALIAS (newdecl) == DECL_NAMESPACE_ALIAS (olddecl))) return olddecl; /* [namespace.alias] A namespace-name or namespace-alias shall not be declared as the name of any other entity in the same declarative region. A namespace-name defined at global scope shall not be declared as the name of any other entity in any glogal scope of the program. */ error ("declaration of `namespace %D' conflicts with", newdecl); cp_error_at ("previous declaration of `namespace %D' here", olddecl); return error_mark_node; } else { const char *errmsg = redeclaration_error_message (newdecl, olddecl); if (errmsg) { error (errmsg, newdecl); if (DECL_NAME (olddecl) != NULL_TREE) cp_error_at ((DECL_INITIAL (olddecl) && namespace_bindings_p ()) ? "`%#D' previously defined here" : "`%#D' previously declared here", olddecl); return error_mark_node; } else if (TREE_CODE (olddecl) == FUNCTION_DECL && DECL_INITIAL (olddecl) != NULL_TREE && TYPE_ARG_TYPES (TREE_TYPE (olddecl)) == NULL_TREE && TYPE_ARG_TYPES (TREE_TYPE (newdecl)) != NULL_TREE) { /* Prototype decl follows defn w/o prototype. */ cp_warning_at ("prototype for `%#D'", newdecl); warning ("%Jfollows non-prototype definition here", olddecl); } else if (TREE_CODE (olddecl) == FUNCTION_DECL && DECL_LANGUAGE (newdecl) != DECL_LANGUAGE (olddecl)) { /* extern "C" int foo (); int foo () { bar (); } is OK. */ if (current_lang_depth () == 0) SET_DECL_LANGUAGE (newdecl, DECL_LANGUAGE (olddecl)); else { cp_error_at ("previous declaration of `%#D' with %L linkage", olddecl, DECL_LANGUAGE (olddecl)); error ("conflicts with new declaration with %L linkage", DECL_LANGUAGE (newdecl)); } } if (DECL_LANG_SPECIFIC (olddecl) && DECL_USE_TEMPLATE (olddecl)) ; else if (TREE_CODE (olddecl) == FUNCTION_DECL) { tree t1 = TYPE_ARG_TYPES (TREE_TYPE (olddecl)); tree t2 = TYPE_ARG_TYPES (TREE_TYPE (newdecl)); int i = 1; if (TREE_CODE (TREE_TYPE (newdecl)) == METHOD_TYPE) t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2); for (; t1 && t1 != void_list_node; t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2), i++) if (TREE_PURPOSE (t1) && TREE_PURPOSE (t2)) { if (1 == simple_cst_equal (TREE_PURPOSE (t1), TREE_PURPOSE (t2))) { pedwarn ("default argument given for parameter %d of `%#D'", i, newdecl); cp_pedwarn_at ("after previous specification in `%#D'", olddecl); } else { error ("default argument given for parameter %d of `%#D'", i, newdecl); cp_error_at ("after previous specification in `%#D'", olddecl); } } if (DECL_DECLARED_INLINE_P (newdecl) && ! DECL_DECLARED_INLINE_P (olddecl) && TREE_ADDRESSABLE (olddecl) && warn_inline) { warning ("`%#D' was used before it was declared inline", newdecl); warning ("%Jprevious non-inline declaration here", olddecl); } } } /* Do not merge an implicit typedef with an explicit one. In: class A; ... typedef class A A __attribute__ ((foo)); the attribute should apply only to the typedef. */ if (TREE_CODE (olddecl) == TYPE_DECL && (DECL_IMPLICIT_TYPEDEF_P (olddecl) || DECL_IMPLICIT_TYPEDEF_P (newdecl))) return NULL_TREE; /* If new decl is `static' and an `extern' was seen previously, warn about it. */ warn_extern_redeclared_static (newdecl, olddecl); /* We have committed to returning 1 at this point. */ if (TREE_CODE (newdecl) == FUNCTION_DECL) { /* Now that functions must hold information normally held by field decls, there is extra work to do so that declaration information does not get destroyed during definition. */ if (DECL_VINDEX (olddecl)) DECL_VINDEX (newdecl) = DECL_VINDEX (olddecl); if (DECL_CONTEXT (olddecl)) DECL_CONTEXT (newdecl) = DECL_CONTEXT (olddecl); DECL_STATIC_CONSTRUCTOR (newdecl) |= DECL_STATIC_CONSTRUCTOR (olddecl); DECL_STATIC_DESTRUCTOR (newdecl) |= DECL_STATIC_DESTRUCTOR (olddecl); DECL_PURE_VIRTUAL_P (newdecl) |= DECL_PURE_VIRTUAL_P (olddecl); DECL_VIRTUAL_P (newdecl) |= DECL_VIRTUAL_P (olddecl); DECL_NEEDS_FINAL_OVERRIDER_P (newdecl) |= DECL_NEEDS_FINAL_OVERRIDER_P (olddecl); DECL_THIS_STATIC (newdecl) |= DECL_THIS_STATIC (olddecl); if (DECL_OVERLOADED_OPERATOR_P (olddecl) != ERROR_MARK) SET_OVERLOADED_OPERATOR_CODE (newdecl, DECL_OVERLOADED_OPERATOR_P (olddecl)); new_defines_function = DECL_INITIAL (newdecl) != NULL_TREE; /* Optionally warn about more than one declaration for the same name, but don't warn about a function declaration followed by a definition. */ if (warn_redundant_decls && ! DECL_ARTIFICIAL (olddecl) && !(new_defines_function && DECL_INITIAL (olddecl) == NULL_TREE) /* Don't warn about extern decl followed by definition. */ && !(DECL_EXTERNAL (olddecl) && ! DECL_EXTERNAL (newdecl)) /* Don't warn about friends, let add_friend take care of it. */ && ! (DECL_FRIEND_P (newdecl) || DECL_FRIEND_P (olddecl))) { warning ("redundant redeclaration of `%D' in same scope", newdecl); cp_warning_at ("previous declaration of `%D'", olddecl); } } /* Deal with C++: must preserve virtual function table size. */ if (TREE_CODE (olddecl) == TYPE_DECL) { tree newtype = TREE_TYPE (newdecl); tree oldtype = TREE_TYPE (olddecl); if (newtype != error_mark_node && oldtype != error_mark_node && TYPE_LANG_SPECIFIC (newtype) && TYPE_LANG_SPECIFIC (oldtype)) CLASSTYPE_FRIEND_CLASSES (newtype) = CLASSTYPE_FRIEND_CLASSES (oldtype); DECL_ORIGINAL_TYPE (newdecl) = DECL_ORIGINAL_TYPE (olddecl); } /* Copy all the DECL_... slots specified in the new decl except for any that we copy here from the old type. */ DECL_ATTRIBUTES (newdecl) = (*targetm.merge_decl_attributes) (olddecl, newdecl); if (TREE_CODE (newdecl) == TEMPLATE_DECL) { TREE_TYPE (olddecl) = TREE_TYPE (DECL_TEMPLATE_RESULT (olddecl)); DECL_TEMPLATE_SPECIALIZATIONS (olddecl) = chainon (DECL_TEMPLATE_SPECIALIZATIONS (olddecl), DECL_TEMPLATE_SPECIALIZATIONS (newdecl)); /* If the new declaration is a definition, update the file and line information on the declaration. */ if (DECL_INITIAL (DECL_TEMPLATE_RESULT (olddecl)) == NULL_TREE && DECL_INITIAL (DECL_TEMPLATE_RESULT (newdecl)) != NULL_TREE) { DECL_SOURCE_LOCATION (olddecl) = DECL_SOURCE_LOCATION (DECL_TEMPLATE_RESULT (olddecl)) = DECL_SOURCE_LOCATION (newdecl); if (DECL_FUNCTION_TEMPLATE_P (newdecl)) DECL_ARGUMENTS (DECL_TEMPLATE_RESULT (olddecl)) = DECL_ARGUMENTS (DECL_TEMPLATE_RESULT (newdecl)); } if (DECL_FUNCTION_TEMPLATE_P (newdecl)) { DECL_INLINE (DECL_TEMPLATE_RESULT (olddecl)) |= DECL_INLINE (DECL_TEMPLATE_RESULT (newdecl)); DECL_DECLARED_INLINE_P (DECL_TEMPLATE_RESULT (olddecl)) |= DECL_DECLARED_INLINE_P (DECL_TEMPLATE_RESULT (newdecl)); } return olddecl; } if (types_match) { /* Automatically handles default parameters. */ tree oldtype = TREE_TYPE (olddecl); tree newtype; /* Merge the data types specified in the two decls. */ newtype = merge_types (TREE_TYPE (newdecl), TREE_TYPE (olddecl)); /* If merge_types produces a non-typedef type, just use the old type. */ if (TREE_CODE (newdecl) == TYPE_DECL && newtype == DECL_ORIGINAL_TYPE (newdecl)) newtype = oldtype; if (TREE_CODE (newdecl) == VAR_DECL) { DECL_THIS_EXTERN (newdecl) |= DECL_THIS_EXTERN (olddecl); DECL_INITIALIZED_P (newdecl) |= DECL_INITIALIZED_P (olddecl); DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (newdecl) |= DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (olddecl); } /* Do this after calling `merge_types' so that default parameters don't confuse us. */ else if (TREE_CODE (newdecl) == FUNCTION_DECL && (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (newdecl)) != TYPE_RAISES_EXCEPTIONS (TREE_TYPE (olddecl)))) { TREE_TYPE (newdecl) = build_exception_variant (newtype, TYPE_RAISES_EXCEPTIONS (TREE_TYPE (newdecl))); TREE_TYPE (olddecl) = build_exception_variant (newtype, TYPE_RAISES_EXCEPTIONS (oldtype)); if ((pedantic || ! DECL_IN_SYSTEM_HEADER (olddecl)) && DECL_SOURCE_LINE (olddecl) != 0 && flag_exceptions && !comp_except_specs (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (newdecl)), TYPE_RAISES_EXCEPTIONS (TREE_TYPE (olddecl)), 1)) { error ("declaration of `%F' throws different exceptions", newdecl); cp_error_at ("than previous declaration `%F'", olddecl); } } TREE_TYPE (newdecl) = TREE_TYPE (olddecl) = newtype; /* Lay the type out, unless already done. */ if (! same_type_p (newtype, oldtype) && TREE_TYPE (newdecl) != error_mark_node && !(processing_template_decl && uses_template_parms (newdecl))) layout_type (TREE_TYPE (newdecl)); if ((TREE_CODE (newdecl) == VAR_DECL || TREE_CODE (newdecl) == PARM_DECL || TREE_CODE (newdecl) == RESULT_DECL || TREE_CODE (newdecl) == FIELD_DECL || TREE_CODE (newdecl) == TYPE_DECL) && !(processing_template_decl && uses_template_parms (newdecl))) layout_decl (newdecl, 0); /* Merge the type qualifiers. */ if (TREE_READONLY (newdecl)) TREE_READONLY (olddecl) = 1; if (TREE_THIS_VOLATILE (newdecl)) TREE_THIS_VOLATILE (olddecl) = 1; /* Merge the initialization information. */ if (DECL_INITIAL (newdecl) == NULL_TREE && DECL_INITIAL (olddecl) != NULL_TREE) { DECL_INITIAL (newdecl) = DECL_INITIAL (olddecl); DECL_SOURCE_LOCATION (newdecl) = DECL_SOURCE_LOCATION (olddecl); if (CAN_HAVE_FULL_LANG_DECL_P (newdecl) && DECL_LANG_SPECIFIC (newdecl) && DECL_LANG_SPECIFIC (olddecl)) { DECL_SAVED_TREE (newdecl) = DECL_SAVED_TREE (olddecl); DECL_SAVED_INSNS (newdecl) = DECL_SAVED_INSNS (olddecl); } } /* Merge the section attribute. We want to issue an error if the sections conflict but that must be done later in decl_attributes since we are called before attributes are assigned. */ if (DECL_SECTION_NAME (newdecl) == NULL_TREE) DECL_SECTION_NAME (newdecl) = DECL_SECTION_NAME (olddecl); if (TREE_CODE (newdecl) == FUNCTION_DECL) { DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (newdecl) |= DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (olddecl); DECL_NO_LIMIT_STACK (newdecl) |= DECL_NO_LIMIT_STACK (olddecl); TREE_THIS_VOLATILE (newdecl) |= TREE_THIS_VOLATILE (olddecl); TREE_READONLY (newdecl) |= TREE_READONLY (olddecl); TREE_NOTHROW (newdecl) |= TREE_NOTHROW (olddecl); DECL_IS_MALLOC (newdecl) |= DECL_IS_MALLOC (olddecl); DECL_IS_PURE (newdecl) |= DECL_IS_PURE (olddecl); /* Keep the old RTL. */ COPY_DECL_RTL (olddecl, newdecl); } else if (TREE_CODE (newdecl) == VAR_DECL && (DECL_SIZE (olddecl) || !DECL_SIZE (newdecl))) { /* Keep the old RTL. We cannot keep the old RTL if the old declaration was for an incomplete object and the new declaration is not since many attributes of the RTL will change. */ COPY_DECL_RTL (olddecl, newdecl); } } /* If cannot merge, then use the new type and qualifiers, and don't preserve the old rtl. */ else { /* Clean out any memory we had of the old declaration. */ tree oldstatic = value_member (olddecl, static_aggregates); if (oldstatic) TREE_VALUE (oldstatic) = error_mark_node; TREE_TYPE (olddecl) = TREE_TYPE (newdecl); TREE_READONLY (olddecl) = TREE_READONLY (newdecl); TREE_THIS_VOLATILE (olddecl) = TREE_THIS_VOLATILE (newdecl); TREE_SIDE_EFFECTS (olddecl) = TREE_SIDE_EFFECTS (newdecl); } /* Merge the storage class information. */ merge_weak (newdecl, olddecl); DECL_ONE_ONLY (newdecl) |= DECL_ONE_ONLY (olddecl); DECL_DEFER_OUTPUT (newdecl) |= DECL_DEFER_OUTPUT (olddecl); TREE_PUBLIC (newdecl) = TREE_PUBLIC (olddecl); TREE_STATIC (olddecl) = TREE_STATIC (newdecl) |= TREE_STATIC (olddecl); if (! DECL_EXTERNAL (olddecl)) DECL_EXTERNAL (newdecl) = 0; if (DECL_LANG_SPECIFIC (newdecl) && DECL_LANG_SPECIFIC (olddecl)) { DECL_INTERFACE_KNOWN (newdecl) |= DECL_INTERFACE_KNOWN (olddecl); DECL_NOT_REALLY_EXTERN (newdecl) |= DECL_NOT_REALLY_EXTERN (olddecl); DECL_COMDAT (newdecl) |= DECL_COMDAT (olddecl); DECL_TEMPLATE_INSTANTIATED (newdecl) |= DECL_TEMPLATE_INSTANTIATED (olddecl); /* Don't really know how much of the language-specific values we should copy from old to new. */ DECL_IN_AGGR_P (newdecl) = DECL_IN_AGGR_P (olddecl); DECL_LANG_SPECIFIC (newdecl)->decl_flags.u2 = DECL_LANG_SPECIFIC (olddecl)->decl_flags.u2; DECL_NONCONVERTING_P (newdecl) = DECL_NONCONVERTING_P (olddecl); DECL_TEMPLATE_INFO (newdecl) = DECL_TEMPLATE_INFO (olddecl); DECL_INITIALIZED_IN_CLASS_P (newdecl) |= DECL_INITIALIZED_IN_CLASS_P (olddecl); olddecl_friend = DECL_FRIEND_P (olddecl); /* Only functions have DECL_BEFRIENDING_CLASSES. */ if (TREE_CODE (newdecl) == FUNCTION_DECL || DECL_FUNCTION_TEMPLATE_P (newdecl)) { DECL_BEFRIENDING_CLASSES (newdecl) = chainon (DECL_BEFRIENDING_CLASSES (newdecl), DECL_BEFRIENDING_CLASSES (olddecl)); /* DECL_THUNKS is only valid for virtual functions, otherwise it is a DECL_FRIEND_CONTEXT. */ if (DECL_VIRTUAL_P (newdecl)) DECL_THUNKS (newdecl) = DECL_THUNKS (olddecl); } } if (TREE_CODE (newdecl) == FUNCTION_DECL) { if (DECL_TEMPLATE_INSTANTIATION (olddecl) && !DECL_TEMPLATE_INSTANTIATION (newdecl)) { /* If newdecl is not a specialization, then it is not a template-related function at all. And that means that we should have exited above, returning 0. */ my_friendly_assert (DECL_TEMPLATE_SPECIALIZATION (newdecl), 0); if (TREE_USED (olddecl)) /* From [temp.expl.spec]: If a template, a member template or the member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs. */ error ("explicit specialization of %D after first use", olddecl); SET_DECL_TEMPLATE_SPECIALIZATION (olddecl); /* [temp.expl.spec/14] We don't inline explicit specialization just because the primary template says so. */ } else { if (DECL_PENDING_INLINE_INFO (newdecl) == 0) DECL_PENDING_INLINE_INFO (newdecl) = DECL_PENDING_INLINE_INFO (olddecl); DECL_DECLARED_INLINE_P (newdecl) |= DECL_DECLARED_INLINE_P (olddecl); /* If either decl says `inline', this fn is inline, unless its definition was passed already. */ if (DECL_INLINE (newdecl) && DECL_INITIAL (olddecl) == NULL_TREE) DECL_INLINE (olddecl) = 1; DECL_INLINE (newdecl) = DECL_INLINE (olddecl); DECL_UNINLINABLE (newdecl) = DECL_UNINLINABLE (olddecl) = (DECL_UNINLINABLE (newdecl) || DECL_UNINLINABLE (olddecl)); } /* Preserve abstractness on cloned [cd]tors. */ DECL_ABSTRACT (newdecl) = DECL_ABSTRACT (olddecl); if (! types_match) { SET_DECL_LANGUAGE (olddecl, DECL_LANGUAGE (newdecl)); COPY_DECL_ASSEMBLER_NAME (newdecl, olddecl); SET_DECL_RTL (olddecl, DECL_RTL (newdecl)); } if (! types_match || new_defines_function) { /* These need to be copied so that the names are available. Note that if the types do match, we'll preserve inline info and other bits, but if not, we won't. */ DECL_ARGUMENTS (olddecl) = DECL_ARGUMENTS (newdecl); DECL_RESULT (olddecl) = DECL_RESULT (newdecl); } if (new_defines_function) /* If defining a function declared with other language linkage, use the previously declared language linkage. */ SET_DECL_LANGUAGE (newdecl, DECL_LANGUAGE (olddecl)); else if (types_match) { /* If redeclaring a builtin function, and not a definition, it stays built in. */ if (DECL_BUILT_IN (olddecl)) { DECL_BUILT_IN_CLASS (newdecl) = DECL_BUILT_IN_CLASS (olddecl); DECL_FUNCTION_CODE (newdecl) = DECL_FUNCTION_CODE (olddecl); /* If we're keeping the built-in definition, keep the rtl, regardless of declaration matches. */ SET_DECL_RTL (newdecl, DECL_RTL (olddecl)); } DECL_RESULT (newdecl) = DECL_RESULT (olddecl); /* Don't clear out the arguments if we're redefining a function. */ if (DECL_ARGUMENTS (olddecl)) DECL_ARGUMENTS (newdecl) = DECL_ARGUMENTS (olddecl); } } else if (TREE_CODE (newdecl) == NAMESPACE_DECL) NAMESPACE_LEVEL (newdecl) = NAMESPACE_LEVEL (olddecl); /* Now preserve various other info from the definition. */ TREE_ADDRESSABLE (newdecl) = TREE_ADDRESSABLE (olddecl); TREE_ASM_WRITTEN (newdecl) = TREE_ASM_WRITTEN (olddecl); DECL_COMMON (newdecl) = DECL_COMMON (olddecl); COPY_DECL_ASSEMBLER_NAME (olddecl, newdecl); /* If either declaration has a nondefault visibility, use it. */ if (DECL_VISIBILITY (olddecl) != VISIBILITY_DEFAULT) { if (DECL_VISIBILITY (newdecl) != VISIBILITY_DEFAULT && DECL_VISIBILITY (newdecl) != DECL_VISIBILITY (olddecl)) { warning ("%J'%D': visibility attribute ignored because it", newdecl, newdecl); warning ("%Jconflicts with previous declaration here", olddecl); } DECL_VISIBILITY (newdecl) = DECL_VISIBILITY (olddecl); } if (TREE_CODE (newdecl) == FUNCTION_DECL) { int function_size; function_size = sizeof (struct tree_decl); memcpy ((char *) olddecl + sizeof (struct tree_common), (char *) newdecl + sizeof (struct tree_common), function_size - sizeof (struct tree_common)); if (DECL_TEMPLATE_INSTANTIATION (newdecl)) /* If newdecl is a template instantiation, it is possible that the following sequence of events has occurred: o A friend function was declared in a class template. The class template was instantiated. o The instantiation of the friend declaration was recorded on the instantiation list, and is newdecl. o Later, however, instantiate_class_template called pushdecl on the newdecl to perform name injection. But, pushdecl in turn called duplicate_decls when it discovered that another declaration of a global function with the same name already existed. o Here, in duplicate_decls, we decided to clobber newdecl. If we're going to do that, we'd better make sure that olddecl, and not newdecl, is on the list of instantiations so that if we try to do the instantiation again we won't get the clobbered declaration. */ reregister_specialization (newdecl, DECL_TI_TEMPLATE (newdecl), olddecl); } else { memcpy ((char *) olddecl + sizeof (struct tree_common), (char *) newdecl + sizeof (struct tree_common), sizeof (struct tree_decl) - sizeof (struct tree_common) + TREE_CODE_LENGTH (TREE_CODE (newdecl)) * sizeof (char *)); } DECL_UID (olddecl) = olddecl_uid; if (olddecl_friend) DECL_FRIEND_P (olddecl) = 1; /* NEWDECL contains the merged attribute lists. Update OLDDECL to be the same. */ DECL_ATTRIBUTES (olddecl) = DECL_ATTRIBUTES (newdecl); /* If OLDDECL had its DECL_RTL instantiated, re-invoke make_decl_rtl so that encode_section_info has a chance to look at the new decl flags and attributes. */ if (DECL_RTL_SET_P (olddecl) && (TREE_CODE (olddecl) == FUNCTION_DECL || (TREE_CODE (olddecl) == VAR_DECL && TREE_STATIC (olddecl)))) make_decl_rtl (olddecl, NULL); return olddecl; } /* Generate an implicit declaration for identifier FUNCTIONID as a function of type int (). Print a warning if appropriate. */ tree implicitly_declare (tree functionid) { tree decl; /* We used to reuse an old implicit decl here, but this loses with inline functions because it can clobber the saved decl chains. */ decl = build_lang_decl (FUNCTION_DECL, functionid, default_function_type); DECL_EXTERNAL (decl) = 1; TREE_PUBLIC (decl) = 1; /* ISO standard says implicit declarations are in the innermost block. So we record the decl in the standard fashion. */ pushdecl (decl); rest_of_decl_compilation (decl, NULL, 0, 0); if (warn_implicit /* Only one warning per identifier. */ && IDENTIFIER_IMPLICIT_DECL (functionid) == NULL_TREE) { pedwarn ("implicit declaration of function `%#D'", decl); } SET_IDENTIFIER_IMPLICIT_DECL (functionid, decl); return decl; } /* Return zero if the declaration NEWDECL is valid when the declaration OLDDECL (assumed to be for the same name) has already been seen. Otherwise return an error message format string with a %s where the identifier should go. */ static const char * redeclaration_error_message (tree newdecl, tree olddecl) { if (TREE_CODE (newdecl) == TYPE_DECL) { /* Because C++ can put things into name space for free, constructs like "typedef struct foo { ... } foo" would look like an erroneous redeclaration. */ if (same_type_p (TREE_TYPE (newdecl), TREE_TYPE (olddecl))) return 0; else return "redefinition of `%#D'"; } else if (TREE_CODE (newdecl) == FUNCTION_DECL) { /* If this is a pure function, its olddecl will actually be the original initialization to `0' (which we force to call abort()). Don't complain about redefinition in this case. */ if (DECL_LANG_SPECIFIC (olddecl) && DECL_PURE_VIRTUAL_P (olddecl)) return 0; /* If both functions come from different namespaces, this is not a redeclaration - this is a conflict with a used function. */ if (DECL_NAMESPACE_SCOPE_P (olddecl) && DECL_CONTEXT (olddecl) != DECL_CONTEXT (newdecl) && ! decls_match (olddecl, newdecl)) return "`%D' conflicts with used function"; /* We'll complain about linkage mismatches in warn_extern_redeclared_static. */ /* Defining the same name twice is no good. */ if (DECL_INITIAL (olddecl) != NULL_TREE && DECL_INITIAL (newdecl) != NULL_TREE) { if (DECL_NAME (olddecl) == NULL_TREE) return "`%#D' not declared in class"; else return "redefinition of `%#D'"; } return 0; } else if (TREE_CODE (newdecl) == TEMPLATE_DECL) { tree nt, ot; if (TREE_CODE (DECL_TEMPLATE_RESULT (newdecl)) == TYPE_DECL) { if (COMPLETE_TYPE_P (TREE_TYPE (newdecl)) && COMPLETE_TYPE_P (TREE_TYPE (olddecl))) return "redefinition of `%#D'"; return NULL; } if (TREE_CODE (DECL_TEMPLATE_RESULT (newdecl)) != FUNCTION_DECL || (DECL_TEMPLATE_RESULT (newdecl) == DECL_TEMPLATE_RESULT (olddecl))) return NULL; nt = DECL_TEMPLATE_RESULT (newdecl); if (DECL_TEMPLATE_INFO (nt)) nt = DECL_TEMPLATE_RESULT (template_for_substitution (nt)); ot = DECL_TEMPLATE_RESULT (olddecl); if (DECL_TEMPLATE_INFO (ot)) ot = DECL_TEMPLATE_RESULT (template_for_substitution (ot)); if (DECL_INITIAL (nt) && DECL_INITIAL (ot)) return "redefinition of `%#D'"; return NULL; } else if (toplevel_bindings_p () || DECL_NAMESPACE_SCOPE_P (newdecl)) { /* Objects declared at top level: */ /* If at least one is a reference, it's ok. */ if (DECL_EXTERNAL (newdecl) || DECL_EXTERNAL (olddecl)) return 0; /* Reject two definitions. */ return "redefinition of `%#D'"; } else { /* Objects declared with block scope: */ /* Reject two definitions, and reject a definition together with an external reference. */ if (!(DECL_EXTERNAL (newdecl) && DECL_EXTERNAL (olddecl))) return "redeclaration of `%#D'"; return 0; } } /* Create a new label, named ID. */ static tree make_label_decl (tree id, int local_p) { tree decl; decl = build_decl (LABEL_DECL, id, void_type_node); DECL_CONTEXT (decl) = current_function_decl; DECL_MODE (decl) = VOIDmode; C_DECLARED_LABEL_FLAG (decl) = local_p; /* Say where one reference is to the label, for the sake of the error if it is not defined. */ DECL_SOURCE_LOCATION (decl) = input_location; /* Record the fact that this identifier is bound to this label. */ SET_IDENTIFIER_LABEL_VALUE (id, decl); return decl; } /* Record this label on the list of used labels so that we can check at the end of the function to see whether or not the label was actually defined, and so we can check when the label is defined whether this use is valid. */ static void use_label (tree decl) { if (named_label_uses == NULL || named_label_uses->names_in_scope != current_binding_level->names || named_label_uses->label_decl != decl) { struct named_label_use_list *new_ent; new_ent = ggc_alloc (sizeof (struct named_label_use_list)); new_ent->label_decl = decl; new_ent->names_in_scope = current_binding_level->names; new_ent->binding_level = current_binding_level; new_ent->o_goto_locus = input_location; new_ent->next = named_label_uses; named_label_uses = new_ent; } } /* Look for a label named ID in the current function. If one cannot be found, create one. (We keep track of used, but undefined, labels, and complain about them at the end of a function.) */ tree lookup_label (tree id) { tree decl; struct named_label_list *ent; timevar_push (TV_NAME_LOOKUP); /* You can't use labels at global scope. */ if (current_function_decl == NULL_TREE) { error ("label `%s' referenced outside of any function", IDENTIFIER_POINTER (id)); POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, NULL_TREE); } /* See if we've already got this label. */ decl = IDENTIFIER_LABEL_VALUE (id); if (decl != NULL_TREE && DECL_CONTEXT (decl) == current_function_decl) POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, decl); /* Record this label on the list of labels used in this function. We do this before calling make_label_decl so that we get the IDENTIFIER_LABEL_VALUE before the new label is declared. */ ent = ggc_alloc_cleared (sizeof (struct named_label_list)); ent->old_value = IDENTIFIER_LABEL_VALUE (id); ent->next = named_labels; named_labels = ent; /* We need a new label. */ decl = make_label_decl (id, /*local_p=*/0); /* Now fill in the information we didn't have before. */ ent->label_decl = decl; POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, decl); } /* Declare a local label named ID. */ tree declare_local_label (tree id) { tree decl; /* Add a new entry to the SHADOWED_LABELS list so that when we leave this scope we can restore the old value of IDENTIFIER_TYPE_VALUE. */ current_binding_level->shadowed_labels = tree_cons (IDENTIFIER_LABEL_VALUE (id), NULL_TREE, current_binding_level->shadowed_labels); /* Look for the label. */ decl = make_label_decl (id, /*local_p=*/1); /* Now fill in the information we didn't have before. */ TREE_VALUE (current_binding_level->shadowed_labels) = decl; return decl; } /* Returns nonzero if it is ill-formed to jump past the declaration of DECL. Returns 2 if it's also a real problem. */ static int decl_jump_unsafe (tree decl) { if (TREE_CODE (decl) != VAR_DECL || TREE_STATIC (decl)) return 0; if (DECL_INITIAL (decl) == NULL_TREE && pod_type_p (TREE_TYPE (decl))) return 0; /* This is really only important if we're crossing an initialization. The POD stuff is just pedantry; why should it matter if the class contains a field of pointer to member type? */ if (DECL_INITIAL (decl) || (TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl)))) return 2; return 1; } /* Check that a single previously seen jump to a newly defined label is OK. DECL is the LABEL_DECL or 0; LEVEL is the binding_level for the jump context; NAMES are the names in scope in LEVEL at the jump context; FILE and LINE are the source position of the jump or 0. */ static void check_previous_goto_1 (tree decl, struct cp_binding_level* level, tree names, const location_t *locus) { int identified = 0; int saw_eh = 0; struct cp_binding_level *b = current_binding_level; for (; b; b = b->level_chain) { tree new_decls = b->names; tree old_decls = (b == level ? names : NULL_TREE); for (; new_decls != old_decls; new_decls = TREE_CHAIN (new_decls)) { int problem = decl_jump_unsafe (new_decls); if (! problem) continue; if (! identified) { if (decl) pedwarn ("jump to label `%D'", decl); else pedwarn ("jump to case label"); if (locus) pedwarn ("%H from here", locus); identified = 1; } if (problem > 1) cp_error_at (" crosses initialization of `%#D'", new_decls); else cp_pedwarn_at (" enters scope of non-POD `%#D'", new_decls); } if (b == level) break; if ((b->kind == sk_try || b->kind == sk_catch) && ! saw_eh) { if (! identified) { if (decl) pedwarn ("jump to label `%D'", decl); else pedwarn ("jump to case label"); if (locus) pedwarn ("%H from here", locus); identified = 1; } if (b->kind == sk_try) error (" enters try block"); else error (" enters catch block"); saw_eh = 1; } } } static void check_previous_goto (struct named_label_use_list* use) { check_previous_goto_1 (use->label_decl, use->binding_level, use->names_in_scope, &use->o_goto_locus); } static void check_switch_goto (struct cp_binding_level* level) { check_previous_goto_1 (NULL_TREE, level, level->names, NULL); } /* Check that any previously seen jumps to a newly defined label DECL are OK. Called by define_label. */ static void check_previous_gotos (tree decl) { struct named_label_use_list **usep; if (! TREE_USED (decl)) return; for (usep = &named_label_uses; *usep; ) { struct named_label_use_list *use = *usep; if (use->label_decl == decl) { check_previous_goto (use); *usep = use->next; } else usep = &(use->next); } } /* Check that a new jump to a label DECL is OK. Called by finish_goto_stmt. */ void check_goto (tree decl) { int identified = 0; tree bad; struct named_label_list *lab; /* We can't know where a computed goto is jumping. So we assume that it's OK. */ if (! DECL_P (decl)) return; /* If the label hasn't been defined yet, defer checking. */ if (! DECL_INITIAL (decl)) { use_label (decl); return; } for (lab = named_labels; lab; lab = lab->next) if (decl == lab->label_decl) break; /* If the label is not on named_labels it's a gcc local label, so it must be in an outer scope, so jumping to it is always OK. */ if (lab == 0) return; if ((lab->in_try_scope || lab->in_catch_scope || lab->bad_decls) && !identified) { cp_pedwarn_at ("jump to label `%D'", decl); pedwarn (" from here"); identified = 1; } for (bad = lab->bad_decls; bad; bad = TREE_CHAIN (bad)) { tree b = TREE_VALUE (bad); int u = decl_jump_unsafe (b); if (u > 1 && DECL_ARTIFICIAL (b)) /* Can't skip init of __exception_info. */ error ("%J enters catch block", b); else if (u > 1) cp_error_at (" skips initialization of `%#D'", b); else cp_pedwarn_at (" enters scope of non-POD `%#D'", b); } if (lab->in_try_scope) error (" enters try block"); else if (lab->in_catch_scope) error (" enters catch block"); } /* Define a label, specifying the location in the source file. Return the LABEL_DECL node for the label. */ tree define_label (location_t location, tree name) { tree decl = lookup_label (name); struct named_label_list *ent; struct cp_binding_level *p; timevar_push (TV_NAME_LOOKUP); for (ent = named_labels; ent; ent = ent->next) if (ent->label_decl == decl) break; /* After labels, make any new cleanups in the function go into their own new (temporary) binding contour. */ for (p = current_binding_level; p->kind != sk_function_parms; p = p->level_chain) p->more_cleanups_ok = 0; if (name == get_identifier ("wchar_t")) pedwarn ("label named wchar_t"); if (DECL_INITIAL (decl) != NULL_TREE) error ("duplicate label `%D'", decl); else { /* Mark label as having been defined. */ DECL_INITIAL (decl) = error_mark_node; /* Say where in the source. */ DECL_SOURCE_LOCATION (decl) = location; if (ent) { ent->names_in_scope = current_binding_level->names; ent->binding_level = current_binding_level; } check_previous_gotos (decl); } timevar_pop (TV_NAME_LOOKUP); return decl; } struct cp_switch { struct cp_binding_level *level; struct cp_switch *next; /* The SWITCH_STMT being built. */ tree switch_stmt; /* A splay-tree mapping the low element of a case range to the high element, or NULL_TREE if there is no high element. Used to determine whether or not a new case label duplicates an old case label. We need a tree, rather than simply a hash table, because of the GNU case range extension. */ splay_tree cases; }; /* A stack of the currently active switch statements. The innermost switch statement is on the top of the stack. There is no need to mark the stack for garbage collection because it is only active during the processing of the body of a function, and we never collect at that point. */ static struct cp_switch *switch_stack; /* Called right after a switch-statement condition is parsed. SWITCH_STMT is the switch statement being parsed. */ void push_switch (tree switch_stmt) { struct cp_switch *p = xmalloc (sizeof (struct cp_switch)); p->level = current_binding_level; p->next = switch_stack; p->switch_stmt = switch_stmt; p->cases = splay_tree_new (case_compare, NULL, NULL); switch_stack = p; } void pop_switch (void) { struct cp_switch *cs; cs = switch_stack; splay_tree_delete (cs->cases); switch_stack = switch_stack->next; free (cs); } /* Note that we've seen a definition of a case label, and complain if this is a bad place for one. */ tree finish_case_label (tree low_value, tree high_value) { tree cond, r; struct cp_binding_level *p; if (processing_template_decl) { tree label; /* For templates, just add the case label; we'll do semantic analysis at instantiation-time. */ label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); return add_stmt (build_case_label (low_value, high_value, label)); } /* Find the condition on which this switch statement depends. */ cond = SWITCH_COND (switch_stack->switch_stmt); if (cond && TREE_CODE (cond) == TREE_LIST) cond = TREE_VALUE (cond); r = c_add_case_label (switch_stack->cases, cond, low_value, high_value); check_switch_goto (switch_stack->level); /* After labels, make any new cleanups in the function go into their own new (temporary) binding contour. */ for (p = current_binding_level; p->kind != sk_function_parms; p = p->level_chain) p->more_cleanups_ok = 0; return r; } /* Hash a TYPENAME_TYPE. K is really of type `tree'. */ static hashval_t typename_hash (const void* k) { hashval_t hash; tree t = (tree) k; hash = (htab_hash_pointer (TYPE_CONTEXT (t)) ^ htab_hash_pointer (DECL_NAME (TYPE_NAME (t)))); return hash; } /* Compare two TYPENAME_TYPEs. K1 and K2 are really of type `tree'. */ static int typename_compare (const void * k1, const void * k2) { tree t1; tree t2; tree d1; tree d2; t1 = (tree) k1; t2 = (tree) k2; d1 = TYPE_NAME (t1); d2 = TYPE_NAME (t2); return (DECL_NAME (d1) == DECL_NAME (d2) && TYPE_CONTEXT (t1) == TYPE_CONTEXT (t2) && ((TREE_TYPE (t1) != NULL_TREE) == (TREE_TYPE (t2) != NULL_TREE)) && same_type_p (TREE_TYPE (t1), TREE_TYPE (t2)) && TYPENAME_TYPE_FULLNAME (t1) == TYPENAME_TYPE_FULLNAME (t2)); } /* Build a TYPENAME_TYPE. If the type is `typename T::t', CONTEXT is the type of `T', NAME is the IDENTIFIER_NODE for `t'. If BASE_TYPE is non-NULL, this type is being created by the implicit typename extension, and BASE_TYPE is a type named `t' in some base class of `T' which depends on template parameters. Returns the new TYPENAME_TYPE. */ static GTY ((param_is (union tree_node))) htab_t typename_htab; static tree build_typename_type (tree context, tree name, tree fullname) { tree t; tree d; void **e; if (typename_htab == NULL) { typename_htab = htab_create_ggc (61, &typename_hash, &typename_compare, NULL); } /* Build the TYPENAME_TYPE. */ t = make_aggr_type (TYPENAME_TYPE); TYPE_CONTEXT (t) = FROB_CONTEXT (context); TYPENAME_TYPE_FULLNAME (t) = fullname; /* Build the corresponding TYPE_DECL. */ d = build_decl (TYPE_DECL, name, t); TYPE_NAME (TREE_TYPE (d)) = d; TYPE_STUB_DECL (TREE_TYPE (d)) = d; DECL_CONTEXT (d) = FROB_CONTEXT (context); DECL_ARTIFICIAL (d) = 1; /* See if we already have this type. */ e = htab_find_slot (typename_htab, t, INSERT); if (*e) t = (tree) *e; else *e = t; return t; } /* Resolve `typename CONTEXT::NAME'. Returns an appropriate type, unless an error occurs, in which case error_mark_node is returned. If we locate a non-artificial TYPE_DECL and TF_KEEP_TYPE_DECL is set, we return that, rather than the _TYPE it corresponds to, in other cases we look through the type decl. If TF_ERROR is set, complain about errors, otherwise be quiet. */ tree make_typename_type (tree context, tree name, tsubst_flags_t complain) { tree fullname; if (name == error_mark_node || context == NULL_TREE || context == error_mark_node) return error_mark_node; if (TYPE_P (name)) { if (!(TYPE_LANG_SPECIFIC (name) && (CLASSTYPE_IS_TEMPLATE (name) || CLASSTYPE_USE_TEMPLATE (name)))) name = TYPE_IDENTIFIER (name); else /* Create a TEMPLATE_ID_EXPR for the type. */ name = build_nt (TEMPLATE_ID_EXPR, CLASSTYPE_TI_TEMPLATE (name), CLASSTYPE_TI_ARGS (name)); } else if (TREE_CODE (name) == TYPE_DECL) name = DECL_NAME (name); fullname = name; if (TREE_CODE (name) == TEMPLATE_ID_EXPR) { name = TREE_OPERAND (name, 0); if (TREE_CODE (name) == TEMPLATE_DECL) name = TREE_OPERAND (fullname, 0) = DECL_NAME (name); } if (TREE_CODE (name) == TEMPLATE_DECL) { error ("`%D' used without template parameters", name); return error_mark_node; } my_friendly_assert (TREE_CODE (name) == IDENTIFIER_NODE, 20030802); - - if (TREE_CODE (context) == NAMESPACE_DECL) - { - /* We can get here from typename_sub0 in the explicit_template_type - expansion. Just fail. */ - if (complain & tf_error) - error ("no class template named `%#T' in `%#T'", - name, context); - return error_mark_node; - } + my_friendly_assert (TYPE_P (context), 20050905); if (!dependent_type_p (context) || currently_open_class (context)) { if (TREE_CODE (fullname) == TEMPLATE_ID_EXPR) { tree tmpl = NULL_TREE; if (IS_AGGR_TYPE (context)) tmpl = lookup_field (context, name, 0, false); if (!tmpl || !DECL_CLASS_TEMPLATE_P (tmpl)) { if (complain & tf_error) error ("no class template named `%#T' in `%#T'", name, context); return error_mark_node; } if (complain & tf_error) perform_or_defer_access_check (TYPE_BINFO (context), tmpl); return lookup_template_class (tmpl, TREE_OPERAND (fullname, 1), NULL_TREE, context, /*entering_scope=*/0, tf_error | tf_warning | tf_user); } else { tree t; if (!IS_AGGR_TYPE (context)) { if (complain & tf_error) error ("no type named `%#T' in `%#T'", name, context); return error_mark_node; } t = lookup_field (context, name, 0, true); if (t) { if (TREE_CODE (t) != TYPE_DECL) { if (complain & tf_error) error ("no type named `%#T' in `%#T'", name, context); return error_mark_node; } if (complain & tf_error) perform_or_defer_access_check (TYPE_BINFO (context), t); if (DECL_ARTIFICIAL (t) || !(complain & tf_keep_type_decl)) t = TREE_TYPE (t); return t; } } } /* If the CONTEXT is not a template type, then either the field is there now or its never going to be. */ if (!dependent_type_p (context)) { if (complain & tf_error) error ("no type named `%#T' in `%#T'", name, context); return error_mark_node; } return build_typename_type (context, name, fullname); } /* Resolve `CONTEXT::template NAME'. Returns an appropriate type, unless an error occurs, in which case error_mark_node is returned. If we locate a TYPE_DECL, we return that, rather than the _TYPE it corresponds to. If COMPLAIN zero, don't complain about any errors that occur. */ tree make_unbound_class_template (tree context, tree name, tsubst_flags_t complain) { tree t; tree d; if (TYPE_P (name)) name = TYPE_IDENTIFIER (name); else if (DECL_P (name)) name = DECL_NAME (name); if (TREE_CODE (name) != IDENTIFIER_NODE) abort (); if (!dependent_type_p (context) || currently_open_class (context)) { tree tmpl = NULL_TREE; if (IS_AGGR_TYPE (context)) tmpl = lookup_field (context, name, 0, false); if (!tmpl || !DECL_CLASS_TEMPLATE_P (tmpl)) { if (complain & tf_error) error ("no class template named `%#T' in `%#T'", name, context); return error_mark_node; } if (complain & tf_error) perform_or_defer_access_check (TYPE_BINFO (context), tmpl); return tmpl; } /* Build the UNBOUND_CLASS_TEMPLATE. */ t = make_aggr_type (UNBOUND_CLASS_TEMPLATE); TYPE_CONTEXT (t) = FROB_CONTEXT (context); TREE_TYPE (t) = NULL_TREE; /* Build the corresponding TEMPLATE_DECL. */ d = build_decl (TEMPLATE_DECL, name, t); TYPE_NAME (TREE_TYPE (d)) = d; TYPE_STUB_DECL (TREE_TYPE (d)) = d; DECL_CONTEXT (d) = FROB_CONTEXT (context); DECL_ARTIFICIAL (d) = 1; return t; } /* A chain of TYPE_DECLs for the builtin types. */ static GTY(()) tree builtin_type_decls; /* Return a chain of TYPE_DECLs for the builtin types. */ tree cxx_builtin_type_decls (void) { return builtin_type_decls; } /* Push the declarations of builtin types into the namespace. RID_INDEX is the index of the builtin type in the array RID_POINTERS. NAME is the name used when looking up the builtin type. TYPE is the _TYPE node for the builtin type. */ void record_builtin_type (enum rid rid_index, const char* name, tree type) { tree rname = NULL_TREE, tname = NULL_TREE; tree tdecl = NULL_TREE; if ((int) rid_index < (int) RID_MAX) rname = ridpointers[(int) rid_index]; if (name) tname = get_identifier (name); /* The calls to SET_IDENTIFIER_GLOBAL_VALUE below should be eliminated. Built-in types should not be looked up name; their names are keywords that the parser can recognize. However, there is code in c-common.c that uses identifier_global_value to look up built-in types by name. */ if (tname) { tdecl = build_decl (TYPE_DECL, tname, type); DECL_ARTIFICIAL (tdecl) = 1; SET_IDENTIFIER_GLOBAL_VALUE (tname, tdecl); } if (rname) { if (!tdecl) { tdecl = build_decl (TYPE_DECL, rname, type); DECL_ARTIFICIAL (tdecl) = 1; } SET_IDENTIFIER_GLOBAL_VALUE (rname, tdecl); } if (!TYPE_NAME (type)) TYPE_NAME (type) = tdecl; if (tdecl) { TREE_CHAIN (tdecl) = builtin_type_decls; builtin_type_decls = tdecl; } } /* Record one of the standard Java types. * Declare it as having the given NAME. * If SIZE > 0, it is the size of one of the integral types; * otherwise it is the negative of the size of one of the other types. */ static tree record_builtin_java_type (const char* name, int size) { tree type, decl; if (size > 0) type = make_signed_type (size); else if (size > -32) { /* "__java_char" or ""__java_boolean". */ type = make_unsigned_type (-size); /*if (size == -1) TREE_SET_CODE (type, BOOLEAN_TYPE);*/ } else { /* "__java_float" or ""__java_double". */ type = make_node (REAL_TYPE); TYPE_PRECISION (type) = - size; layout_type (type); } record_builtin_type (RID_MAX, name, type); decl = TYPE_NAME (type); /* Suppress generate debug symbol entries for these types, since for normal C++ they are just clutter. However, push_lang_context undoes this if extern "Java" is seen. */ DECL_IGNORED_P (decl) = 1; TYPE_FOR_JAVA (type) = 1; return type; } /* Push a type into the namespace so that the back-ends ignore it. */ static void record_unknown_type (tree type, const char* name) { tree decl = pushdecl (build_decl (TYPE_DECL, get_identifier (name), type)); /* Make sure the "unknown type" typedecl gets ignored for debug info. */ DECL_IGNORED_P (decl) = 1; TYPE_DECL_SUPPRESS_DEBUG (decl) = 1; TYPE_SIZE (type) = TYPE_SIZE (void_type_node); TYPE_ALIGN (type) = 1; TYPE_USER_ALIGN (type) = 0; TYPE_MODE (type) = TYPE_MODE (void_type_node); } /* An string for which we should create an IDENTIFIER_NODE at startup. */ typedef struct predefined_identifier { /* The name of the identifier. */ const char *const name; /* The place where the IDENTIFIER_NODE should be stored. */ tree *const node; /* Nonzero if this is the name of a constructor or destructor. */ const int ctor_or_dtor_p; } predefined_identifier; /* Create all the predefined identifiers. */ static void initialize_predefined_identifiers (void) { const predefined_identifier *pid; /* A table of identifiers to create at startup. */ static const predefined_identifier predefined_identifiers[] = { { "C++", &lang_name_cplusplus, 0 }, { "C", &lang_name_c, 0 }, { "Java", &lang_name_java, 0 }, { CTOR_NAME, &ctor_identifier, 1 }, { "__base_ctor", &base_ctor_identifier, 1 }, { "__comp_ctor", &complete_ctor_identifier, 1 }, { DTOR_NAME, &dtor_identifier, 1 }, { "__comp_dtor", &complete_dtor_identifier, 1 }, { "__base_dtor", &base_dtor_identifier, 1 }, { "__deleting_dtor", &deleting_dtor_identifier, 1 }, { IN_CHARGE_NAME, &in_charge_identifier, 0 }, { "nelts", &nelts_identifier, 0 }, { THIS_NAME, &this_identifier, 0 }, { VTABLE_DELTA_NAME, &delta_identifier, 0 }, { VTABLE_PFN_NAME, &pfn_identifier, 0 }, { "_vptr", &vptr_identifier, 0 }, { "__vtt_parm", &vtt_parm_identifier, 0 }, { "::", &global_scope_name, 0 }, { "std", &std_identifier, 0 }, { NULL, NULL, 0 } }; for (pid = predefined_identifiers; pid->name; ++pid) { *pid->node = get_identifier (pid->name); if (pid->ctor_or_dtor_p) IDENTIFIER_CTOR_OR_DTOR_P (*pid->node) = 1; } } /* Create the predefined scalar types of C, and some nodes representing standard constants (0, 1, (void *)0). Initialize the global binding level. Make definitions for built-in primitive functions. */ void cxx_init_decl_processing (void) { tree void_ftype; tree void_ftype_ptr; /* Create all the identifiers we need. */ initialize_predefined_identifiers (); /* Fill in back-end hooks. */ lang_missing_noreturn_ok_p = &cp_missing_noreturn_ok_p; /* Create the global variables. */ push_to_top_level (); current_function_decl = NULL_TREE; current_binding_level = NULL; /* Enter the global namespace. */ my_friendly_assert (global_namespace == NULL_TREE, 375); global_namespace = build_lang_decl (NAMESPACE_DECL, global_scope_name, void_type_node); begin_scope (sk_namespace, global_namespace); current_lang_name = NULL_TREE; /* Adjust various flags based on command-line settings. */ if (!flag_permissive) flag_pedantic_errors = 1; if (!flag_no_inline) { flag_inline_trees = 1; flag_no_inline = 1; } if (flag_inline_functions) { flag_inline_trees = 2; flag_inline_functions = 0; } /* Force minimum function alignment if using the least significant bit of function pointers to store the virtual bit. */ if (TARGET_PTRMEMFUNC_VBIT_LOCATION == ptrmemfunc_vbit_in_pfn && force_align_functions_log < 1) force_align_functions_log = 1; /* Initially, C. */ current_lang_name = lang_name_c; build_common_tree_nodes (flag_signed_char); error_mark_list = build_tree_list (error_mark_node, error_mark_node); TREE_TYPE (error_mark_list) = error_mark_node; /* Create the `std' namespace. */ push_namespace (std_identifier); std_node = current_namespace; pop_namespace (); c_common_nodes_and_builtins (); java_byte_type_node = record_builtin_java_type ("__java_byte", 8); java_short_type_node = record_builtin_java_type ("__java_short", 16); java_int_type_node = record_builtin_java_type ("__java_int", 32); java_long_type_node = record_builtin_java_type ("__java_long", 64); java_float_type_node = record_builtin_java_type ("__java_float", -32); java_double_type_node = record_builtin_java_type ("__java_double", -64); java_char_type_node = record_builtin_java_type ("__java_char", -16); java_boolean_type_node = record_builtin_java_type ("__java_boolean", -1); integer_two_node = build_int_2 (2, 0); TREE_TYPE (integer_two_node) = integer_type_node; integer_three_node = build_int_2 (3, 0); TREE_TYPE (integer_three_node) = integer_type_node; record_builtin_type (RID_BOOL, "bool", boolean_type_node); truthvalue_type_node = boolean_type_node; truthvalue_false_node = boolean_false_node; truthvalue_true_node = boolean_true_node; empty_except_spec = build_tree_list (NULL_TREE, NULL_TREE); #if 0 record_builtin_type (RID_MAX, NULL, string_type_node); #endif delta_type_node = ptrdiff_type_node; vtable_index_type = ptrdiff_type_node; vtt_parm_type = build_pointer_type (const_ptr_type_node); void_ftype = build_function_type (void_type_node, void_list_node); void_ftype_ptr = build_function_type (void_type_node, tree_cons (NULL_TREE, ptr_type_node, void_list_node)); void_ftype_ptr = build_exception_variant (void_ftype_ptr, empty_except_spec); /* C++ extensions */ unknown_type_node = make_node (UNKNOWN_TYPE); record_unknown_type (unknown_type_node, "unknown type"); /* Indirecting an UNKNOWN_TYPE node yields an UNKNOWN_TYPE node. */ TREE_TYPE (unknown_type_node) = unknown_type_node; /* Looking up TYPE_POINTER_TO and TYPE_REFERENCE_TO yield the same result. */ TYPE_POINTER_TO (unknown_type_node) = unknown_type_node; TYPE_REFERENCE_TO (unknown_type_node) = unknown_type_node; { /* Make sure we get a unique function type, so we can give its pointer type a name. (This wins for gdb.) */ tree vfunc_type = make_node (FUNCTION_TYPE); TREE_TYPE (vfunc_type) = integer_type_node; TYPE_ARG_TYPES (vfunc_type) = NULL_TREE; layout_type (vfunc_type); vtable_entry_type = build_pointer_type (vfunc_type); } record_builtin_type (RID_MAX, VTBL_PTR_TYPE, vtable_entry_type); vtbl_type_node = build_cplus_array_type (vtable_entry_type, NULL_TREE); layout_type (vtbl_type_node); vtbl_type_node = build_qualified_type (vtbl_type_node, TYPE_QUAL_CONST); record_builtin_type (RID_MAX, NULL, vtbl_type_node); vtbl_ptr_type_node = build_pointer_type (vtable_entry_type); layout_type (vtbl_ptr_type_node); record_builtin_type (RID_MAX, NULL, vtbl_ptr_type_node); push_namespace (get_identifier ("__cxxabiv1")); abi_node = current_namespace; pop_namespace (); global_type_node = make_node (LANG_TYPE); record_unknown_type (global_type_node, "global type"); /* Now, C++. */ current_lang_name = lang_name_cplusplus; { tree bad_alloc_id; tree bad_alloc_type_node; tree bad_alloc_decl; tree newtype, deltype; tree ptr_ftype_sizetype; push_namespace (std_identifier); bad_alloc_id = get_identifier ("bad_alloc"); bad_alloc_type_node = make_aggr_type (RECORD_TYPE); TYPE_CONTEXT (bad_alloc_type_node) = current_namespace; bad_alloc_decl = create_implicit_typedef (bad_alloc_id, bad_alloc_type_node); DECL_CONTEXT (bad_alloc_decl) = current_namespace; TYPE_STUB_DECL (bad_alloc_type_node) = bad_alloc_decl; pop_namespace (); ptr_ftype_sizetype = build_function_type (ptr_type_node, tree_cons (NULL_TREE, size_type_node, void_list_node)); newtype = build_exception_variant (ptr_ftype_sizetype, add_exception_specifier (NULL_TREE, bad_alloc_type_node, -1)); deltype = build_exception_variant (void_ftype_ptr, empty_except_spec); push_cp_library_fn (NEW_EXPR, newtype); push_cp_library_fn (VEC_NEW_EXPR, newtype); global_delete_fndecl = push_cp_library_fn (DELETE_EXPR, deltype); push_cp_library_fn (VEC_DELETE_EXPR, deltype); } abort_fndecl = build_library_fn_ptr ("__cxa_pure_virtual", void_ftype); /* Perform other language dependent initializations. */ init_class_processing (); init_search_processing (); init_rtti_processing (); if (flag_exceptions) init_exception_processing (); if (! supports_one_only ()) flag_weak = 0; make_fname_decl = cp_make_fname_decl; start_fname_decls (); /* Show we use EH for cleanups. */ using_eh_for_cleanups (); /* Maintain consistency. Perhaps we should just complain if they say -fwritable-strings? */ if (flag_writable_strings) flag_const_strings = 0; } /* Generate an initializer for a function naming variable from NAME. NAME may be NULL, to indicate a dependent name. TYPE_P is filled in with the type of the init. */ tree cp_fname_init (const char* name, tree *type_p) { tree domain = NULL_TREE; tree type; tree init = NULL_TREE; size_t length = 0; if (name) { length = strlen (name); domain = build_index_type (size_int (length)); init = build_string (length + 1, name); } type = build_qualified_type (char_type_node, TYPE_QUAL_CONST); type = build_cplus_array_type (type, domain); *type_p = type; if (init) TREE_TYPE (init) = type; else init = error_mark_node; return init; } /* Create the VAR_DECL for __FUNCTION__ etc. ID is the name to give the decl, NAME is the initialization string and TYPE_DEP indicates whether NAME depended on the type of the function. We make use of that to detect __PRETTY_FUNCTION__ inside a template fn. This is being done lazily at the point of first use, so we musn't push the decl now. */ static tree cp_make_fname_decl (tree id, int type_dep) { const char *const name = (type_dep && processing_template_decl ? NULL : fname_as_string (type_dep)); tree type; tree init = cp_fname_init (name, &type); tree decl = build_decl (VAR_DECL, id, type); /* As we're using pushdecl_with_scope, we must set the context. */ DECL_CONTEXT (decl) = current_function_decl; DECL_PRETTY_FUNCTION_P (decl) = type_dep; TREE_STATIC (decl) = 1; TREE_READONLY (decl) = 1; DECL_ARTIFICIAL (decl) = 1; DECL_INITIAL (decl) = init; TREE_USED (decl) = 1; if (current_function_decl) { struct cp_binding_level *b = current_binding_level; while (b->level_chain->kind != sk_function_parms) b = b->level_chain; pushdecl_with_scope (decl, b); cp_finish_decl (decl, init, NULL_TREE, LOOKUP_ONLYCONVERTING); } else pushdecl_top_level_and_finish (decl, init); return decl; } /* Make a definition for a builtin function named NAME in the current namespace, whose data type is TYPE and whose context is CONTEXT. TYPE should be a function type with argument types. CLASS and CODE tell later passes how to compile calls to this function. See tree.h for possible values. If LIBNAME is nonzero, use that for DECL_ASSEMBLER_NAME, the name to be called if we can't opencode the function. If ATTRS is nonzero, use that for the function's attribute list. */ static tree builtin_function_1 (const char* name, tree type, tree context, int code, enum built_in_class class, const char* libname, tree attrs) { tree decl = build_library_fn_1 (get_identifier (name), ERROR_MARK, type); DECL_BUILT_IN_CLASS (decl) = class; DECL_FUNCTION_CODE (decl) = code; DECL_CONTEXT (decl) = context; pushdecl (decl); /* Since `pushdecl' relies on DECL_ASSEMBLER_NAME instead of DECL_NAME, we cannot change DECL_ASSEMBLER_NAME until we have installed this function in the namespace. */ if (libname) SET_DECL_ASSEMBLER_NAME (decl, get_identifier (libname)); make_decl_rtl (decl, NULL); /* Warn if a function in the namespace for users is used without an occasion to consider it declared. */ if (name[0] != '_' || name[1] != '_') DECL_ANTICIPATED (decl) = 1; /* Possibly apply some default attributes to this built-in function. */ if (attrs) decl_attributes (&decl, attrs, ATTR_FLAG_BUILT_IN); else decl_attributes (&decl, NULL_TREE, 0); return decl; } /* Entry point for the benefit of c_common_nodes_and_builtins. Make a definition for a builtin function named NAME and whose data type is TYPE. TYPE should be a function type with argument types. This function places the anticipated declaration in the global namespace and additionally in the std namespace if appropriate. CLASS and CODE tell later passes how to compile calls to this function. See tree.h for possible values. If LIBNAME is nonzero, use that for DECL_ASSEMBLER_NAME, the name to be called if we can't opencode the function. If ATTRS is nonzero, use that for the function's attribute list. */ tree builtin_function (const char* name, tree type, int code, enum built_in_class class, const char* libname, tree attrs) { /* All builtins that don't begin with an '_' should additionally go in the 'std' namespace. */ if (name[0] != '_') { push_namespace (std_identifier); builtin_function_1 (name, type, std_node, code, class, libname, attrs); pop_namespace (); } return builtin_function_1 (name, type, NULL_TREE, code, class, libname, attrs); } /* Generate a FUNCTION_DECL with the typical flags for a runtime library function. Not called directly. */ static tree build_library_fn_1 (tree name, enum tree_code operator_code, tree type) { tree fn = build_lang_decl (FUNCTION_DECL, name, type); DECL_EXTERNAL (fn) = 1; TREE_PUBLIC (fn) = 1; DECL_ARTIFICIAL (fn) = 1; TREE_NOTHROW (fn) = 1; SET_OVERLOADED_OPERATOR_CODE (fn, operator_code); SET_DECL_LANGUAGE (fn, lang_c); return fn; } /* Returns the _DECL for a library function with C linkage. We assume that such functions never throw; if this is incorrect, callers should unset TREE_NOTHROW. */ tree build_library_fn (tree name, tree type) { return build_library_fn_1 (name, ERROR_MARK, type); } /* Returns the _DECL for a library function with C++ linkage. */ static tree build_cp_library_fn (tree name, enum tree_code operator_code, tree type) { tree fn = build_library_fn_1 (name, operator_code, type); TREE_NOTHROW (fn) = TYPE_NOTHROW_P (type); DECL_CONTEXT (fn) = FROB_CONTEXT (current_namespace); SET_DECL_LANGUAGE (fn, lang_cplusplus); set_mangled_name_for_decl (fn); return fn; } /* Like build_library_fn, but takes a C string instead of an IDENTIFIER_NODE. */ tree build_library_fn_ptr (const char* name, tree type) { return build_library_fn (get_identifier (name), type); } /* Like build_cp_library_fn, but takes a C string instead of an IDENTIFIER_NODE. */ tree build_cp_library_fn_ptr (const char* name, tree type) { return build_cp_library_fn (get_identifier (name), ERROR_MARK, type); } /* Like build_library_fn, but also pushes the function so that we will be able to find it via IDENTIFIER_GLOBAL_VALUE. */ tree push_library_fn (tree name, tree type) { tree fn = build_library_fn (name, type); pushdecl_top_level (fn); return fn; } /* Like build_cp_library_fn, but also pushes the function so that it will be found by normal lookup. */ static tree push_cp_library_fn (enum tree_code operator_code, tree type) { tree fn = build_cp_library_fn (ansi_opname (operator_code), operator_code, type); pushdecl (fn); return fn; } /* Like push_library_fn, but takes a TREE_LIST of parm types rather than a FUNCTION_TYPE. */ tree push_void_library_fn (tree name, tree parmtypes) { tree type = build_function_type (void_type_node, parmtypes); return push_library_fn (name, type); } /* Like push_library_fn, but also note that this function throws and does not return. Used for __throw_foo and the like. */ tree push_throw_library_fn (tree name, tree type) { tree fn = push_library_fn (name, type); TREE_THIS_VOLATILE (fn) = 1; TREE_NOTHROW (fn) = 0; return fn; } /* When we call finish_struct for an anonymous union, we create default copy constructors and such. But, an anonymous union shouldn't have such things; this function undoes the damage to the anonymous union type T. (The reason that we create the synthesized methods is that we don't distinguish `union { int i; }' from `typedef union { int i; } U'. The first is an anonymous union; the second is just an ordinary union type.) */ void fixup_anonymous_aggr (tree t) { tree *q; /* Wipe out memory of synthesized methods. */ TYPE_HAS_CONSTRUCTOR (t) = 0; TYPE_HAS_DEFAULT_CONSTRUCTOR (t) = 0; TYPE_HAS_INIT_REF (t) = 0; TYPE_HAS_CONST_INIT_REF (t) = 0; TYPE_HAS_ASSIGN_REF (t) = 0; TYPE_HAS_CONST_ASSIGN_REF (t) = 0; /* Splice the implicitly generated functions out of the TYPE_METHODS list. */ q = &TYPE_METHODS (t); while (*q) { if (DECL_ARTIFICIAL (*q)) *q = TREE_CHAIN (*q); else q = &TREE_CHAIN (*q); } /* ISO C++ 9.5.3. Anonymous unions may not have function members. */ if (TYPE_METHODS (t)) error ("%Jan anonymous union cannot have function members", TYPE_MAIN_DECL (t)); /* Anonymous aggregates cannot have fields with ctors, dtors or complex assignment operators (because they cannot have these methods themselves). For anonymous unions this is already checked because they are not allowed in any union, otherwise we have to check it. */ if (TREE_CODE (t) != UNION_TYPE) { tree field, type; for (field = TYPE_FIELDS (t); field; field = TREE_CHAIN (field)) if (TREE_CODE (field) == FIELD_DECL) { type = TREE_TYPE (field); if (CLASS_TYPE_P (type)) { if (TYPE_NEEDS_CONSTRUCTING (type)) cp_error_at ("member %#D' with constructor not allowed in anonymous aggregate", field); if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type)) cp_error_at ("member %#D' with destructor not allowed in anonymous aggregate", field); if (TYPE_HAS_COMPLEX_ASSIGN_REF (type)) cp_error_at ("member %#D' with copy assignment operator not allowed in anonymous aggregate", field); } } } } /* Make sure that a declaration with no declarator is well-formed, i.e. just declares a tagged type or anonymous union. Returns the type declared; or NULL_TREE if none. */ tree check_tag_decl (tree declspecs) { int found_type = 0; int saw_friend = 0; int saw_typedef = 0; tree ob_modifier = NULL_TREE; tree link; /* If a class, struct, or enum type is declared by the DECLSPECS (i.e, if a class-specifier, enum-specifier, or non-typename elaborated-type-specifier appears in the DECLSPECS), DECLARED_TYPE is set to the corresponding type. */ tree declared_type = NULL_TREE; bool error_p = false; for (link = declspecs; link; link = TREE_CHAIN (link)) { tree value = TREE_VALUE (link); if (TYPE_P (value) || TREE_CODE (value) == TYPE_DECL || (TREE_CODE (value) == IDENTIFIER_NODE && is_typename_at_global_scope (value))) { ++found_type; if (found_type == 2 && TREE_CODE (value) == IDENTIFIER_NODE) { if (! in_system_header) pedwarn ("redeclaration of C++ built-in type `%T'", value); return NULL_TREE; } if (TYPE_P (value) && ((TREE_CODE (value) != TYPENAME_TYPE && IS_AGGR_TYPE (value)) || TREE_CODE (value) == ENUMERAL_TYPE)) { my_friendly_assert (TYPE_MAIN_DECL (value) != NULL_TREE, 261); declared_type = value; } } else if (value == ridpointers[(int) RID_TYPEDEF]) saw_typedef = 1; else if (value == ridpointers[(int) RID_FRIEND]) { if (current_class_type == NULL_TREE || current_scope () != current_class_type) ob_modifier = value; else saw_friend = 1; } else if (value == ridpointers[(int) RID_STATIC] || value == ridpointers[(int) RID_EXTERN] || value == ridpointers[(int) RID_AUTO] || value == ridpointers[(int) RID_REGISTER] || value == ridpointers[(int) RID_INLINE] || value == ridpointers[(int) RID_VIRTUAL] || value == ridpointers[(int) RID_CONST] || value == ridpointers[(int) RID_VOLATILE] || value == ridpointers[(int) RID_EXPLICIT] || value == ridpointers[(int) RID_THREAD]) ob_modifier = value; else if (value == error_mark_node) error_p = true; } if (found_type > 1) error ("multiple types in one declaration"); if (declared_type == NULL_TREE && ! saw_friend && !error_p) pedwarn ("declaration does not declare anything"); /* Check for an anonymous union. */ else if (declared_type && IS_AGGR_TYPE_CODE (TREE_CODE (declared_type)) && TYPE_ANONYMOUS_P (declared_type)) { /* 7/3 In a simple-declaration, the optional init-declarator-list can be omitted only when declaring a class (clause 9) or enumeration (7.2), that is, when the decl-specifier-seq contains either a class-specifier, an elaborated-type-specifier with a class-key (9.1), or an enum-specifier. In these cases and whenever a class-specifier or enum-specifier is present in the decl-specifier-seq, the identifiers in these specifiers are among the names being declared by the declaration (as class-name, enum-names, or enumerators, depending on the syntax). In such cases, and except for the declaration of an unnamed bit-field (9.6), the decl-specifier-seq shall introduce one or more names into the program, or shall redeclare a name introduced by a previous declaration. [Example: enum { }; // ill-formed typedef class { }; // ill-formed --end example] */ if (saw_typedef) { error ("missing type-name in typedef-declaration"); return NULL_TREE; } /* Anonymous unions are objects, so they can have specifiers. */; SET_ANON_AGGR_TYPE_P (declared_type); if (TREE_CODE (declared_type) != UNION_TYPE && pedantic && !in_system_header) pedwarn ("ISO C++ prohibits anonymous structs"); } else if (ob_modifier) { if (ob_modifier == ridpointers[(int) RID_INLINE] || ob_modifier == ridpointers[(int) RID_VIRTUAL]) error ("`%D' can only be specified for functions", ob_modifier); else if (ob_modifier == ridpointers[(int) RID_FRIEND]) error ("`%D' can only be specified inside a class", ob_modifier); else if (ob_modifier == ridpointers[(int) RID_EXPLICIT]) error ("`%D' can only be specified for constructors", ob_modifier); else error ("`%D' can only be specified for objects and functions", ob_modifier); } return declared_type; } /* Called when a declaration is seen that contains no names to declare. If its type is a reference to a structure, union or enum inherited from a containing scope, shadow that tag name for the current scope with a forward reference. If its type defines a new named structure or union or defines an enum, it is valid but we need not do anything here. Otherwise, it is an error. C++: may have to grok the declspecs to learn about static, complain for anonymous unions. Returns the TYPE declared -- or NULL_TREE if none. */ tree shadow_tag (tree declspecs) { tree t = check_tag_decl (declspecs); if (!t) return NULL_TREE; maybe_process_partial_specialization (t); /* This is where the variables in an anonymous union are declared. An anonymous union declaration looks like: union { ... } ; because there is no declarator after the union, the parser sends that declaration here. */ if (ANON_AGGR_TYPE_P (t)) { fixup_anonymous_aggr (t); if (TYPE_FIELDS (t)) { tree decl = grokdeclarator (NULL_TREE, declspecs, NORMAL, 0, NULL); finish_anon_union (decl); } } return t; } /* Decode a "typename", such as "int **", returning a ..._TYPE node. */ tree groktypename (tree typename) { tree specs, attrs; tree type; if (TREE_CODE (typename) != TREE_LIST) return typename; split_specs_attrs (TREE_PURPOSE (typename), &specs, &attrs); type = grokdeclarator (TREE_VALUE (typename), specs, TYPENAME, 0, &attrs); if (attrs) cplus_decl_attributes (&type, attrs, 0); return type; } /* Decode a declarator in an ordinary declaration or data definition. This is called as soon as the type information and variable name have been parsed, before parsing the initializer if any. Here we create the ..._DECL node, fill in its type, and put it on the list of decls for the current context. The ..._DECL node is returned as the value. Exception: for arrays where the length is not specified, the type is left null, to be filled in by `cp_finish_decl'. Function definitions do not come here; they go to start_function instead. However, external and forward declarations of functions do go through here. Structure field declarations are done by grokfield and not through here. */ tree start_decl (tree declarator, tree declspecs, int initialized, tree attributes, tree prefix_attributes) { tree decl; tree type, tem; tree context; /* This should only be done once on the top most decl. */ if (have_extern_spec) { declspecs = tree_cons (NULL_TREE, get_identifier ("extern"), declspecs); have_extern_spec = false; } /* An object declared as __attribute__((deprecated)) suppresses warnings of uses of other deprecated items. */ if (lookup_attribute ("deprecated", attributes)) deprecated_state = DEPRECATED_SUPPRESS; attributes = chainon (attributes, prefix_attributes); decl = grokdeclarator (declarator, declspecs, NORMAL, initialized, &attributes); deprecated_state = DEPRECATED_NORMAL; if (decl == NULL_TREE || TREE_CODE (decl) == VOID_TYPE) return error_mark_node; type = TREE_TYPE (decl); if (type == error_mark_node) return error_mark_node; context = DECL_CONTEXT (decl); if (initialized && context && TREE_CODE (context) == NAMESPACE_DECL && context != current_namespace && TREE_CODE (decl) == VAR_DECL) { /* When parsing the initializer, lookup should use the object's namespace. */ push_decl_namespace (context); } /* We are only interested in class contexts, later. */ if (context && TREE_CODE (context) == NAMESPACE_DECL) context = NULL_TREE; if (initialized) /* Is it valid for this decl to have an initializer at all? If not, set INITIALIZED to zero, which will indirectly tell `cp_finish_decl' to ignore the initializer once it is parsed. */ switch (TREE_CODE (decl)) { case TYPE_DECL: error ("typedef `%D' is initialized (use __typeof__ instead)", decl); initialized = 0; break; case FUNCTION_DECL: error ("function `%#D' is initialized like a variable", decl); initialized = 0; break; default: break; } if (initialized) { if (! toplevel_bindings_p () && DECL_EXTERNAL (decl)) warning ("declaration of `%#D' has `extern' and is initialized", decl); DECL_EXTERNAL (decl) = 0; if (toplevel_bindings_p ()) TREE_STATIC (decl) = 1; /* Tell `pushdecl' this is an initialized decl even though we don't yet have the initializer expression. Also tell `cp_finish_decl' it may store the real initializer. */ DECL_INITIAL (decl) = error_mark_node; } /* Set attributes here so if duplicate decl, will have proper attributes. */ cplus_decl_attributes (&decl, attributes, 0); /* If #pragma weak was used, mark the decl weak now. */ if (global_scope_p (current_binding_level)) maybe_apply_pragma_weak (decl); if (TREE_CODE (decl) == FUNCTION_DECL && DECL_DECLARED_INLINE_P (decl) && DECL_UNINLINABLE (decl) && lookup_attribute ("noinline", DECL_ATTRIBUTES (decl))) warning ("%Jinline function '%D' given attribute noinline", decl, decl); if (context && COMPLETE_TYPE_P (complete_type (context))) { push_nested_class (context); if (TREE_CODE (decl) == VAR_DECL) { tree field = lookup_field (context, DECL_NAME (decl), 0, false); if (field == NULL_TREE || TREE_CODE (field) != VAR_DECL) error ("`%#D' is not a static member of `%#T'", decl, context); else { if (DECL_CONTEXT (field) != context) { if (!same_type_p (DECL_CONTEXT (field), context)) pedwarn ("ISO C++ does not permit `%T::%D' to be defined as `%T::%D'", DECL_CONTEXT (field), DECL_NAME (decl), context, DECL_NAME (decl)); DECL_CONTEXT (decl) = DECL_CONTEXT (field); } /* Static data member are tricky; an in-class initialization still doesn't provide a definition, so the in-class declaration will have DECL_EXTERNAL set, but will have an initialization. Thus, duplicate_decls won't warn about this situation, and so we check here. */ if (DECL_INITIAL (decl) && DECL_INITIAL (field)) error ("duplicate initialization of %D", decl); if (duplicate_decls (decl, field)) decl = field; } } else { tree field = check_classfn (context, decl, processing_template_decl > template_class_depth (context)); if (field && duplicate_decls (decl, field)) decl = field; } /* cp_finish_decl sets DECL_EXTERNAL if DECL_IN_AGGR_P is set. */ DECL_IN_AGGR_P (decl) = 0; if ((DECL_LANG_SPECIFIC (decl) && DECL_USE_TEMPLATE (decl)) || CLASSTYPE_TEMPLATE_INSTANTIATION (context)) { SET_DECL_TEMPLATE_SPECIALIZATION (decl); /* [temp.expl.spec] An explicit specialization of a static data member of a template is a definition if the declaration includes an initializer; otherwise, it is a declaration. We check for processing_specialization so this only applies to the new specialization syntax. */ if (DECL_INITIAL (decl) == NULL_TREE && processing_specialization) DECL_EXTERNAL (decl) = 1; } if (DECL_EXTERNAL (decl) && ! DECL_TEMPLATE_SPECIALIZATION (decl)) pedwarn ("declaration of `%#D' outside of class is not definition", decl); } /* Enter this declaration into the symbol table. */ tem = maybe_push_decl (decl); if (processing_template_decl) tem = push_template_decl (tem); if (tem == error_mark_node) return error_mark_node; #if ! defined (ASM_OUTPUT_BSS) && ! defined (ASM_OUTPUT_ALIGNED_BSS) /* Tell the back-end to use or not use .common as appropriate. If we say -fconserve-space, we want this to save .data space, at the expense of wrong semantics. If we say -fno-conserve-space, we want this to produce errors about redefs; to do this we force variables into the data segment. */ DECL_COMMON (tem) = ((TREE_CODE (tem) != VAR_DECL || !DECL_THREAD_LOCAL (tem)) && (flag_conserve_space || ! TREE_PUBLIC (tem))); #endif if (! processing_template_decl) start_decl_1 (tem); return tem; } void start_decl_1 (tree decl) { tree type = TREE_TYPE (decl); int initialized = (DECL_INITIAL (decl) != NULL_TREE); if (type == error_mark_node) return; if (initialized) /* Is it valid for this decl to have an initializer at all? If not, set INITIALIZED to zero, which will indirectly tell `cp_finish_decl' to ignore the initializer once it is parsed. */ { /* Don't allow initializations for incomplete types except for arrays which might be completed by the initialization. */ if (COMPLETE_TYPE_P (complete_type (type))) ; /* A complete type is ok. */ else if (TREE_CODE (type) != ARRAY_TYPE) { error ("variable `%#D' has initializer but incomplete type", decl); initialized = 0; type = TREE_TYPE (decl) = error_mark_node; } else if (!COMPLETE_TYPE_P (complete_type (TREE_TYPE (type)))) { if (DECL_LANG_SPECIFIC (decl) && DECL_TEMPLATE_INFO (decl)) error ("elements of array `%#D' have incomplete type", decl); /* else we already gave an error in start_decl. */ initialized = 0; } } if (!initialized && TREE_CODE (decl) != TYPE_DECL && TREE_CODE (decl) != TEMPLATE_DECL && type != error_mark_node && IS_AGGR_TYPE (type) && ! DECL_EXTERNAL (decl)) { if ((! processing_template_decl || ! uses_template_parms (type)) && !COMPLETE_TYPE_P (complete_type (type))) { error ("aggregate `%#D' has incomplete type and cannot be defined", decl); /* Change the type so that assemble_variable will give DECL an rtl we can live with: (mem (const_int 0)). */ type = TREE_TYPE (decl) = error_mark_node; } else { /* If any base type in the hierarchy of TYPE needs a constructor, then we set initialized to 1. This way any nodes which are created for the purposes of initializing this aggregate will live as long as it does. This is necessary for global aggregates which do not have their initializers processed until the end of the file. */ initialized = TYPE_NEEDS_CONSTRUCTING (type); } } if (! initialized) DECL_INITIAL (decl) = NULL_TREE; /* Create a new scope to hold this declaration if necessary. Whether or not a new scope is necessary cannot be determined until after the type has been completed; if the type is a specialization of a class template it is not until after instantiation has occurred that TYPE_HAS_NONTRIVIAL_DESTRUCTOR will be set correctly. */ maybe_push_cleanup_level (type); } /* Handle initialization of references. DECL, TYPE, and INIT have the same meaning as in cp_finish_decl. *CLEANUP must be NULL on entry, but will be set to a new CLEANUP_STMT if a temporary is created that must be destroyed subsequently. Returns an initializer expression to use to initialize DECL, or NULL if the initialization can be performed statically. Quotes on semantics can be found in ARM 8.4.3. */ static tree grok_reference_init (tree decl, tree type, tree init, tree *cleanup) { tree tmp; if (init == NULL_TREE) { if ((DECL_LANG_SPECIFIC (decl) == 0 || DECL_IN_AGGR_P (decl) == 0) && ! DECL_THIS_EXTERN (decl)) error ("`%D' declared as reference but not initialized", decl); return NULL_TREE; } if (TREE_CODE (init) == CONSTRUCTOR) { error ("ISO C++ forbids use of initializer list to initialize reference `%D'", decl); return NULL_TREE; } if (TREE_CODE (init) == TREE_LIST) init = build_x_compound_expr_from_list (init, "initializer"); if (TREE_CODE (TREE_TYPE (init)) == REFERENCE_TYPE) init = convert_from_reference (init); if (TREE_CODE (TREE_TYPE (type)) != ARRAY_TYPE && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE) /* Note: default conversion is only called in very special cases. */ init = decay_conversion (init); /* Convert INIT to the reference type TYPE. This may involve the creation of a temporary, whose lifetime must be the same as that of the reference. If so, a DECL_STMT for the temporary will be added just after the DECL_STMT for DECL. That's why we don't set DECL_INITIAL for local references (instead assigning to them explicitly); we need to allow the temporary to be initialized first. */ tmp = initialize_reference (type, init, decl, cleanup); if (tmp == error_mark_node) return NULL_TREE; else if (tmp == NULL_TREE) { error ("cannot initialize `%T' from `%T'", type, TREE_TYPE (init)); return NULL_TREE; } if (TREE_STATIC (decl) && !TREE_CONSTANT (tmp)) return tmp; DECL_INITIAL (decl) = tmp; return NULL_TREE; } /* When parsing `int a[] = {1, 2};' we don't know the size of the array until we finish parsing the initializer. If that's the situation we're in, update DECL accordingly. */ static void maybe_deduce_size_from_array_init (tree decl, tree init) { tree type = TREE_TYPE (decl); if (TREE_CODE (type) == ARRAY_TYPE && TYPE_DOMAIN (type) == NULL_TREE && TREE_CODE (decl) != TYPE_DECL) { /* do_default is really a C-ism to deal with tentative definitions. But let's leave it here to ease the eventual merge. */ int do_default = !DECL_EXTERNAL (decl); tree initializer = init ? init : DECL_INITIAL (decl); int failure = complete_array_type (type, initializer, do_default); if (failure == 1) error ("initializer fails to determine size of `%D'", decl); if (failure == 2) { if (do_default) error ("array size missing in `%D'", decl); /* If a `static' var's size isn't known, make it extern as well as static, so it does not get allocated. If it's not `static', then don't mark it extern; finish_incomplete_decl will give it a default size and it will get allocated. */ else if (!pedantic && TREE_STATIC (decl) && !TREE_PUBLIC (decl)) DECL_EXTERNAL (decl) = 1; } if (pedantic && TYPE_DOMAIN (type) != NULL_TREE && tree_int_cst_lt (TYPE_MAX_VALUE (TYPE_DOMAIN (type)), integer_zero_node)) error ("zero-size array `%D'", decl); layout_decl (decl, 0); } } /* Set DECL_SIZE, DECL_ALIGN, etc. for DECL (a VAR_DECL), and issue any appropriate error messages regarding the layout. */ static void layout_var_decl (tree decl) { tree type = TREE_TYPE (decl); #if 0 tree ttype = target_type (type); #endif /* If we haven't already layed out this declaration, do so now. Note that we must not call complete type for an external object because it's type might involve templates that we are not supposed to instantiate yet. (And it's perfectly valid to say `extern X x' for some incomplete type `X'.) */ if (!DECL_EXTERNAL (decl)) complete_type (type); if (!DECL_SIZE (decl) && TREE_TYPE (decl) != error_mark_node && (COMPLETE_TYPE_P (type) || (TREE_CODE (type) == ARRAY_TYPE && !TYPE_DOMAIN (type) && COMPLETE_TYPE_P (TREE_TYPE (type))))) layout_decl (decl, 0); if (!DECL_EXTERNAL (decl) && DECL_SIZE (decl) == NULL_TREE) { /* An automatic variable with an incomplete type: that is an error. Don't talk about array types here, since we took care of that message in grokdeclarator. */ error ("storage size of `%D' isn't known", decl); TREE_TYPE (decl) = error_mark_node; } #if 0 /* Keep this code around in case we later want to control debug info based on whether a type is "used". (jason 1999-11-11) */ else if (!DECL_EXTERNAL (decl) && IS_AGGR_TYPE (ttype)) /* Let debugger know it should output info for this type. */ note_debug_info_needed (ttype); if (TREE_STATIC (decl) && DECL_CLASS_SCOPE_P (decl)) note_debug_info_needed (DECL_CONTEXT (decl)); #endif if ((DECL_EXTERNAL (decl) || TREE_STATIC (decl)) && DECL_SIZE (decl) != NULL_TREE && ! TREE_CONSTANT (DECL_SIZE (decl))) { if (TREE_CODE (DECL_SIZE (decl)) == INTEGER_CST) constant_expression_warning (DECL_SIZE (decl)); else error ("storage size of `%D' isn't constant", decl); } if (TREE_STATIC (decl) && !DECL_ARTIFICIAL (decl) && current_function_decl && DECL_CONTEXT (decl) == current_function_decl) push_local_name (decl); } /* If a local static variable is declared in an inline function, or if we have a weak definition, we must endeavor to create only one instance of the variable at link-time. */ static void maybe_commonize_var (tree decl) { /* Static data in a function with comdat linkage also has comdat linkage. */ if (TREE_STATIC (decl) /* Don't mess with __FUNCTION__. */ && ! DECL_ARTIFICIAL (decl) && DECL_FUNCTION_SCOPE_P (decl) /* Unfortunately, import_export_decl has not always been called before the function is processed, so we cannot simply check DECL_COMDAT. */ && (DECL_COMDAT (DECL_CONTEXT (decl)) || ((DECL_DECLARED_INLINE_P (DECL_CONTEXT (decl)) || DECL_TEMPLATE_INSTANTIATION (DECL_CONTEXT (decl))) && TREE_PUBLIC (DECL_CONTEXT (decl))))) { if (flag_weak) { /* With weak symbols, we simply make the variable COMDAT; that will cause copies in multiple translations units to be merged. */ comdat_linkage (decl); } else { if (DECL_INITIAL (decl) == NULL_TREE || DECL_INITIAL (decl) == error_mark_node) { /* Without weak symbols, we can use COMMON to merge uninitialized variables. */ TREE_PUBLIC (decl) = 1; DECL_COMMON (decl) = 1; } else { /* While for initialized variables, we must use internal linkage -- which means that multiple copies will not be merged. */ TREE_PUBLIC (decl) = 0; DECL_COMMON (decl) = 0; cp_warning_at ("sorry: semantics of inline function static data `%#D' are wrong (you'll wind up with multiple copies)", decl); warning ("%J you can work around this by removing the initializer", decl); } } } else if (DECL_LANG_SPECIFIC (decl) && DECL_COMDAT (decl)) /* Set it up again; we might have set DECL_INITIAL since the last time. */ comdat_linkage (decl); } /* Issue an error message if DECL is an uninitialized const variable. */ static void check_for_uninitialized_const_var (tree decl) { tree type = TREE_TYPE (decl); /* ``Unless explicitly declared extern, a const object does not have external linkage and must be initialized. ($8.4; $12.1)'' ARM 7.1.6 */ if (TREE_CODE (decl) == VAR_DECL && TREE_CODE (type) != REFERENCE_TYPE && CP_TYPE_CONST_P (type) && !TYPE_NEEDS_CONSTRUCTING (type) && !DECL_INITIAL (decl)) error ("uninitialized const `%D'", decl); } /* FIELD is a FIELD_DECL or NULL. In the former case, the value returned is the next FIELD_DECL (possibly FIELD itself) that can be initialized. If there are no more such fields, the return value will be NULL. */ static tree next_initializable_field (tree field) { while (field && (TREE_CODE (field) != FIELD_DECL || (DECL_C_BIT_FIELD (field) && !DECL_NAME (field)) || DECL_ARTIFICIAL (field))) field = TREE_CHAIN (field); return field; } /* Subroutine of reshape_init. Reshape the constructor for an array. INITP is the pointer to the old constructor list (to the CONSTRUCTOR_ELTS of the CONSTRUCTOR we are processing), while NEW_INIT is the CONSTRUCTOR we are building. ELT_TYPE is the element type of the array. MAX_INDEX is an INTEGER_CST representing the size of the array minus one (the maximum index), or NULL_TREE if the array was declared without specifying the size. */ static bool reshape_init_array (tree elt_type, tree max_index, tree *initp, tree new_init) { bool sized_array_p = (max_index != NULL_TREE); unsigned HOST_WIDE_INT max_index_cst = 0; unsigned HOST_WIDE_INT index; if (sized_array_p) { if (host_integerp (max_index, 1)) max_index_cst = tree_low_cst (max_index, 1); /* sizetype is sign extended, not zero extended. */ else max_index_cst = tree_low_cst (convert (size_type_node, max_index), 1); } /* Loop until there are no more initializers. */ for (index = 0; *initp && (!sized_array_p || index <= max_index_cst); ++index) { tree element_init; tree designated_index; element_init = reshape_init (elt_type, initp); if (element_init == error_mark_node) return false; TREE_CHAIN (element_init) = CONSTRUCTOR_ELTS (new_init); CONSTRUCTOR_ELTS (new_init) = element_init; designated_index = TREE_PURPOSE (element_init); if (designated_index) { /* Handle array designated initializers (GNU extension). */ if (TREE_CODE (designated_index) == IDENTIFIER_NODE) { error ("name `%D' used in a GNU-style designated " "initializer for an array", designated_index); TREE_PURPOSE (element_init) = NULL_TREE; } else abort (); } } return true; } /* Undo the brace-elision allowed by [dcl.init.aggr] in a brace-enclosed aggregate initializer. *INITP is one of a list of initializers describing a brace-enclosed initializer for an entity of the indicated aggregate TYPE. It may not presently match the shape of the TYPE; for example: struct S { int a; int b; }; struct S a[] = { 1, 2, 3, 4 }; Here *INITP will point to TREE_LIST of four elements, rather than a list of two elements, each itself a list of two elements. This routine transforms INIT from the former form into the latter. The revised initializer is returned. */ static tree reshape_init (tree type, tree *initp) { tree inits; tree old_init; tree old_init_value; tree new_init; bool brace_enclosed_p; bool string_init_p; old_init = *initp; old_init_value = (TREE_CODE (*initp) == TREE_LIST ? TREE_VALUE (*initp) : old_init); my_friendly_assert (old_init_value, 20030723); /* If the initializer is brace-enclosed, pull initializers from the enclosed elements. Advance past the brace-enclosed initializer now. */ if (TREE_CODE (old_init_value) == CONSTRUCTOR && TREE_TYPE (old_init_value) == NULL_TREE && TREE_HAS_CONSTRUCTOR (old_init_value)) { *initp = TREE_CHAIN (old_init); TREE_CHAIN (old_init) = NULL_TREE; inits = CONSTRUCTOR_ELTS (old_init_value); initp = &inits; brace_enclosed_p = true; } else { inits = NULL_TREE; brace_enclosed_p = false; } /* A non-aggregate type is always initialized with a single initializer. */ if (!CP_AGGREGATE_TYPE_P (type)) { *initp = TREE_CHAIN (old_init); TREE_CHAIN (old_init) = NULL_TREE; /* It is invalid to initialize a non-aggregate type with a brace-enclosed initializer. */ if (brace_enclosed_p) { error ("brace-enclosed initializer used to initialize `%T'", type); if (TREE_CODE (old_init) == TREE_LIST) TREE_VALUE (old_init) = error_mark_node; else old_init = error_mark_node; } return old_init; } /* [dcl.init.aggr] All implicit type conversions (clause _conv_) are considered when initializing the aggregate member with an initializer from an initializer-list. If the initializer can initialize a member, the member is initialized. Otherwise, if the member is itself a non-empty subaggregate, brace elision is assumed and the initializer is considered for the initialization of the first member of the subaggregate. */ if (!brace_enclosed_p && can_convert_arg (type, TREE_TYPE (old_init_value), old_init_value)) { *initp = TREE_CHAIN (old_init); TREE_CHAIN (old_init) = NULL_TREE; return old_init; } string_init_p = false; if (TREE_CODE (old_init_value) == STRING_CST && TREE_CODE (type) == ARRAY_TYPE && char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type)))) { /* [dcl.init.string] A char array (whether plain char, signed char, or unsigned char) can be initialized by a string-literal (optionally enclosed in braces); a wchar_t array can be initialized by a wide string-literal (optionally enclosed in braces). */ new_init = old_init; /* Move past the initializer. */ *initp = TREE_CHAIN (old_init); TREE_CHAIN (old_init) = NULL_TREE; string_init_p = true; } else { /* Build a CONSTRUCTOR to hold the contents of the aggregate. */ new_init = build_constructor (type, NULL_TREE); TREE_HAS_CONSTRUCTOR (new_init) = 1; if (CLASS_TYPE_P (type)) { tree field; field = next_initializable_field (TYPE_FIELDS (type)); if (!field) { /* [dcl.init.aggr] An initializer for an aggregate member that is an empty class shall have the form of an empty initializer-list {}. */ if (!brace_enclosed_p) { error ("initializer for `%T' must be brace-enclosed", type); return error_mark_node; } } else { /* Loop through the initializable fields, gathering initializers. */ while (*initp) { tree field_init; /* Handle designated initializers, as an extension. */ if (TREE_PURPOSE (*initp)) { if (pedantic) pedwarn ("ISO C++ does not allow designated initializers"); field = lookup_field_1 (type, TREE_PURPOSE (*initp), /*want_type=*/false); if (!field || TREE_CODE (field) != FIELD_DECL) error ("`%T' has no non-static data member named `%D'", type, TREE_PURPOSE (*initp)); } if (!field) break; field_init = reshape_init (TREE_TYPE (field), initp); if (field_init == error_mark_node) return error_mark_node; TREE_CHAIN (field_init) = CONSTRUCTOR_ELTS (new_init); CONSTRUCTOR_ELTS (new_init) = field_init; /* [dcl.init.aggr] When a union is initialized with a brace-enclosed initializer, the braces shall only contain an initializer for the first member of the union. */ if (TREE_CODE (type) == UNION_TYPE) break; field = next_initializable_field (TREE_CHAIN (field)); } } } else if ((TREE_CODE (type) == ARRAY_TYPE)|| (TREE_CODE (type) == VECTOR_TYPE)) { tree max_index; /* If the bound of the array is known, take no more initializers than are allowed. */ max_index = ((TYPE_DOMAIN (type) && (TREE_CODE (type) == ARRAY_TYPE)) ? array_type_nelts (type) : NULL_TREE); if (!reshape_init_array (TREE_TYPE (type), max_index, initp, new_init)) return error_mark_node; } else abort (); /* The initializers were placed in reverse order in the CONSTRUCTOR. */ CONSTRUCTOR_ELTS (new_init) = nreverse (CONSTRUCTOR_ELTS (new_init)); if (TREE_CODE (old_init) == TREE_LIST) new_init = build_tree_list (TREE_PURPOSE (old_init), new_init); } /* If there are more initializers than necessary, issue a diagnostic. */ if (*initp) { if (brace_enclosed_p) error ("too many initializers for `%T'", type); else if (warn_missing_braces && !string_init_p) warning ("missing braces around initializer"); } return new_init; } /* Verify INIT (the initializer for DECL), and record the initialization in DECL_INITIAL, if appropriate. CLEANUP is as for grok_reference_init. If the return value is non-NULL, it is an expression that must be evaluated dynamically to initialize DECL. */ static tree check_initializer (tree decl, tree init, int flags, tree *cleanup) { tree type = TREE_TYPE (decl); tree init_code = NULL; /* If `start_decl' didn't like having an initialization, ignore it now. */ if (init != NULL_TREE && DECL_INITIAL (decl) == NULL_TREE) init = NULL_TREE; /* If an initializer is present, DECL_INITIAL has been error_mark_node, to indicate that an as-of-yet unevaluated initialization will occur. From now on, DECL_INITIAL reflects the static initialization -- if any -- of DECL. */ DECL_INITIAL (decl) = NULL_TREE; /* Things that are going to be initialized need to have complete type. */ TREE_TYPE (decl) = type = complete_type (TREE_TYPE (decl)); if (type == error_mark_node) /* We will have already complained. */ init = NULL_TREE; else if (init && COMPLETE_TYPE_P (type) && !TREE_CONSTANT (TYPE_SIZE (type))) { error ("variable-sized object `%D' may not be initialized", decl); init = NULL_TREE; } else if (TREE_CODE (type) == ARRAY_TYPE && !COMPLETE_TYPE_P (complete_type (TREE_TYPE (type)))) { error ("elements of array `%#D' have incomplete type", decl); init = NULL_TREE; } else if (TREE_CODE (type) != ARRAY_TYPE && !COMPLETE_TYPE_P (type)) { error ("`%D' has incomplete type", decl); TREE_TYPE (decl) = error_mark_node; init = NULL_TREE; } if (TREE_CODE (decl) == CONST_DECL) { my_friendly_assert (TREE_CODE (decl) != REFERENCE_TYPE, 148); DECL_INITIAL (decl) = init; my_friendly_assert (init != NULL_TREE, 149); init = NULL_TREE; } else if (!DECL_EXTERNAL (decl) && TREE_CODE (type) == REFERENCE_TYPE) init = grok_reference_init (decl, type, init, cleanup); else if (init) { if (TREE_CODE (init) == CONSTRUCTOR && TREE_HAS_CONSTRUCTOR (init)) { /* [dcl.init] paragraph 13, If T is a scalar type, then a declaration of the form T x = { a }; is equivalent to T x = a; reshape_init will complain about the extra braces, and doesn't do anything useful in the case where TYPE is scalar, so just don't call it. */ if (CP_AGGREGATE_TYPE_P (type)) init = reshape_init (type, &init); if ((*targetm.vector_opaque_p) (type)) { error ("opaque vector types cannot be initialized"); init = error_mark_node; } } /* If DECL has an array type without a specific bound, deduce the array size from the initializer. */ maybe_deduce_size_from_array_init (decl, init); type = TREE_TYPE (decl); if (TREE_CODE (init) == CONSTRUCTOR && TREE_HAS_CONSTRUCTOR (init)) TREE_TYPE (init) = type; if (TYPE_HAS_CONSTRUCTOR (type) || TYPE_NEEDS_CONSTRUCTING (type)) { if (TREE_CODE (type) == ARRAY_TYPE) goto initialize_aggr; else if (TREE_CODE (init) == CONSTRUCTOR && TREE_HAS_CONSTRUCTOR (init)) { if (TYPE_NON_AGGREGATE_CLASS (type)) { error ("`%D' must be initialized by constructor, not by `{...}'", decl); init = error_mark_node; } else goto dont_use_constructor; } else { int saved_stmts_are_full_exprs_p; initialize_aggr: saved_stmts_are_full_exprs_p = 0; if (building_stmt_tree ()) { saved_stmts_are_full_exprs_p = stmts_are_full_exprs_p (); current_stmt_tree ()->stmts_are_full_exprs_p = 1; } init = build_aggr_init (decl, init, flags); if (building_stmt_tree ()) current_stmt_tree ()->stmts_are_full_exprs_p = saved_stmts_are_full_exprs_p; return init; } } else { dont_use_constructor: if (TREE_CODE (init) != TREE_VEC) { init_code = store_init_value (decl, init); init = NULL; } } } else if (DECL_EXTERNAL (decl)) ; else if (TYPE_P (type) && TYPE_NEEDS_CONSTRUCTING (type)) goto initialize_aggr; else if (IS_AGGR_TYPE (type)) { tree core_type = strip_array_types (type); if (CLASSTYPE_READONLY_FIELDS_NEED_INIT (core_type)) error ("structure `%D' with uninitialized const members", decl); if (CLASSTYPE_REF_FIELDS_NEED_INIT (core_type)) error ("structure `%D' with uninitialized reference members", decl); check_for_uninitialized_const_var (decl); } else check_for_uninitialized_const_var (decl); if (init && init != error_mark_node) init_code = build (INIT_EXPR, type, decl, init); return init_code; } /* If DECL is not a local variable, give it RTL. */ static void make_rtl_for_nonlocal_decl (tree decl, tree init, const char* asmspec) { int toplev = toplevel_bindings_p (); int defer_p; /* Handle non-variables up front. */ if (TREE_CODE (decl) != VAR_DECL) { rest_of_decl_compilation (decl, asmspec, toplev, at_eof); return; } /* If we see a class member here, it should be a static data member. */ if (DECL_LANG_SPECIFIC (decl) && DECL_IN_AGGR_P (decl)) { my_friendly_assert (TREE_STATIC (decl), 19990828); /* An in-class declaration of a static data member should be external; it is only a declaration, and not a definition. */ if (init == NULL_TREE) my_friendly_assert (DECL_EXTERNAL (decl), 20000723); } /* Set the DECL_ASSEMBLER_NAME for the variable. */ if (asmspec) { change_decl_assembler_name (decl, get_identifier (asmspec)); /* The `register' keyword, when used together with an asm-specification, indicates that the variable should be placed in a particular register. */ if (DECL_REGISTER (decl)) DECL_C_HARD_REGISTER (decl) = 1; } /* We don't create any RTL for local variables. */ if (DECL_FUNCTION_SCOPE_P (decl) && !TREE_STATIC (decl)) return; /* We defer emission of local statics until the corresponding DECL_STMT is expanded. */ defer_p = DECL_FUNCTION_SCOPE_P (decl) || DECL_VIRTUAL_P (decl); /* We try to defer namespace-scope static constants so that they are not emitted into the object file unnecessarily. */ if (!DECL_VIRTUAL_P (decl) && TREE_READONLY (decl) && DECL_INITIAL (decl) != NULL_TREE && DECL_INITIAL (decl) != error_mark_node && ! EMPTY_CONSTRUCTOR_P (DECL_INITIAL (decl)) && toplev && !TREE_PUBLIC (decl)) { /* Fool with the linkage of static consts according to #pragma interface. */ if (!interface_unknown && !TREE_PUBLIC (decl)) { TREE_PUBLIC (decl) = 1; DECL_EXTERNAL (decl) = interface_only; } defer_p = 1; } /* Likewise for template instantiations. */ else if (DECL_COMDAT (decl)) defer_p = 1; /* If we're deferring the variable, we only need to make RTL if there's an ASMSPEC. Otherwise, we'll lazily create it later when we need it. (There's no way to lazily create RTL for things that have assembly specs because the information about the specifier isn't stored in the tree, yet) */ if (defer_p && asmspec) make_decl_rtl (decl, asmspec); /* If we're not deferring, go ahead and assemble the variable. */ else if (!defer_p) rest_of_decl_compilation (decl, asmspec, toplev, at_eof); } /* Generate code to initialize DECL (a local variable). */ static void initialize_local_var (tree decl, tree init) { tree type = TREE_TYPE (decl); tree cleanup; my_friendly_assert (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == RESULT_DECL, 20021010); my_friendly_assert (!TREE_STATIC (decl), 20021010); if (DECL_SIZE (decl) == NULL_TREE) { /* If we used it already as memory, it must stay in memory. */ DECL_INITIAL (decl) = NULL_TREE; TREE_ADDRESSABLE (decl) = TREE_USED (decl); } if (DECL_SIZE (decl) && type != error_mark_node) { int already_used; /* Compute and store the initial value. */ already_used = TREE_USED (decl) || TREE_USED (type); /* Perform the initialization. */ if (init) { int saved_stmts_are_full_exprs_p; my_friendly_assert (building_stmt_tree (), 20000906); saved_stmts_are_full_exprs_p = stmts_are_full_exprs_p (); current_stmt_tree ()->stmts_are_full_exprs_p = 1; finish_expr_stmt (init); current_stmt_tree ()->stmts_are_full_exprs_p = saved_stmts_are_full_exprs_p; } /* Set this to 0 so we can tell whether an aggregate which was initialized was ever used. Don't do this if it has a destructor, so we don't complain about the 'resource allocation is initialization' idiom. Now set attribute((unused)) on types so decls of that type will be marked used. (see TREE_USED, above.) */ if (TYPE_NEEDS_CONSTRUCTING (type) && ! already_used && TYPE_HAS_TRIVIAL_DESTRUCTOR (type) && DECL_NAME (decl)) TREE_USED (decl) = 0; else if (already_used) TREE_USED (decl) = 1; } /* Generate a cleanup, if necessary. */ cleanup = cxx_maybe_build_cleanup (decl); if (DECL_SIZE (decl) && cleanup) finish_decl_cleanup (decl, cleanup); } /* Finish processing of a declaration; install its line number and initial value. If the length of an array type is not known before, it must be determined now, from the initial value, or it is an error. INIT holds the value of an initializer that should be allowed to escape the normal rules. FLAGS is LOOKUP_ONLYCONVERTING if the = init syntax was used, else 0 if the (init) syntax was used. */ void cp_finish_decl (tree decl, tree init, tree asmspec_tree, int flags) { tree type; tree ttype = NULL_TREE; tree cleanup; const char *asmspec = NULL; int was_readonly = 0; bool var_definition_p = false; if (decl == error_mark_node) return; else if (! decl) { if (init) error ("assignment (not initialization) in declaration"); return; } my_friendly_assert (TREE_CODE (decl) != RESULT_DECL, 20030619); /* Assume no cleanup is required. */ cleanup = NULL_TREE; /* If a name was specified, get the string. */ if (global_scope_p (current_binding_level)) asmspec_tree = maybe_apply_renaming_pragma (decl, asmspec_tree); if (asmspec_tree) asmspec = TREE_STRING_POINTER (asmspec_tree); if (init && TREE_CODE (init) == NAMESPACE_DECL) { error ("cannot initialize `%D' to namespace `%D'", decl, init); init = NULL_TREE; } if (current_class_type && CP_DECL_CONTEXT (decl) == current_class_type && TYPE_BEING_DEFINED (current_class_type) && (DECL_INITIAL (decl) || init)) DECL_INITIALIZED_IN_CLASS_P (decl) = 1; if (TREE_CODE (decl) == VAR_DECL && DECL_CONTEXT (decl) && TREE_CODE (DECL_CONTEXT (decl)) == NAMESPACE_DECL && DECL_CONTEXT (decl) != current_namespace && init) { /* Leave the namespace of the object. */ pop_decl_namespace (); } type = TREE_TYPE (decl); if (type == error_mark_node) goto finish_end0; if (TYPE_HAS_MUTABLE_P (type)) TREE_READONLY (decl) = 0; if (processing_template_decl) { /* Add this declaration to the statement-tree. */ if (at_function_scope_p ()) add_decl_stmt (decl); if (init && DECL_INITIAL (decl)) DECL_INITIAL (decl) = init; if (TREE_CODE (decl) == VAR_DECL && !DECL_PRETTY_FUNCTION_P (decl) && !dependent_type_p (TREE_TYPE (decl))) maybe_deduce_size_from_array_init (decl, init); goto finish_end0; } /* Parameters are handled by store_parm_decls, not cp_finish_decl. */ my_friendly_assert (TREE_CODE (decl) != PARM_DECL, 19990828); /* Take care of TYPE_DECLs up front. */ if (TREE_CODE (decl) == TYPE_DECL) { if (type != error_mark_node && IS_AGGR_TYPE (type) && DECL_NAME (decl)) { if (TREE_TYPE (DECL_NAME (decl)) && TREE_TYPE (decl) != type) warning ("shadowing previous type declaration of `%#D'", decl); set_identifier_type_value (DECL_NAME (decl), decl); } /* If we have installed this as the canonical typedef for this type, and that type has not been defined yet, delay emitting the debug information for it, as we will emit it later. */ if (TYPE_MAIN_DECL (TREE_TYPE (decl)) == decl && !COMPLETE_TYPE_P (TREE_TYPE (decl))) TYPE_DECL_SUPPRESS_DEBUG (decl) = 1; rest_of_decl_compilation (decl, NULL, DECL_CONTEXT (decl) == NULL_TREE, at_eof); goto finish_end; } if (TREE_CODE (decl) != FUNCTION_DECL) ttype = target_type (type); /* Currently, GNU C++ puts constants in text space, making them impossible to initialize. In the future, one would hope for an operating system which understood the difference between initialization and the running of a program. */ if (! DECL_EXTERNAL (decl) && TREE_READONLY (decl)) { was_readonly = 1; if (TYPE_NEEDS_CONSTRUCTING (type) || TREE_CODE (type) == REFERENCE_TYPE) TREE_READONLY (decl) = 0; } if (TREE_CODE (decl) == VAR_DECL) { /* Only PODs can have thread-local storage. Other types may require various kinds of non-trivial initialization. */ if (DECL_THREAD_LOCAL (decl) && !pod_type_p (TREE_TYPE (decl))) error ("`%D' cannot be thread-local because it has non-POD type `%T'", decl, TREE_TYPE (decl)); /* Convert the initializer to the type of DECL, if we have not already initialized DECL. */ if (!DECL_INITIALIZED_P (decl) /* If !DECL_EXTERNAL then DECL is being defined. In the case of a static data member initialized inside the class-specifier, there can be an initializer even if DECL is *not* defined. */ && (!DECL_EXTERNAL (decl) || init)) { init = check_initializer (decl, init, flags, &cleanup); /* Thread-local storage cannot be dynamically initialized. */ if (DECL_THREAD_LOCAL (decl) && init) { error ("`%D' is thread-local and so cannot be dynamically " "initialized", decl); init = NULL_TREE; } + if (DECL_EXTERNAL (decl) && init) + { + /* The static data member cannot be initialized by a + non-constant when being declared. */ + error ("`%D' cannot be initialized by a non-constant expression" + " when being declared", decl); + DECL_INITIALIZED_IN_CLASS_P (decl) = 0; + init = NULL_TREE; + } + /* Handle: [dcl.init] The memory occupied by any object of static storage duration is zero-initialized at program startup before any other initialization takes place. We cannot create an appropriate initializer until after the type of DECL is finalized. If DECL_INITIAL is set, then the DECL is statically initialized, and any necessary zero-initialization has already been performed. */ if (TREE_STATIC (decl) && !DECL_INITIAL (decl)) DECL_INITIAL (decl) = build_zero_init (TREE_TYPE (decl), /*nelts=*/NULL_TREE, /*static_storage_p=*/true); /* Remember that the initialization for this variable has taken place. */ DECL_INITIALIZED_P (decl) = 1; /* This declaration is the definition of this variable, unless we are initializing a static data member within the class specifier. */ if (!DECL_EXTERNAL (decl)) var_definition_p = true; } /* If the variable has an array type, lay out the type, even if there is no initializer. It is valid to index through the array, and we must get TYPE_ALIGN set correctly on the array type. */ else if (TREE_CODE (type) == ARRAY_TYPE) layout_type (type); } /* Add this declaration to the statement-tree. This needs to happen after the call to check_initializer so that the DECL_STMT for a reference temp is added before the DECL_STMT for the reference itself. */ if (at_function_scope_p ()) add_decl_stmt (decl); if (TREE_CODE (decl) == VAR_DECL) layout_var_decl (decl); /* Output the assembler code and/or RTL code for variables and functions, unless the type is an undefined structure or union. If not, it will get done when the type is completed. */ if (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == FUNCTION_DECL) { if (TREE_CODE (decl) == VAR_DECL) maybe_commonize_var (decl); make_rtl_for_nonlocal_decl (decl, init, asmspec); if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE) abstract_virtuals_error (decl, strip_array_types (TREE_TYPE (type))); else if (POINTER_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE) { /* If it's either a pointer or an array type, strip through all of them but the last one. If the last is an array type, issue an error if the element type is abstract. */ while (POINTER_TYPE_P (TREE_TYPE (type)) || TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE) type = TREE_TYPE (type); if (TREE_CODE (type) == ARRAY_TYPE) abstract_virtuals_error (decl, TREE_TYPE (type)); } else abstract_virtuals_error (decl, type); if (TREE_CODE (decl) == FUNCTION_DECL || TREE_TYPE (decl) == error_mark_node) /* No initialization required. */ ; else if (DECL_EXTERNAL (decl) && ! (DECL_LANG_SPECIFIC (decl) && DECL_NOT_REALLY_EXTERN (decl))) { if (init) DECL_INITIAL (decl) = init; } else { /* A variable definition. */ if (DECL_FUNCTION_SCOPE_P (decl)) { /* This is a local declaration. */ maybe_inject_for_scope_var (decl); /* Initialize the local variable. */ if (processing_template_decl) { if (init || DECL_INITIAL (decl) == error_mark_node) DECL_INITIAL (decl) = init; } else if (!TREE_STATIC (decl)) initialize_local_var (decl, init); } /* If a variable is defined, and then a subsequent definintion with external linkage is encountered, we will get here twice for the same variable. We want to avoid calling expand_static_init more than once. For variables that are not static data members, we can call expand_static_init only when we actually process the initializer. It is not legal to redeclare a static data member, so this issue does not arise in that case. */ if (var_definition_p && TREE_STATIC (decl)) expand_static_init (decl, init); } finish_end0: /* Undo call to `pushclass' that was done in `start_decl' due to initialization of qualified member variable. I.e., Foo::x = 10; */ { tree context = CP_DECL_CONTEXT (decl); if (context && TYPE_P (context) && (TREE_CODE (decl) == VAR_DECL /* We also have a pushclass done that we need to undo here if we're at top level and declare a method. */ || TREE_CODE (decl) == FUNCTION_DECL) /* If size hasn't been set, we're still defining it, and therefore inside the class body; don't pop the binding level.. */ && COMPLETE_TYPE_P (context) && context == current_class_type) pop_nested_class (); } } /* If a CLEANUP_STMT was created to destroy a temporary bound to a reference, insert it in the statement-tree now. */ if (cleanup) add_stmt (cleanup); finish_end: if (was_readonly) TREE_READONLY (decl) = 1; /* If this was marked 'used', be sure it will be output. */ if (lookup_attribute ("used", DECL_ATTRIBUTES (decl))) mark_referenced (DECL_ASSEMBLER_NAME (decl)); } /* This is here for a midend callback from c-common.c. */ void finish_decl (tree decl, tree init, tree asmspec_tree) { cp_finish_decl (decl, init, asmspec_tree, 0); } /* Returns a declaration for a VAR_DECL as if: extern "C" TYPE NAME; had been seen. Used to create compiler-generated global variables. */ tree declare_global_var (tree name, tree type) { tree decl; push_to_top_level (); decl = build_decl (VAR_DECL, name, type); TREE_PUBLIC (decl) = 1; DECL_EXTERNAL (decl) = 1; DECL_ARTIFICIAL (decl) = 1; pushdecl (decl); cp_finish_decl (decl, NULL_TREE, NULL_TREE, 0); pop_from_top_level (); return decl; } /* Returns a pointer to the `atexit' function. Note that if FLAG_USE_CXA_ATEXIT is nonzero, then this will actually be the new `__cxa_atexit' function specified in the IA64 C++ ABI. */ static tree get_atexit_node (void) { tree atexit_fndecl; tree arg_types; tree fn_type; tree fn_ptr_type; const char *name; if (atexit_node) return atexit_node; if (flag_use_cxa_atexit) { /* The declaration for `__cxa_atexit' is: int __cxa_atexit (void (*)(void *), void *, void *) We build up the argument types and then then function type itself. */ /* First, build the pointer-to-function type for the first argument. */ arg_types = tree_cons (NULL_TREE, ptr_type_node, void_list_node); fn_type = build_function_type (void_type_node, arg_types); fn_ptr_type = build_pointer_type (fn_type); /* Then, build the rest of the argument types. */ arg_types = tree_cons (NULL_TREE, ptr_type_node, void_list_node); arg_types = tree_cons (NULL_TREE, ptr_type_node, arg_types); arg_types = tree_cons (NULL_TREE, fn_ptr_type, arg_types); /* And the final __cxa_atexit type. */ fn_type = build_function_type (integer_type_node, arg_types); fn_ptr_type = build_pointer_type (fn_type); name = "__cxa_atexit"; } else { /* The declaration for `atexit' is: int atexit (void (*)()); We build up the argument types and then then function type itself. */ fn_type = build_function_type (void_type_node, void_list_node); fn_ptr_type = build_pointer_type (fn_type); arg_types = tree_cons (NULL_TREE, fn_ptr_type, void_list_node); /* Build the final atexit type. */ fn_type = build_function_type (integer_type_node, arg_types); name = "atexit"; } /* Now, build the function declaration. */ push_lang_context (lang_name_c); atexit_fndecl = build_library_fn_ptr (name, fn_type); mark_used (atexit_fndecl); pop_lang_context (); atexit_node = decay_conversion (atexit_fndecl); return atexit_node; } /* Returns the __dso_handle VAR_DECL. */ static tree get_dso_handle_node (void) { if (dso_handle_node) return dso_handle_node; /* Declare the variable. */ dso_handle_node = declare_global_var (get_identifier ("__dso_handle"), ptr_type_node); return dso_handle_node; } /* Begin a new function with internal linkage whose job will be simply to destroy some particular variable. */ static GTY(()) int start_cleanup_cnt; static tree start_cleanup_fn (void) { int old_interface_only = interface_only; int old_interface_unknown = interface_unknown; char name[32]; tree parmtypes; tree fntype; tree fndecl; push_to_top_level (); /* No need to mangle this. */ push_lang_context (lang_name_c); interface_only = 0; interface_unknown = 1; /* Build the parameter-types. */ parmtypes = void_list_node; /* Functions passed to __cxa_atexit take an additional parameter. We'll just ignore it. After we implement the new calling convention for destructors, we can eliminate the use of additional cleanup functions entirely in the -fnew-abi case. */ if (flag_use_cxa_atexit) parmtypes = tree_cons (NULL_TREE, ptr_type_node, parmtypes); /* Build the function type itself. */ fntype = build_function_type (void_type_node, parmtypes); /* Build the name of the function. */ sprintf (name, "__tcf_%d", start_cleanup_cnt++); /* Build the function declaration. */ fndecl = build_lang_decl (FUNCTION_DECL, get_identifier (name), fntype); /* It's a function with internal linkage, generated by the compiler. */ TREE_PUBLIC (fndecl) = 0; DECL_ARTIFICIAL (fndecl) = 1; /* Make the function `inline' so that it is only emitted if it is actually needed. It is unlikely that it will be inlined, since it is only called via a function pointer, but we avoid unnecessary emissions this way. */ DECL_INLINE (fndecl) = 1; DECL_DECLARED_INLINE_P (fndecl) = 1; DECL_INTERFACE_KNOWN (fndecl) = 1; /* Build the parameter. */ if (flag_use_cxa_atexit) { tree parmdecl; parmdecl = cp_build_parm_decl (NULL_TREE, ptr_type_node); DECL_CONTEXT (parmdecl) = fndecl; TREE_USED (parmdecl) = 1; DECL_ARGUMENTS (fndecl) = parmdecl; } pushdecl (fndecl); start_function (/*specs=*/NULL_TREE, fndecl, NULL_TREE, SF_PRE_PARSED); interface_unknown = old_interface_unknown; interface_only = old_interface_only; pop_lang_context (); return current_function_decl; } /* Finish the cleanup function begun by start_cleanup_fn. */ static void end_cleanup_fn (void) { expand_or_defer_fn (finish_function (0)); pop_from_top_level (); } /* Generate code to handle the destruction of DECL, an object with static storage duration. */ void register_dtor_fn (tree decl) { tree cleanup; tree compound_stmt; tree args; tree fcall; if (TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl))) return; /* Call build_cleanup before we enter the anonymous function so that any access checks will be done relative to the current scope, rather than the scope of the anonymous function. */ build_cleanup (decl); /* Now start the function. */ cleanup = start_cleanup_fn (); /* Now, recompute the cleanup. It may contain SAVE_EXPRs that refer to the original function, rather than the anonymous one. That will make the back-end think that nested functions are in use, which causes confusion. */ push_deferring_access_checks (dk_no_check); fcall = build_cleanup (decl); pop_deferring_access_checks (); /* Create the body of the anonymous function. */ compound_stmt = begin_compound_stmt (/*has_no_scope=*/false); finish_expr_stmt (fcall); finish_compound_stmt (compound_stmt); end_cleanup_fn (); /* Call atexit with the cleanup function. */ cxx_mark_addressable (cleanup); mark_used (cleanup); cleanup = build_unary_op (ADDR_EXPR, cleanup, 0); if (flag_use_cxa_atexit) { args = tree_cons (NULL_TREE, build_unary_op (ADDR_EXPR, get_dso_handle_node (), 0), NULL_TREE); args = tree_cons (NULL_TREE, null_pointer_node, args); args = tree_cons (NULL_TREE, cleanup, args); } else args = tree_cons (NULL_TREE, cleanup, NULL_TREE); finish_expr_stmt (build_function_call (get_atexit_node (), args)); } /* DECL is a VAR_DECL with static storage duration. INIT, if present, is its initializer. Generate code to handle the construction and destruction of DECL. */ static void expand_static_init (tree decl, tree init) { my_friendly_assert (TREE_CODE (decl) == VAR_DECL, 20021010); my_friendly_assert (TREE_STATIC (decl), 20021010); /* Some variables require no initialization. */ if (!init && !TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl)) && TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl))) return; if (! toplevel_bindings_p ()) { /* Emit code to perform this initialization but once. */ tree if_stmt; tree then_clause; tree assignment; tree guard; tree guard_init; /* Emit code to perform this initialization but once. This code looks like: static int guard = 0; if (!guard) { // Do initialization. guard = 1; // Register variable for destruction at end of program. } Note that the `temp' variable is only set to 1 *after* the initialization is complete. This ensures that an exception, thrown during the construction, will cause the variable to reinitialized when we pass through this code again, as per: [stmt.dcl] If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. In theory, this process should be thread-safe, too; multiple threads should not be able to initialize the variable more than once. We don't yet attempt to ensure thread-safety. */ /* Create the guard variable. */ guard = get_guard (decl); /* Begin the conditional initialization. */ if_stmt = begin_if_stmt (); finish_if_stmt_cond (get_guard_cond (guard), if_stmt); then_clause = begin_compound_stmt (/*has_no_scope=*/false); /* Do the initialization itself. */ assignment = init ? init : NULL_TREE; /* Once the assignment is complete, set TEMP to 1. Since the construction of the static object is complete at this point, we want to make sure TEMP is set to 1 even if a temporary constructed during the initialization throws an exception when it is destroyed. So, we combine the initialization and the assignment to TEMP into a single expression, ensuring that when we call finish_expr_stmt the cleanups will not be run until after TEMP is set to 1. */ guard_init = set_guard (guard); if (assignment) assignment = build_compound_expr (assignment, guard_init); else assignment = guard_init; finish_expr_stmt (assignment); /* Use atexit to register a function for destroying this static variable. */ register_dtor_fn (decl); finish_compound_stmt (then_clause); finish_then_clause (if_stmt); finish_if_stmt (); } else static_aggregates = tree_cons (init, decl, static_aggregates); } /* Finish the declaration of a catch-parameter. */ tree start_handler_parms (tree declspecs, tree declarator) { tree decl; if (declspecs) { decl = grokdeclarator (declarator, declspecs, CATCHPARM, 1, NULL); if (decl == NULL_TREE) error ("invalid catch parameter"); } else decl = NULL_TREE; return decl; } /* Make TYPE a complete type based on INITIAL_VALUE. Return 0 if successful, 1 if INITIAL_VALUE can't be deciphered, 2 if there was no information (in which case assume 0 if DO_DEFAULT). */ int complete_array_type (tree type, tree initial_value, int do_default) { tree maxindex = NULL_TREE; int value = 0; if (initial_value) { /* An array of character type can be initialized from a brace-enclosed string constant. */ if (char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type))) && TREE_CODE (initial_value) == CONSTRUCTOR && CONSTRUCTOR_ELTS (initial_value) && (TREE_CODE (TREE_VALUE (CONSTRUCTOR_ELTS (initial_value))) == STRING_CST) && TREE_CHAIN (CONSTRUCTOR_ELTS (initial_value)) == NULL_TREE) initial_value = TREE_VALUE (CONSTRUCTOR_ELTS (initial_value)); /* Note MAXINDEX is really the maximum index, one less than the size. */ if (TREE_CODE (initial_value) == STRING_CST) { int eltsize = int_size_in_bytes (TREE_TYPE (TREE_TYPE (initial_value))); maxindex = build_int_2 ((TREE_STRING_LENGTH (initial_value) / eltsize) - 1, 0); } else if (TREE_CODE (initial_value) == CONSTRUCTOR) { tree elts = CONSTRUCTOR_ELTS (initial_value); maxindex = ssize_int (-1); for (; elts; elts = TREE_CHAIN (elts)) { if (TREE_PURPOSE (elts)) maxindex = TREE_PURPOSE (elts); else maxindex = size_binop (PLUS_EXPR, maxindex, ssize_int (1)); } maxindex = copy_node (maxindex); } else { /* Make an error message unless that happened already. */ if (initial_value != error_mark_node) value = 1; else initial_value = NULL_TREE; /* Prevent further error messages. */ maxindex = build_int_2 (0, 0); } } if (!maxindex) { if (do_default) maxindex = build_int_2 (0, 0); value = 2; } if (maxindex) { tree itype; tree domain; domain = build_index_type (maxindex); TYPE_DOMAIN (type) = domain; if (! TREE_TYPE (maxindex)) TREE_TYPE (maxindex) = domain; if (initial_value) itype = TREE_TYPE (initial_value); else itype = NULL; if (itype && !TYPE_DOMAIN (itype)) TYPE_DOMAIN (itype) = domain; /* The type of the main variant should never be used for arrays of different sizes. It should only ever be completed with the size of the array. */ if (! TYPE_DOMAIN (TYPE_MAIN_VARIANT (type))) TYPE_DOMAIN (TYPE_MAIN_VARIANT (type)) = domain; } /* Lay out the type now that we can get the real answer. */ layout_type (type); return value; } /* Return zero if something is declared to be a member of type CTYPE when in the context of CUR_TYPE. STRING is the error message to print in that case. Otherwise, quietly return 1. */ static int member_function_or_else (tree ctype, tree cur_type, enum overload_flags flags) { if (ctype && ctype != cur_type) { if (flags == DTOR_FLAG) error ("destructor for alien class `%T' cannot be a member", ctype); else error ("constructor for alien class `%T' cannot be a member", ctype); return 0; } return 1; } /* Subroutine of `grokdeclarator'. */ /* Generate errors possibly applicable for a given set of specifiers. This is for ARM $7.1.2. */ static void bad_specifiers (tree object, const char* type, int virtualp, int quals, int inlinep, int friendp, int raises) { if (virtualp) error ("`%D' declared as a `virtual' %s", object, type); if (inlinep) error ("`%D' declared as an `inline' %s", object, type); if (quals) error ("`const' and `volatile' function specifiers on `%D' invalid in %s declaration", object, type); if (friendp) cp_error_at ("`%D' declared as a friend", object); if (raises && (TREE_CODE (object) == TYPE_DECL || (!TYPE_PTRFN_P (TREE_TYPE (object)) && !TYPE_REFFN_P (TREE_TYPE (object)) && !TYPE_PTRMEMFUNC_P (TREE_TYPE (object))))) cp_error_at ("`%D' declared with an exception specification", object); } /* CTYPE is class type, or null if non-class. TYPE is type this FUNCTION_DECL should have, either FUNCTION_TYPE or METHOD_TYPE. DECLARATOR is the function's name. PARMS is a chain of PARM_DECLs for the function. VIRTUALP is truthvalue of whether the function is virtual or not. FLAGS are to be passed through to `grokclassfn'. QUALS are qualifiers indicating whether the function is `const' or `volatile'. RAISES is a list of exceptions that this function can raise. CHECK is 1 if we must find this method in CTYPE, 0 if we should not look, and -1 if we should not call `grokclassfn' at all. Returns `NULL_TREE' if something goes wrong, after issuing applicable error messages. */ static tree grokfndecl (tree ctype, tree type, tree declarator, tree parms, tree orig_declarator, int virtualp, enum overload_flags flags, tree quals, tree raises, int check, int friendp, int publicp, int inlinep, int funcdef_flag, int template_count, tree in_namespace) { tree decl; int staticp = ctype && TREE_CODE (type) == FUNCTION_TYPE; int has_default_arg = 0; tree t; if (raises) type = build_exception_variant (type, raises); decl = build_lang_decl (FUNCTION_DECL, declarator, type); DECL_ARGUMENTS (decl) = parms; /* Propagate volatile out from type to decl. */ if (TYPE_VOLATILE (type)) TREE_THIS_VOLATILE (decl) = 1; /* If this decl has namespace scope, set that up. */ if (in_namespace) set_decl_namespace (decl, in_namespace, friendp); else if (!ctype) DECL_CONTEXT (decl) = FROB_CONTEXT (current_namespace); /* `main' and builtins have implicit 'C' linkage. */ if ((MAIN_NAME_P (declarator) || (IDENTIFIER_LENGTH (declarator) > 10 && IDENTIFIER_POINTER (declarator)[0] == '_' && IDENTIFIER_POINTER (declarator)[1] == '_' && strncmp (IDENTIFIER_POINTER (declarator)+2, "builtin_", 8) == 0)) && current_lang_name == lang_name_cplusplus && ctype == NULL_TREE /* NULL_TREE means global namespace. */ && DECL_CONTEXT (decl) == NULL_TREE) SET_DECL_LANGUAGE (decl, lang_c); /* Should probably propagate const out from type to decl I bet (mrs). */ if (staticp) { DECL_STATIC_FUNCTION_P (decl) = 1; DECL_CONTEXT (decl) = ctype; } if (ctype) DECL_CONTEXT (decl) = ctype; if (ctype == NULL_TREE && DECL_MAIN_P (decl)) { if (processing_template_decl) error ("cannot declare `::main' to be a template"); if (inlinep) error ("cannot declare `::main' to be inline"); if (!publicp) error ("cannot declare `::main' to be static"); if (!same_type_p (TREE_TYPE (TREE_TYPE (decl)), integer_type_node)) error ("`main' must return `int'"); inlinep = 0; publicp = 1; } /* Members of anonymous types and local classes have no linkage; make them internal. */ /* FIXME what if it gets a name from typedef? */ if (ctype && (TYPE_ANONYMOUS_P (ctype) || decl_function_context (TYPE_MAIN_DECL (ctype)))) publicp = 0; if (publicp) { /* [basic.link]: A name with no linkage (notably, the name of a class or enumeration declared in a local scope) shall not be used to declare an entity with linkage. Only check this for public decls for now. See core 319, 389. */ t = no_linkage_check (TREE_TYPE (decl)); if (t) { if (TYPE_ANONYMOUS_P (t)) { if (DECL_EXTERN_C_P (decl)) /* Allow this; it's pretty common in C. */; else { pedwarn ("non-local function `%#D' uses anonymous type", decl); if (DECL_ORIGINAL_TYPE (TYPE_NAME (t))) cp_pedwarn_at ("\ `%#D' does not refer to the unqualified type, so it is not used for linkage", TYPE_NAME (t)); } } else pedwarn ("non-local function `%#D' uses local type `%T'", decl, t); } } TREE_PUBLIC (decl) = publicp; if (! publicp) { DECL_INTERFACE_KNOWN (decl) = 1; DECL_NOT_REALLY_EXTERN (decl) = 1; } /* If the declaration was declared inline, mark it as such. */ if (inlinep) DECL_DECLARED_INLINE_P (decl) = 1; /* We inline functions that are explicitly declared inline, or, when the user explicitly asks us to, all functions. */ if (DECL_DECLARED_INLINE_P (decl) || (flag_inline_trees == 2 && !DECL_INLINE (decl) && funcdef_flag)) DECL_INLINE (decl) = 1; DECL_EXTERNAL (decl) = 1; if (quals != NULL_TREE && TREE_CODE (type) == FUNCTION_TYPE) { error ("%smember function `%D' cannot have `%T' method qualifier", (ctype ? "static " : "non-"), decl, TREE_VALUE (quals)); quals = NULL_TREE; } if (IDENTIFIER_OPNAME_P (DECL_NAME (decl))) - grok_op_properties (decl, friendp, /*complain=*/true); + grok_op_properties (decl, /*complain=*/true); if (ctype && decl_function_context (decl)) DECL_NO_STATIC_CHAIN (decl) = 1; for (t = TYPE_ARG_TYPES (TREE_TYPE (decl)); t; t = TREE_CHAIN (t)) if (TREE_PURPOSE (t) && TREE_CODE (TREE_PURPOSE (t)) == DEFAULT_ARG) { has_default_arg = 1; break; } if (friendp && TREE_CODE (orig_declarator) == TEMPLATE_ID_EXPR) { if (funcdef_flag) error ("defining explicit specialization `%D' in friend declaration", orig_declarator); else { tree fns = TREE_OPERAND (orig_declarator, 0); tree args = TREE_OPERAND (orig_declarator, 1); if (PROCESSING_REAL_TEMPLATE_DECL_P ()) { /* Something like `template friend void f()'. */ error ("invalid use of template-id `%D' in declaration of primary template", orig_declarator); return NULL_TREE; } /* A friend declaration of the form friend void f<>(). Record the information in the TEMPLATE_ID_EXPR. */ SET_DECL_IMPLICIT_INSTANTIATION (decl); if (TREE_CODE (fns) == COMPONENT_REF) { /* Due to bison parser ickiness, we will have already looked up an operator_name or PFUNCNAME within the current class (see template_id in parse.y). If the current class contains such a name, we'll get a COMPONENT_REF here. Undo that. */ my_friendly_assert (TREE_TYPE (TREE_OPERAND (fns, 0)) == current_class_type, 20001120); fns = TREE_OPERAND (fns, 1); } my_friendly_assert (TREE_CODE (fns) == IDENTIFIER_NODE || TREE_CODE (fns) == OVERLOAD, 20001120); DECL_TEMPLATE_INFO (decl) = tree_cons (fns, args, NULL_TREE); if (has_default_arg) { error ("default arguments are not allowed in declaration of friend template specialization `%D'", decl); return NULL_TREE; } if (inlinep) { error ("`inline' is not allowed in declaration of friend template specialization `%D'", decl); return NULL_TREE; } } } if (funcdef_flag) /* Make the init_value nonzero so pushdecl knows this is not tentative. error_mark_node is replaced later with the BLOCK. */ DECL_INITIAL (decl) = error_mark_node; if (TYPE_NOTHROW_P (type) || nothrow_libfn_p (decl)) TREE_NOTHROW (decl) = 1; /* Caller will do the rest of this. */ if (check < 0) return decl; if (flags == NO_SPECIAL && ctype && constructor_name_p (declarator, ctype)) DECL_CONSTRUCTOR_P (decl) = 1; /* Function gets the ugly name, field gets the nice one. This call may change the type of the function (because of default parameters)! */ if (ctype != NULL_TREE) grokclassfn (ctype, decl, flags, quals); decl = check_explicit_specialization (orig_declarator, decl, template_count, 2 * (funcdef_flag != 0) + 4 * (friendp != 0)); if (decl == error_mark_node) return NULL_TREE; if (ctype != NULL_TREE && (! TYPE_FOR_JAVA (ctype) || check_java_method (decl)) && check) { tree old_decl; old_decl = check_classfn (ctype, decl, processing_template_decl > template_class_depth (ctype)); if (old_decl && TREE_CODE (old_decl) == TEMPLATE_DECL) /* Because grokfndecl is always supposed to return a FUNCTION_DECL, we pull out the DECL_TEMPLATE_RESULT here. We depend on our callers to figure out that its really a template that's being returned. */ old_decl = DECL_TEMPLATE_RESULT (old_decl); if (old_decl && DECL_STATIC_FUNCTION_P (old_decl) && TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE) /* Remove the `this' parm added by grokclassfn. XXX Isn't this done in start_function, too? */ revert_static_member_fn (decl); if (old_decl && DECL_ARTIFICIAL (old_decl)) error ("definition of implicitly-declared `%D'", old_decl); if (old_decl) { tree ok; bool pop_p; /* Since we've smashed OLD_DECL to its DECL_TEMPLATE_RESULT, we must do the same to DECL. */ if (TREE_CODE (decl) == TEMPLATE_DECL) decl = DECL_TEMPLATE_RESULT (decl); /* Attempt to merge the declarations. This can fail, in the case of some invalid specialization declarations. */ pop_p = push_scope (ctype); ok = duplicate_decls (decl, old_decl); if (pop_p) pop_scope (ctype); if (!ok) { error ("no `%#D' member function declared in class `%T'", decl, ctype); return NULL_TREE; } return old_decl; } } if (DECL_CONSTRUCTOR_P (decl) && !grok_ctor_properties (ctype, decl)) return NULL_TREE; if (ctype == NULL_TREE || check) return decl; if (virtualp) DECL_VIRTUAL_P (decl) = 1; return decl; } /* Create a VAR_DECL named NAME with the indicated TYPE. If SCOPE is non-NULL, it is the class type or namespace containing the variable. If SCOPE is NULL, the variable should is created in the innermost enclosings scope. */ static tree grokvardecl (tree type, tree name, RID_BIT_TYPE * specbits_in, int initialized, int constp, tree scope) { tree decl; + tree explicit_scope; RID_BIT_TYPE specbits; my_friendly_assert (!name || TREE_CODE (name) == IDENTIFIER_NODE, 20020808); specbits = *specbits_in; - /* Compute the scope in which to place the variable. */ + /* Compute the scope in which to place the variable, but remember + whether or not that scope was explicitly specified by the user. */ + explicit_scope = scope; if (!scope) { /* An explicit "extern" specifier indicates a namespace-scope variable. */ if (RIDBIT_SETP (RID_EXTERN, specbits)) scope = current_namespace; else if (!at_function_scope_p ()) { scope = current_scope (); if (!scope) scope = current_namespace; } } if (scope && (/* If the variable is a namespace-scope variable declared in a template, we need DECL_LANG_SPECIFIC. */ (TREE_CODE (scope) == NAMESPACE_DECL && processing_template_decl) /* Similarly for namespace-scope variables with language linkage other than C++. */ || (TREE_CODE (scope) == NAMESPACE_DECL && current_lang_name != lang_name_cplusplus) /* Similarly for static data members. */ || TYPE_P (scope))) decl = build_lang_decl (VAR_DECL, name, type); else decl = build_decl (VAR_DECL, name, type); - if (scope && TREE_CODE (scope) == NAMESPACE_DECL) - set_decl_namespace (decl, scope, 0); + if (explicit_scope && TREE_CODE (explicit_scope) == NAMESPACE_DECL) + set_decl_namespace (decl, explicit_scope, 0); else DECL_CONTEXT (decl) = scope; if (name && scope && current_lang_name != lang_name_c) /* We can't mangle lazily here because we don't have any way to recover whether or not a variable was `extern "C"' later. */ mangle_decl (decl); if (RIDBIT_SETP (RID_EXTERN, specbits)) { DECL_THIS_EXTERN (decl) = 1; DECL_EXTERNAL (decl) = !initialized; } /* In class context, static means one per class, public access, and static storage. */ if (DECL_CLASS_SCOPE_P (decl)) { TREE_PUBLIC (decl) = 1; TREE_STATIC (decl) = 1; DECL_EXTERNAL (decl) = 0; } /* At top level, either `static' or no s.c. makes a definition (perhaps tentative), and absence of `static' makes it public. */ else if (toplevel_bindings_p ()) { TREE_PUBLIC (decl) = (RIDBIT_NOTSETP (RID_STATIC, specbits) && (DECL_THIS_EXTERN (decl) || ! constp)); TREE_STATIC (decl) = ! DECL_EXTERNAL (decl); } /* Not at top level, only `static' makes a static definition. */ else { TREE_STATIC (decl) = !! RIDBIT_SETP (RID_STATIC, specbits); TREE_PUBLIC (decl) = DECL_EXTERNAL (decl); } if (RIDBIT_SETP (RID_THREAD, specbits)) { if (targetm.have_tls) DECL_THREAD_LOCAL (decl) = 1; else /* A mere warning is sure to result in improper semantics at runtime. Don't bother to allow this to compile. */ error ("thread-local storage not supported for this target"); } if (TREE_PUBLIC (decl)) { /* [basic.link]: A name with no linkage (notably, the name of a class or enumeration declared in a local scope) shall not be used to declare an entity with linkage. Only check this for public decls for now. */ tree t = no_linkage_check (TREE_TYPE (decl)); if (t) { if (TYPE_ANONYMOUS_P (t)) /* Ignore for now; `enum { foo } e' is pretty common. */; else pedwarn ("non-local variable `%#D' uses local type `%T'", decl, t); } } return decl; } /* Create and return a canonical pointer to member function type, for TYPE, which is a POINTER_TYPE to a METHOD_TYPE. */ tree build_ptrmemfunc_type (tree type) { tree field, fields; tree t; tree unqualified_variant = NULL_TREE; if (type == error_mark_node) return type; /* If a canonical type already exists for this type, use it. We use this method instead of type_hash_canon, because it only does a simple equality check on the list of field members. */ if ((t = TYPE_GET_PTRMEMFUNC_TYPE (type))) return t; /* Make sure that we always have the unqualified pointer-to-member type first. */ if (cp_type_quals (type) != TYPE_UNQUALIFIED) unqualified_variant = build_ptrmemfunc_type (TYPE_MAIN_VARIANT (type)); t = make_aggr_type (RECORD_TYPE); /* Let the front-end know this is a pointer to member function... */ TYPE_PTRMEMFUNC_FLAG (t) = 1; /* ... and not really an aggregate. */ SET_IS_AGGR_TYPE (t, 0); field = build_decl (FIELD_DECL, pfn_identifier, type); fields = field; field = build_decl (FIELD_DECL, delta_identifier, delta_type_node); TREE_CHAIN (field) = fields; fields = field; finish_builtin_struct (t, "__ptrmemfunc_type", fields, ptr_type_node); /* Zap out the name so that the back-end will give us the debugging information for this anonymous RECORD_TYPE. */ TYPE_NAME (t) = NULL_TREE; /* If this is not the unqualified form of this pointer-to-member type, set the TYPE_MAIN_VARIANT for this type to be the unqualified type. Since they are actually RECORD_TYPEs that are not variants of each other, we must do this manually. */ if (cp_type_quals (type) != TYPE_UNQUALIFIED) { t = build_qualified_type (t, cp_type_quals (type)); TYPE_MAIN_VARIANT (t) = unqualified_variant; TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (unqualified_variant); TYPE_NEXT_VARIANT (unqualified_variant) = t; } /* Cache this pointer-to-member type so that we can find it again later. */ TYPE_SET_PTRMEMFUNC_TYPE (type, t); return t; } /* Create and return a pointer to data member type. */ tree build_ptrmem_type (tree class_type, tree member_type) { if (TREE_CODE (member_type) == METHOD_TYPE) { tree arg_types; arg_types = TYPE_ARG_TYPES (member_type); class_type = (cp_build_qualified_type (class_type, cp_type_quals (TREE_TYPE (TREE_VALUE (arg_types))))); member_type = build_method_type_directly (class_type, TREE_TYPE (member_type), TREE_CHAIN (arg_types)); return build_ptrmemfunc_type (build_pointer_type (member_type)); } else { my_friendly_assert (TREE_CODE (member_type) != FUNCTION_TYPE, 20030716); return build_offset_type (class_type, member_type); } } /* DECL is a VAR_DECL defined in-class, whose TYPE is also given. Check to see that the definition is valid. Issue appropriate error messages. Return 1 if the definition is particularly bad, or 0 otherwise. */ int check_static_variable_definition (tree decl, tree type) { /* Motion 10 at San Diego: If a static const integral data member is initialized with an integral constant expression, the initializer may appear either in the declaration (within the class), or in the definition, but not both. If it appears in the class, the member is a member constant. The file-scope definition is always required. */ if (!ARITHMETIC_TYPE_P (type) && TREE_CODE (type) != ENUMERAL_TYPE) { error ("invalid in-class initialization of static data member of non-integral type `%T'", type); /* If we just return the declaration, crashes will sometimes occur. We therefore return void_type_node, as if this was a friend declaration, to cause callers to completely ignore this declaration. */ return 1; } else if (!CP_TYPE_CONST_P (type)) error ("ISO C++ forbids in-class initialization of non-const static member `%D'", decl); else if (pedantic && !INTEGRAL_TYPE_P (type)) pedwarn ("ISO C++ forbids initialization of member constant `%D' of non-integral type `%T'", decl, type); return 0; } /* Given the SIZE (i.e., number of elements) in an array, compute an appropriate index type for the array. If non-NULL, NAME is the name of the thing being declared. */ tree compute_array_index_type (tree name, tree size) { tree type = TREE_TYPE (size); tree itype; /* The array bound must be an integer type. */ if (!dependent_type_p (type) && !INTEGRAL_TYPE_P (type)) { if (name) error ("size of array `%D' has non-integral type `%T'", name, type); else error ("size of array has non-integral type `%T'", type); size = integer_one_node; type = TREE_TYPE (size); } if (abi_version_at_least (2) /* We should only handle value dependent expressions specially. */ ? value_dependent_expression_p (size) /* But for abi-1, we handled all instances in templates. This effects the manglings produced. */ : processing_template_decl) return build_index_type (build_min (MINUS_EXPR, sizetype, size, integer_one_node)); /* The size might be the result of a cast. */ STRIP_TYPE_NOPS (size); /* It might be a const variable or enumeration constant. */ size = decl_constant_value (size); /* Normally, the array-bound will be a constant. */ if (TREE_CODE (size) == INTEGER_CST) { /* Check to see if the array bound overflowed. Make that an error, no matter how generous we're being. */ int old_flag_pedantic_errors = flag_pedantic_errors; int old_pedantic = pedantic; pedantic = flag_pedantic_errors = 1; constant_expression_warning (size); pedantic = old_pedantic; flag_pedantic_errors = old_flag_pedantic_errors; /* An array must have a positive number of elements. */ if (INT_CST_LT (size, integer_zero_node)) { if (name) error ("size of array `%D' is negative", name); else error ("size of array is negative"); size = integer_one_node; } /* As an extension we allow zero-sized arrays. We always allow them in system headers because glibc uses them. */ else if (integer_zerop (size) && pedantic && !in_system_header) { if (name) pedwarn ("ISO C++ forbids zero-size array `%D'", name); else pedwarn ("ISO C++ forbids zero-size array"); } } else if (TREE_CONSTANT (size)) { /* `(int) &fn' is not a valid array bound. */ if (name) error ("size of array `%D' is not an integral constant-expression", name); else error ("size of array is not an integral constant-expression"); } else if (pedantic) { if (name) pedwarn ("ISO C++ forbids variable-size array `%D'", name); else pedwarn ("ISO C++ forbids variable-size array"); } if (processing_template_decl && !TREE_CONSTANT (size)) /* A variable sized array. */ itype = build_min (MINUS_EXPR, sizetype, size, integer_one_node); else { /* Compute the index of the largest element in the array. It is one less than the number of elements in the array. */ itype = fold (cp_build_binary_op (MINUS_EXPR, cp_convert (ssizetype, size), cp_convert (ssizetype, integer_one_node))); if (!TREE_CONSTANT (itype)) /* A variable sized array. */ itype = variable_size (itype); /* Make sure that there was no overflow when creating to a signed index type. (For example, on a 32-bit machine, an array with size 2^32 - 1 is too big.) */ else if (TREE_OVERFLOW (itype)) { error ("overflow in array dimension"); TREE_OVERFLOW (itype) = 0; } } /* Create and return the appropriate index type. */ return build_index_type (itype); } /* Returns the scope (if any) in which the entity declared by DECLARATOR will be located. If the entity was declared with an unqualified name, NULL_TREE is returned. */ tree get_scope_of_declarator (tree declarator) { if (!declarator) return NULL_TREE; switch (TREE_CODE (declarator)) { case CALL_EXPR: case ARRAY_REF: case INDIRECT_REF: case ADDR_EXPR: /* For any of these, the main declarator is the first operand. */ return get_scope_of_declarator (TREE_OPERAND (declarator, 0)); case SCOPE_REF: /* For a pointer-to-member, continue descending. */ if (TREE_CODE (TREE_OPERAND (declarator, 1)) == INDIRECT_REF) return get_scope_of_declarator (TREE_OPERAND (declarator, 1)); /* Otherwise, if the declarator-id is a SCOPE_REF, the scope in which the declaration occurs is the first operand. */ return TREE_OPERAND (declarator, 0); case TREE_LIST: /* Attributes to be applied. The declarator is TREE_VALUE. */ return get_scope_of_declarator (TREE_VALUE (declarator)); default: /* Otherwise, we have a declarator-id which is not a qualified name; the entity will be declared in the current scope. */ return NULL_TREE; } } /* Returns an ARRAY_TYPE for an array with SIZE elements of the indicated TYPE. If non-NULL, NAME is the NAME of the declaration with this type. */ static tree create_array_type_for_decl (tree name, tree type, tree size) { tree itype = NULL_TREE; const char* error_msg; /* If things have already gone awry, bail now. */ if (type == error_mark_node || size == error_mark_node) return error_mark_node; /* Assume that everything will go OK. */ error_msg = NULL; /* There are some types which cannot be array elements. */ switch (TREE_CODE (type)) { case VOID_TYPE: error_msg = "array of void"; break; case FUNCTION_TYPE: error_msg = "array of functions"; break; case REFERENCE_TYPE: error_msg = "array of references"; break; case METHOD_TYPE: error_msg = "array of function members"; break; default: break; } /* If something went wrong, issue an error-message and return. */ if (error_msg) { if (name) error ("declaration of `%D' as %s", name, error_msg); else error ("creating %s", error_msg); return error_mark_node; } /* [dcl.array] The constant expressions that specify the bounds of the arrays can be omitted only for the first member of the sequence. */ if (TREE_CODE (type) == ARRAY_TYPE && !TYPE_DOMAIN (type)) { if (name) error ("declaration of `%D' as multidimensional array must have bounds for all dimensions except the first", name); else error ("multidimensional array must have bounds for all dimensions except the first"); return error_mark_node; } /* Figure out the index type for the array. */ if (size) itype = compute_array_index_type (name, size); return build_cplus_array_type (type, itype); } /* Check that it's OK to declare a function with the indicated TYPE. SFK indicates the kind of special function (if any) that this function is. OPTYPE is the type given in a conversion operator declaration. Returns the actual return type of the function; that may be different than TYPE if an error occurs, or for certain special functions. */ static tree check_special_function_return_type (special_function_kind sfk, tree type, tree optype) { switch (sfk) { case sfk_constructor: if (type) error ("return type specification for constructor invalid"); type = void_type_node; break; case sfk_destructor: if (type) error ("return type specification for destructor invalid"); type = void_type_node; break; case sfk_conversion: if (type && !same_type_p (type, optype)) error ("operator `%T' declared to return `%T'", optype, type); else if (type) pedwarn ("return type specified for `operator %T'", optype); type = optype; break; default: abort (); break; } return type; } /* A variable or data member (whose unqualified name is IDENTIFIER) has been declared with the indicated TYPE. If the TYPE is not acceptable, issue an error message and return a type to use for error-recovery purposes. */ tree check_var_type (tree identifier, tree type) { if (VOID_TYPE_P (type)) { if (!identifier) error ("unnamed variable or field declared void"); else if (TREE_CODE (identifier) == IDENTIFIER_NODE) { if (IDENTIFIER_OPNAME_P (identifier)) abort (); error ("variable or field `%E' declared void", identifier); } else error ("variable or field declared void"); type = integer_type_node; } return type; } /* Given declspecs and a declarator (abstract or otherwise), determine the name and type of the object declared and construct a DECL node for it. DECLSPECS is a chain of tree_list nodes whose value fields are the storage classes and type specifiers. DECL_CONTEXT says which syntactic context this declaration is in: NORMAL for most contexts. Make a VAR_DECL or FUNCTION_DECL or TYPE_DECL. FUNCDEF for a function definition. Like NORMAL but a few different error messages in each case. Return value may be zero meaning this definition is too screwy to try to parse. MEMFUNCDEF for a function definition. Like FUNCDEF but prepares to handle member functions (which have FIELD context). Return value may be zero meaning this definition is too screwy to try to parse. PARM for a parameter declaration (either within a function prototype or before a function body). Make a PARM_DECL, or return void_type_node. CATCHPARM for a parameter declaration before a catch clause. TYPENAME if for a typename (in a cast or sizeof). Don't make a DECL node; just return the ..._TYPE node. FIELD for a struct or union field; make a FIELD_DECL. BITFIELD for a field with specified width. INITIALIZED is 1 if the decl has an initializer. ATTRLIST is a pointer to the list of attributes, which may be NULL if there are none; *ATTRLIST may be modified if attributes from inside the declarator should be applied to the declaration. When this function is called, scoping variables (such as CURRENT_CLASS_TYPE) should reflect the scope in which the declaration occurs, not the scope in which the new declaration will be placed. For example, on: void S::f() { ... } when grokdeclarator is called for `S::f', the CURRENT_CLASS_TYPE should not be `S'. */ tree grokdeclarator (tree declarator, tree declspecs, enum decl_context decl_context, int initialized, tree* attrlist) { RID_BIT_TYPE specbits; int nclasses = 0; tree spec; tree type = NULL_TREE; int longlong = 0; int type_quals; int virtualp, explicitp, friendp, inlinep, staticp; int explicit_int = 0; int explicit_char = 0; int defaulted_int = 0; int extern_langp = 0; tree dependant_name = NULL_TREE; tree typedef_decl = NULL_TREE; const char *name; tree typedef_type = NULL_TREE; int funcdef_flag = 0; enum tree_code innermost_code = ERROR_MARK; int bitfield = 0; #if 0 /* See the code below that used this. */ tree decl_attr = NULL_TREE; #endif /* Keep track of what sort of function is being processed so that we can warn about default return values, or explicit return values which do not match prescribed defaults. */ special_function_kind sfk = sfk_none; tree dname = NULL_TREE; tree ctype = current_class_type; tree ctor_return_type = NULL_TREE; enum overload_flags flags = NO_SPECIAL; tree quals = NULL_TREE; tree raises = NULL_TREE; int template_count = 0; tree in_namespace = NULL_TREE; tree returned_attrs = NULL_TREE; tree scope = NULL_TREE; tree parms = NULL_TREE; RIDBIT_RESET_ALL (specbits); if (decl_context == FUNCDEF) funcdef_flag = 1, decl_context = NORMAL; else if (decl_context == MEMFUNCDEF) funcdef_flag = -1, decl_context = FIELD; else if (decl_context == BITFIELD) bitfield = 1, decl_context = FIELD; /* Look inside a declarator for the name being declared and get it as a string, for an error message. */ { tree *next = &declarator; tree decl; name = NULL; while (next && *next) { decl = *next; switch (TREE_CODE (decl)) { case TREE_LIST: /* For attributes. */ next = &TREE_VALUE (decl); break; case COND_EXPR: ctype = NULL_TREE; next = &TREE_OPERAND (decl, 0); break; case BIT_NOT_EXPR: /* For C++ destructors! */ { tree name = TREE_OPERAND (decl, 0); tree rename = NULL_TREE; my_friendly_assert (flags == NO_SPECIAL, 152); flags = DTOR_FLAG; sfk = sfk_destructor; if (TYPE_P (name)) TREE_OPERAND (decl, 0) = name = constructor_name (name); my_friendly_assert (TREE_CODE (name) == IDENTIFIER_NODE, 153); if (ctype == NULL_TREE) { if (current_class_type == NULL_TREE) { error ("destructors must be member functions"); flags = NO_SPECIAL; } else { tree t = constructor_name (current_class_type); if (t != name) rename = t; } } else { tree t = constructor_name (ctype); if (t != name) rename = t; } if (rename) { error ("destructor `%T' must match class name `%T'", name, rename); TREE_OPERAND (decl, 0) = rename; } next = &name; } break; case ADDR_EXPR: /* C++ reference declaration */ /* Fall through. */ case ARRAY_REF: case INDIRECT_REF: ctype = NULL_TREE; innermost_code = TREE_CODE (decl); next = &TREE_OPERAND (decl, 0); break; case CALL_EXPR: innermost_code = TREE_CODE (decl); if (decl_context == FIELD && ctype == NULL_TREE) ctype = current_class_type; if (ctype && TREE_OPERAND (decl, 0) && (TREE_CODE (TREE_OPERAND (decl, 0)) == TYPE_DECL && constructor_name_p (DECL_NAME (TREE_OPERAND (decl, 0)), ctype))) TREE_OPERAND (decl, 0) = constructor_name (ctype); next = &TREE_OPERAND (decl, 0); decl = *next; if (ctype != NULL_TREE && decl != NULL_TREE && flags != DTOR_FLAG && constructor_name_p (decl, ctype)) { sfk = sfk_constructor; ctor_return_type = ctype; } ctype = NULL_TREE; break; case TEMPLATE_ID_EXPR: { tree fns = TREE_OPERAND (decl, 0); dname = fns; if (TREE_CODE (dname) == COMPONENT_REF) dname = TREE_OPERAND (dname, 1); if (TREE_CODE (dname) != IDENTIFIER_NODE) { my_friendly_assert (is_overloaded_fn (dname), 19990331); dname = DECL_NAME (get_first_fn (dname)); } } /* Fall through. */ case IDENTIFIER_NODE: if (TREE_CODE (decl) == IDENTIFIER_NODE) dname = decl; next = 0; if (C_IS_RESERVED_WORD (dname)) { error ("declarator-id missing; using reserved word `%D'", dname); name = IDENTIFIER_POINTER (dname); } else if (!IDENTIFIER_TYPENAME_P (dname)) name = IDENTIFIER_POINTER (dname); else { my_friendly_assert (flags == NO_SPECIAL, 154); flags = TYPENAME_FLAG; ctor_return_type = TREE_TYPE (dname); sfk = sfk_conversion; if (is_typename_at_global_scope (dname)) name = IDENTIFIER_POINTER (dname); else name = ""; } break; /* C++ extension */ case SCOPE_REF: { /* Perform error checking, and decide on a ctype. */ tree cname = TREE_OPERAND (decl, 0); if (cname == NULL_TREE) ctype = NULL_TREE; else if (TREE_CODE (cname) == NAMESPACE_DECL) { ctype = NULL_TREE; in_namespace = TREE_OPERAND (decl, 0); } else if (! is_aggr_type (cname, 1)) ctype = NULL_TREE; /* Must test TREE_OPERAND (decl, 1), in case user gives us `typedef (class::memfunc)(int); memfunc *memfuncptr;' */ else if (TREE_OPERAND (decl, 1) && TREE_CODE (TREE_OPERAND (decl, 1)) == INDIRECT_REF) ctype = cname; else if (TREE_CODE (cname) == TEMPLATE_TYPE_PARM || TREE_CODE (cname) == BOUND_TEMPLATE_TEMPLATE_PARM) { /* This might be declaring a member of a template parm to be a friend. */ ctype = cname; dependant_name = TREE_OPERAND (decl, 1); } else if (ctype == NULL_TREE) ctype = cname; else if (TREE_COMPLEXITY (decl) == current_class_depth) ; else { if (! UNIQUELY_DERIVED_FROM_P (cname, ctype)) { error ("type `%T' is not derived from type `%T'", cname, ctype); ctype = NULL_TREE; } else ctype = cname; } /* It is valid to write: class C { void f(); }; typedef C D; void D::f(); The standard is not clear about whether `typedef const C D' is legal; as of 2002-09-15 the committee is considering that question. EDG 3.0 allows that syntax. Therefore, we do as well. */ if (ctype) ctype = TYPE_MAIN_VARIANT (ctype); /* Update the declarator so that when we process it again the correct type is present. */ TREE_OPERAND (decl, 0) = ctype; if (ctype && TREE_CODE (TREE_OPERAND (decl, 1)) == TYPE_DECL && constructor_name_p (DECL_NAME (TREE_OPERAND (decl, 1)), ctype)) TREE_OPERAND (decl, 1) = constructor_name (ctype); next = &TREE_OPERAND (decl, 1); decl = *next; if (ctype) { tree name = decl; if (TREE_CODE (name) == BIT_NOT_EXPR) name = TREE_OPERAND (name, 0); if (!constructor_name_p (decl, ctype)) ; else if (decl == name) { sfk = sfk_constructor; ctor_return_type = ctype; } else { sfk = sfk_destructor; ctor_return_type = ctype; flags = DTOR_FLAG; TREE_OPERAND (decl, 0) = constructor_name (ctype); next = &TREE_OPERAND (decl, 0); } } } break; case ERROR_MARK: next = 0; break; case TYPE_DECL: /* Parse error puts this typespec where a declarator should go. */ error ("`%T' specified as declarator-id", DECL_NAME (decl)); if (TREE_TYPE (decl) == current_class_type) error (" perhaps you want `%T' for a constructor", current_class_name); dname = DECL_NAME (decl); name = IDENTIFIER_POINTER (dname); /* Avoid giving two errors for this. */ IDENTIFIER_CLASS_VALUE (dname) = NULL_TREE; declspecs = tree_cons (NULL_TREE, integer_type_node, declspecs); *next = dname; next = 0; break; case BASELINK: next = &BASELINK_FUNCTIONS (decl); break; case TEMPLATE_DECL: /* Sometimes, we see a template-name used as part of a decl-specifier like in std::allocator alloc; Handle that gracefully. */ error ("invalid use of template-name '%E' in a declarator", decl); return error_mark_node; break; default: my_friendly_assert (0, 20020917); } } } /* A function definition's declarator must have the form of a function declarator. */ if (funcdef_flag && innermost_code != CALL_EXPR) return 0; if (((dname && IDENTIFIER_OPNAME_P (dname)) || flags == TYPENAME_FLAG) && innermost_code != CALL_EXPR && ! (ctype && declspecs == NULL_TREE)) { error ("declaration of `%D' as non-function", dname); return void_type_node; } /* Anything declared one level down from the top level must be one of the parameters of a function (because the body is at least two levels down). */ /* This heuristic cannot be applied to C++ nodes! Fixed, however, by not allowing C++ class definitions to specify their parameters with xdecls (must be spec.d in the parmlist). Since we now wait to push a class scope until we are sure that we are in a legitimate method context, we must set oldcname explicitly (since current_class_name is not yet alive). We also want to avoid calling this a PARM if it is in a namespace. */ if (decl_context == NORMAL && !toplevel_bindings_p ()) { struct cp_binding_level *b = current_binding_level; current_binding_level = b->level_chain; if (current_binding_level != 0 && toplevel_bindings_p ()) decl_context = PARM; current_binding_level = b; } if (name == NULL) name = decl_context == PARM ? "parameter" : "type name"; /* Look through the decl specs and record which ones appear. Some typespecs are defined as built-in typenames. Others, the ones that are modifiers of other types, are represented by bits in SPECBITS: set the bits for the modifiers that appear. Storage class keywords are also in SPECBITS. If there is a typedef name or a type, store the type in TYPE. This includes builtin typedefs such as `int'. Set EXPLICIT_INT if the type is `int' or `char' and did not come from a user typedef. Set LONGLONG if `long' is mentioned twice. For C++, constructors and destructors have their own fast treatment. */ for (spec = declspecs; spec; spec = TREE_CHAIN (spec)) { int i; tree id; /* Certain parse errors slip through. For example, `int class;' is not caught by the parser. Try weakly to recover here. */ if (TREE_CODE (spec) != TREE_LIST) return 0; id = TREE_VALUE (spec); /* If the entire declaration is itself tagged as deprecated then suppress reports of deprecated items. */ if (!adding_implicit_members && id && TREE_DEPRECATED (id)) { if (deprecated_state != DEPRECATED_SUPPRESS) warn_deprecated_use (id); } if (TREE_CODE (id) == IDENTIFIER_NODE) { if (id == ridpointers[(int) RID_INT] || id == ridpointers[(int) RID_CHAR] || id == ridpointers[(int) RID_BOOL] || id == ridpointers[(int) RID_WCHAR]) { if (type) { if (id == ridpointers[(int) RID_BOOL]) error ("`bool' is now a keyword"); else error ("extraneous `%T' ignored", id); } else { if (id == ridpointers[(int) RID_INT]) explicit_int = 1; else if (id == ridpointers[(int) RID_CHAR]) explicit_char = 1; type = TREE_TYPE (IDENTIFIER_GLOBAL_VALUE (id)); } goto found; } /* C++ aggregate types. */ if (IDENTIFIER_HAS_TYPE_VALUE (id)) { if (type) error ("multiple declarations `%T' and `%T'", type, id); else type = IDENTIFIER_TYPE_VALUE (id); goto found; } for (i = (int) RID_FIRST_MODIFIER; i <= (int) RID_LAST_MODIFIER; i++) { if (ridpointers[i] == id) { if (i == (int) RID_LONG && RIDBIT_SETP (i, specbits)) { if (pedantic && ! in_system_header && warn_long_long) pedwarn ("ISO C++ does not support `long long'"); if (longlong) error ("`long long long' is too long for GCC"); else longlong = 1; } else if (RIDBIT_SETP (i, specbits)) pedwarn ("duplicate `%s'", IDENTIFIER_POINTER (id)); /* Diagnose "__thread extern" or "__thread static". */ if (RIDBIT_SETP (RID_THREAD, specbits)) { if (i == (int)RID_EXTERN) error ("`__thread' before `extern'"); else if (i == (int)RID_STATIC) error ("`__thread' before `static'"); } if (i == (int)RID_EXTERN && TREE_PURPOSE (spec) == error_mark_node) /* This extern was part of a language linkage. */ extern_langp = 1; RIDBIT_SET (i, specbits); goto found; } } } else if (TREE_CODE (id) == TYPE_DECL) { if (type) error ("multiple declarations `%T' and `%T'", type, TREE_TYPE (id)); else { type = TREE_TYPE (id); TREE_VALUE (spec) = type; typedef_decl = id; } goto found; } if (type) error ("two or more data types in declaration of `%s'", name); else if (TREE_CODE (id) == IDENTIFIER_NODE) { tree t = lookup_name (id, 1); if (!t || TREE_CODE (t) != TYPE_DECL) error ("`%s' fails to be a typedef or built in type", IDENTIFIER_POINTER (id)); else { type = TREE_TYPE (t); typedef_decl = t; } } else if (id != error_mark_node) /* Can't change CLASS nodes into RECORD nodes here! */ type = id; found: ; } #if 0 /* See the code below that used this. */ if (typedef_decl) decl_attr = DECL_ATTRIBUTES (typedef_decl); #endif typedef_type = type; /* No type at all: default to `int', and set DEFAULTED_INT because it was not a user-defined typedef. */ if (type == NULL_TREE && (RIDBIT_SETP (RID_SIGNED, specbits) || RIDBIT_SETP (RID_UNSIGNED, specbits) || RIDBIT_SETP (RID_LONG, specbits) || RIDBIT_SETP (RID_SHORT, specbits))) { /* These imply 'int'. */ type = integer_type_node; defaulted_int = 1; } if (sfk != sfk_none) type = check_special_function_return_type (sfk, type, ctor_return_type); else if (type == NULL_TREE) { int is_main; explicit_int = -1; /* We handle `main' specially here, because 'main () { }' is so common. With no options, it is allowed. With -Wreturn-type, it is a warning. It is only an error with -pedantic-errors. */ is_main = (funcdef_flag && dname && MAIN_NAME_P (dname) && ctype == NULL_TREE && in_namespace == NULL_TREE && current_namespace == global_namespace); if (in_system_header || flag_ms_extensions) /* Allow it, sigh. */; else if (pedantic || ! is_main) pedwarn ("ISO C++ forbids declaration of `%s' with no type", name); else if (warn_return_type) warning ("ISO C++ forbids declaration of `%s' with no type", name); type = integer_type_node; } ctype = NULL_TREE; /* Now process the modifiers that were specified and check for invalid combinations. */ /* Long double is a special combination. */ if (RIDBIT_SETP (RID_LONG, specbits) && TYPE_MAIN_VARIANT (type) == double_type_node) { RIDBIT_RESET (RID_LONG, specbits); type = build_qualified_type (long_double_type_node, cp_type_quals (type)); } /* Check all other uses of type modifiers. */ if (RIDBIT_SETP (RID_UNSIGNED, specbits) || RIDBIT_SETP (RID_SIGNED, specbits) || RIDBIT_SETP (RID_LONG, specbits) || RIDBIT_SETP (RID_SHORT, specbits)) { int ok = 0; if (TREE_CODE (type) == REAL_TYPE) error ("short, signed or unsigned invalid for `%s'", name); else if (TREE_CODE (type) != INTEGER_TYPE) error ("long, short, signed or unsigned invalid for `%s'", name); else if (RIDBIT_SETP (RID_LONG, specbits) && RIDBIT_SETP (RID_SHORT, specbits)) error ("long and short specified together for `%s'", name); else if ((RIDBIT_SETP (RID_LONG, specbits) || RIDBIT_SETP (RID_SHORT, specbits)) && explicit_char) error ("long or short specified with char for `%s'", name); else if ((RIDBIT_SETP (RID_LONG, specbits) || RIDBIT_SETP (RID_SHORT, specbits)) && TREE_CODE (type) == REAL_TYPE) error ("long or short specified with floating type for `%s'", name); else if (RIDBIT_SETP (RID_SIGNED, specbits) && RIDBIT_SETP (RID_UNSIGNED, specbits)) error ("signed and unsigned given together for `%s'", name); else { ok = 1; if (!explicit_int && !defaulted_int && !explicit_char && pedantic) { pedwarn ("long, short, signed or unsigned used invalidly for `%s'", name); if (flag_pedantic_errors) ok = 0; } } /* Discard the type modifiers if they are invalid. */ if (! ok) { RIDBIT_RESET (RID_UNSIGNED, specbits); RIDBIT_RESET (RID_SIGNED, specbits); RIDBIT_RESET (RID_LONG, specbits); RIDBIT_RESET (RID_SHORT, specbits); longlong = 0; } } if (RIDBIT_SETP (RID_COMPLEX, specbits) && TREE_CODE (type) != INTEGER_TYPE && TREE_CODE (type) != REAL_TYPE) { error ("complex invalid for `%s'", name); RIDBIT_RESET (RID_COMPLEX, specbits); } /* Decide whether an integer type is signed or not. Optionally treat bitfields as signed by default. */ if (RIDBIT_SETP (RID_UNSIGNED, specbits) /* [class.bit] It is implementation-defined whether a plain (neither explicitly signed or unsigned) char, short, int, or long bit-field is signed or unsigned. Naturally, we extend this to long long as well. Note that this does not include wchar_t. */ || (bitfield && !flag_signed_bitfields && RIDBIT_NOTSETP (RID_SIGNED, specbits) /* A typedef for plain `int' without `signed' can be controlled just like plain `int', but a typedef for `signed int' cannot be so controlled. */ && !(typedef_decl && C_TYPEDEF_EXPLICITLY_SIGNED (typedef_decl)) && (TREE_CODE (type) == INTEGER_TYPE || TREE_CODE (type) == CHAR_TYPE) && !same_type_p (TYPE_MAIN_VARIANT (type), wchar_type_node))) { if (longlong) type = long_long_unsigned_type_node; else if (RIDBIT_SETP (RID_LONG, specbits)) type = long_unsigned_type_node; else if (RIDBIT_SETP (RID_SHORT, specbits)) type = short_unsigned_type_node; else if (type == char_type_node) type = unsigned_char_type_node; else if (typedef_decl) type = c_common_unsigned_type (type); else type = unsigned_type_node; } else if (RIDBIT_SETP (RID_SIGNED, specbits) && type == char_type_node) type = signed_char_type_node; else if (longlong) type = long_long_integer_type_node; else if (RIDBIT_SETP (RID_LONG, specbits)) type = long_integer_type_node; else if (RIDBIT_SETP (RID_SHORT, specbits)) type = short_integer_type_node; if (RIDBIT_SETP (RID_COMPLEX, specbits)) { /* If we just have "complex", it is equivalent to "complex double", but if any modifiers at all are specified it is the complex form of TYPE. E.g, "complex short" is "complex short int". */ if (defaulted_int && ! longlong && ! (RIDBIT_SETP (RID_LONG, specbits) || RIDBIT_SETP (RID_SHORT, specbits) || RIDBIT_SETP (RID_SIGNED, specbits) || RIDBIT_SETP (RID_UNSIGNED, specbits))) type = complex_double_type_node; else if (type == integer_type_node) type = complex_integer_type_node; else if (type == float_type_node) type = complex_float_type_node; else if (type == double_type_node) type = complex_double_type_node; else if (type == long_double_type_node) type = complex_long_double_type_node; else type = build_complex_type (type); } type_quals = TYPE_UNQUALIFIED; if (RIDBIT_SETP (RID_CONST, specbits)) type_quals |= TYPE_QUAL_CONST; if (RIDBIT_SETP (RID_VOLATILE, specbits)) type_quals |= TYPE_QUAL_VOLATILE; if (RIDBIT_SETP (RID_RESTRICT, specbits)) type_quals |= TYPE_QUAL_RESTRICT; if (sfk == sfk_conversion && type_quals != TYPE_UNQUALIFIED) error ("qualifiers are not allowed on declaration of `operator %T'", ctor_return_type); type_quals |= cp_type_quals (type); type = cp_build_qualified_type_real (type, type_quals, ((typedef_decl && !DECL_ARTIFICIAL (typedef_decl) ? tf_ignore_bad_quals : 0) | tf_error | tf_warning)); /* We might have ignored or rejected some of the qualifiers. */ type_quals = cp_type_quals (type); staticp = 0; inlinep = !! RIDBIT_SETP (RID_INLINE, specbits); virtualp = RIDBIT_SETP (RID_VIRTUAL, specbits); RIDBIT_RESET (RID_VIRTUAL, specbits); explicitp = RIDBIT_SETP (RID_EXPLICIT, specbits) != 0; RIDBIT_RESET (RID_EXPLICIT, specbits); if (RIDBIT_SETP (RID_STATIC, specbits)) staticp = 1 + (decl_context == FIELD); if (virtualp && staticp == 2) { - error ("member `%D' cannot be declared both virtual and static", - dname); + error ("member `%D' cannot be declared both virtual and static", dname); + RIDBIT_RESET (RID_STATIC, specbits); staticp = 0; } friendp = RIDBIT_SETP (RID_FRIEND, specbits); RIDBIT_RESET (RID_FRIEND, specbits); if (dependant_name && !friendp) { error ("`%T::%D' is not a valid declarator", ctype, dependant_name); return void_type_node; } /* Warn if two storage classes are given. Default to `auto'. */ if (RIDBIT_ANY_SET (specbits)) { if (RIDBIT_SETP (RID_STATIC, specbits)) nclasses++; if (RIDBIT_SETP (RID_EXTERN, specbits) && !extern_langp) nclasses++; if (RIDBIT_SETP (RID_THREAD, specbits)) nclasses++; if (decl_context == PARM && nclasses > 0) error ("storage class specifiers invalid in parameter declarations"); if (RIDBIT_SETP (RID_TYPEDEF, specbits)) { if (decl_context == PARM) error ("typedef declaration invalid in parameter declaration"); nclasses++; } if (RIDBIT_SETP (RID_AUTO, specbits)) nclasses++; if (RIDBIT_SETP (RID_REGISTER, specbits)) nclasses++; if (!nclasses && !friendp && extern_langp) nclasses++; } /* Give error if `virtual' is used outside of class declaration. */ if (virtualp && (current_class_name == NULL_TREE || decl_context != FIELD)) { error ("virtual outside class declaration"); virtualp = 0; } /* Static anonymous unions are dealt with here. */ if (staticp && decl_context == TYPENAME && TREE_CODE (declspecs) == TREE_LIST && ANON_AGGR_TYPE_P (TREE_VALUE (declspecs))) decl_context = FIELD; /* Warn about storage classes that are invalid for certain kinds of declarations (parameters, typenames, etc.). */ /* "static __thread" and "extern __thread" are allowed. */ if (nclasses == 2 && RIDBIT_SETP (RID_THREAD, specbits) && (RIDBIT_SETP (RID_EXTERN, specbits) || RIDBIT_SETP (RID_STATIC, specbits))) nclasses = 1; if (nclasses > 1) error ("multiple storage classes in declaration of `%s'", name); else if (decl_context != NORMAL && nclasses > 0) { if ((decl_context == PARM || decl_context == CATCHPARM) && (RIDBIT_SETP (RID_REGISTER, specbits) || RIDBIT_SETP (RID_AUTO, specbits))) ; else if (RIDBIT_SETP (RID_TYPEDEF, specbits)) ; else if (decl_context == FIELD /* C++ allows static class elements. */ && RIDBIT_SETP (RID_STATIC, specbits)) /* C++ also allows inlines and signed and unsigned elements, but in those cases we don't come in here. */ ; else { if (decl_context == FIELD) { tree tmp = NULL_TREE; int op = 0; if (declarator) { /* Avoid trying to get an operand off an identifier node. */ if (TREE_CODE (declarator) == IDENTIFIER_NODE) tmp = declarator; else tmp = TREE_OPERAND (declarator, 0); op = IDENTIFIER_OPNAME_P (tmp); if (IDENTIFIER_TYPENAME_P (tmp)) { if (is_typename_at_global_scope (tmp)) name = IDENTIFIER_POINTER (tmp); else name = ""; } } error ("storage class specified for %s `%s'", op ? "member operator" : "field", name); } else { if (decl_context == PARM || decl_context == CATCHPARM) error ("storage class specified for parameter `%s'", name); else error ("storage class specified for typename"); } RIDBIT_RESET (RID_REGISTER, specbits); RIDBIT_RESET (RID_AUTO, specbits); RIDBIT_RESET (RID_EXTERN, specbits); RIDBIT_RESET (RID_THREAD, specbits); } } else if (RIDBIT_SETP (RID_EXTERN, specbits) && initialized && !funcdef_flag) { if (toplevel_bindings_p ()) { /* It's common practice (and completely valid) to have a const be initialized and declared extern. */ if (!(type_quals & TYPE_QUAL_CONST)) warning ("`%s' initialized and declared `extern'", name); } else error ("`%s' has both `extern' and initializer", name); } else if (RIDBIT_SETP (RID_EXTERN, specbits) && funcdef_flag && ! toplevel_bindings_p ()) error ("nested function `%s' declared `extern'", name); else if (toplevel_bindings_p ()) { if (RIDBIT_SETP (RID_AUTO, specbits)) error ("top-level declaration of `%s' specifies `auto'", name); } else if (RIDBIT_SETP (RID_THREAD, specbits) && !RIDBIT_SETP (RID_EXTERN, specbits) && !RIDBIT_SETP (RID_STATIC, specbits)) { error ("function-scope `%s' implicitly auto and declared `__thread'", name); RIDBIT_RESET (RID_THREAD, specbits); } if (nclasses > 0 && friendp) error ("storage class specifiers invalid in friend function declarations"); scope = get_scope_of_declarator (declarator); /* Now figure out the structure of the declarator proper. Descend through it, creating more complex types, until we reach the declared identifier (or NULL_TREE, in an abstract declarator). */ while (declarator && TREE_CODE (declarator) != IDENTIFIER_NODE && TREE_CODE (declarator) != TEMPLATE_ID_EXPR) { /* Each level of DECLARATOR is either an ARRAY_REF (for ...[..]), an INDIRECT_REF (for *...), a CALL_EXPR (for ...(...)), an identifier (for the name being declared) or a null pointer (for the place in an absolute declarator where the name was omitted). For the last two cases, we have just exited the loop. For C++ it could also be a SCOPE_REF (for class :: ...). In this case, we have converted sensible names to types, and those are the values we use to qualify the member name. an ADDR_EXPR (for &...), a BIT_NOT_EXPR (for destructors) At this point, TYPE is the type of elements of an array, or for a function to return, or for a pointer to point to. After this sequence of ifs, TYPE is the type of the array or function or pointer, and DECLARATOR has had its outermost layer removed. */ if (type == error_mark_node) { if (declarator == error_mark_node) return error_mark_node; else if (TREE_CODE (declarator) == SCOPE_REF) declarator = TREE_OPERAND (declarator, 1); else declarator = TREE_OPERAND (declarator, 0); continue; } if (quals != NULL_TREE && (declarator == NULL_TREE || TREE_CODE (declarator) != SCOPE_REF)) { if (ctype == NULL_TREE && TREE_CODE (type) == METHOD_TYPE) ctype = TYPE_METHOD_BASETYPE (type); if (ctype != NULL_TREE) { tree dummy = build_decl (TYPE_DECL, NULL_TREE, type); grok_method_quals (ctype, dummy, quals); type = TREE_TYPE (dummy); quals = NULL_TREE; } } switch (TREE_CODE (declarator)) { case TREE_LIST: { /* We encode a declarator with embedded attributes using a TREE_LIST. */ tree attrs = TREE_PURPOSE (declarator); tree inner_decl; int attr_flags; declarator = TREE_VALUE (declarator); inner_decl = declarator; while (inner_decl != NULL_TREE && TREE_CODE (inner_decl) == TREE_LIST) inner_decl = TREE_VALUE (inner_decl); attr_flags = 0; if (inner_decl == NULL_TREE || TREE_CODE (inner_decl) == IDENTIFIER_NODE) attr_flags |= (int) ATTR_FLAG_DECL_NEXT; if (TREE_CODE (inner_decl) == CALL_EXPR) attr_flags |= (int) ATTR_FLAG_FUNCTION_NEXT; if (TREE_CODE (inner_decl) == ARRAY_REF) attr_flags |= (int) ATTR_FLAG_ARRAY_NEXT; returned_attrs = decl_attributes (&type, chainon (returned_attrs, attrs), attr_flags); } break; case ARRAY_REF: { tree size = TREE_OPERAND (declarator, 1); declarator = TREE_OPERAND (declarator, 0); type = create_array_type_for_decl (dname, type, size); ctype = NULL_TREE; } break; case CALL_EXPR: { tree arg_types; int funcdecl_p; tree inner_parms = CALL_DECLARATOR_PARMS (declarator); tree inner_decl = TREE_OPERAND (declarator, 0); /* Declaring a function type. Make sure we have a valid type for the function to return. */ /* We now know that the TYPE_QUALS don't apply to the decl, but to its return type. */ type_quals = TYPE_UNQUALIFIED; /* Warn about some types functions can't return. */ if (TREE_CODE (type) == FUNCTION_TYPE) { error ("`%s' declared as function returning a function", name); type = integer_type_node; } if (TREE_CODE (type) == ARRAY_TYPE) { error ("`%s' declared as function returning an array", name); type = integer_type_node; } if (inner_decl && TREE_CODE (inner_decl) == SCOPE_REF) inner_decl = TREE_OPERAND (inner_decl, 1); if (inner_decl && TREE_CODE (inner_decl) == TEMPLATE_ID_EXPR) inner_decl = dname; /* Pick up type qualifiers which should be applied to `this'. */ quals = CALL_DECLARATOR_QUALS (declarator); /* Pick up the exception specifications. */ raises = CALL_DECLARATOR_EXCEPTION_SPEC (declarator); /* Say it's a definition only for the CALL_EXPR closest to the identifier. */ funcdecl_p = inner_decl && (TREE_CODE (inner_decl) == IDENTIFIER_NODE || TREE_CODE (inner_decl) == TEMPLATE_ID_EXPR || TREE_CODE (inner_decl) == BIT_NOT_EXPR); if (ctype == NULL_TREE && decl_context == FIELD && funcdecl_p && (friendp == 0 || dname == current_class_name)) ctype = current_class_type; if (ctype && sfk == sfk_conversion) TYPE_HAS_CONVERSION (ctype) = 1; if (ctype && constructor_name_p (dname, ctype)) { /* We are within a class's scope. If our declarator name is the same as the class name, and we are defining a function, then it is a constructor/destructor, and therefore returns a void type. */ if (flags == DTOR_FLAG) { /* ISO C++ 12.4/2. A destructor may not be declared const or volatile. A destructor may not be static. */ if (staticp == 2) error ("destructor cannot be static member function"); if (quals) { error ("destructors may not be `%s'", IDENTIFIER_POINTER (TREE_VALUE (quals))); quals = NULL_TREE; } if (decl_context == FIELD) { if (! member_function_or_else (ctype, current_class_type, flags)) return void_type_node; } } else /* It's a constructor. */ { if (explicitp == 1) explicitp = 2; /* ISO C++ 12.1. A constructor may not be declared const or volatile. A constructor may not be virtual. A constructor may not be static. */ if (staticp == 2) error ("constructor cannot be static member function"); if (virtualp) { pedwarn ("constructors cannot be declared virtual"); virtualp = 0; } if (quals) { error ("constructors may not be `%s'", IDENTIFIER_POINTER (TREE_VALUE (quals))); quals = NULL_TREE; } { RID_BIT_TYPE tmp_bits; memcpy (&tmp_bits, &specbits, sizeof (RID_BIT_TYPE)); RIDBIT_RESET (RID_INLINE, tmp_bits); RIDBIT_RESET (RID_STATIC, tmp_bits); if (RIDBIT_ANY_SET (tmp_bits)) error ("return value type specifier for constructor ignored"); } if (decl_context == FIELD) { if (! member_function_or_else (ctype, current_class_type, flags)) return void_type_node; TYPE_HAS_CONSTRUCTOR (ctype) = 1; if (sfk != sfk_constructor) return NULL_TREE; } } if (decl_context == FIELD) staticp = 0; } else if (friendp) { if (initialized) error ("can't initialize friend function `%s'", name); if (virtualp) { /* Cannot be both friend and virtual. */ error ("virtual functions cannot be friends"); RIDBIT_RESET (RID_FRIEND, specbits); friendp = 0; } if (decl_context == NORMAL) error ("friend declaration not in class definition"); if (current_function_decl && funcdef_flag) error ("can't define friend function `%s' in a local class definition", name); } /* Construct the function type and go to the next inner layer of declarator. */ declarator = TREE_OPERAND (declarator, 0); arg_types = grokparms (inner_parms, &parms); if (declarator && flags == DTOR_FLAG) { /* A destructor declared in the body of a class will be represented as a BIT_NOT_EXPR. But, we just want the underlying IDENTIFIER. */ if (TREE_CODE (declarator) == BIT_NOT_EXPR) declarator = TREE_OPERAND (declarator, 0); if (arg_types != void_list_node) { error ("destructors may not have parameters"); arg_types = void_list_node; parms = NULL_TREE; } } /* ANSI says that `const int foo ();' does not make the function foo const. */ type = build_function_type (type, arg_types); } break; case ADDR_EXPR: case INDIRECT_REF: /* Filter out pointers-to-references and references-to-references. We can get these if a TYPE_DECL is used. */ if (TREE_CODE (type) == REFERENCE_TYPE) { error (TREE_CODE (declarator) == ADDR_EXPR ? "cannot declare reference to `%#T'" : "cannot declare pointer to `%#T'", type); type = TREE_TYPE (type); } else if (VOID_TYPE_P (type) && (ctype || TREE_CODE (declarator) == ADDR_EXPR)) error (ctype ? "cannot declare pointer to `%#T' member" : "cannot declare reference to `%#T'", type); /* Merge any constancy or volatility into the target type for the pointer. */ /* We now know that the TYPE_QUALS don't apply to the decl, but to the target of the pointer. */ type_quals = TYPE_UNQUALIFIED; if (TREE_CODE (declarator) == ADDR_EXPR) { if (!VOID_TYPE_P (type)) type = build_reference_type (type); } else if (TREE_CODE (type) == METHOD_TYPE) type = build_ptrmemfunc_type (build_pointer_type (type)); else if (ctype) type = build_ptrmem_type (ctype, type); else type = build_pointer_type (type); /* Process a list of type modifier keywords (such as const or volatile) that were given inside the `*' or `&'. */ if (TREE_TYPE (declarator)) { tree typemodlist; int erred = 0; int constp = 0; int volatilep = 0; int restrictp = 0; for (typemodlist = TREE_TYPE (declarator); typemodlist; typemodlist = TREE_CHAIN (typemodlist)) { tree qualifier = TREE_VALUE (typemodlist); if (qualifier == ridpointers[(int) RID_CONST]) { constp++; type_quals |= TYPE_QUAL_CONST; } else if (qualifier == ridpointers[(int) RID_VOLATILE]) { volatilep++; type_quals |= TYPE_QUAL_VOLATILE; } else if (qualifier == ridpointers[(int) RID_RESTRICT]) { restrictp++; type_quals |= TYPE_QUAL_RESTRICT; } else if (!erred) { erred = 1; error ("invalid type modifier within pointer declarator"); } } if (constp > 1) pedwarn ("duplicate `const'"); if (volatilep > 1) pedwarn ("duplicate `volatile'"); if (restrictp > 1) pedwarn ("duplicate `restrict'"); type = cp_build_qualified_type (type, type_quals); type_quals = cp_type_quals (type); } declarator = TREE_OPERAND (declarator, 0); ctype = NULL_TREE; break; case SCOPE_REF: { /* We have converted type names to NULL_TREE if the name was bogus, or to a _TYPE node, if not. The variable CTYPE holds the type we will ultimately resolve to. The code here just needs to build up appropriate member types. */ tree sname = TREE_OPERAND (declarator, 1); tree t; /* Destructors can have their visibilities changed as well. */ if (TREE_CODE (sname) == BIT_NOT_EXPR) sname = TREE_OPERAND (sname, 0); if (TREE_OPERAND (declarator, 0) == NULL_TREE) { /* We had a reference to a global decl, or perhaps we were given a non-aggregate typedef, in which case we cleared this out, and should just keep going as though it wasn't there. */ declarator = sname; continue; } ctype = TREE_OPERAND (declarator, 0); t = ctype; if (TREE_CODE (TREE_OPERAND (declarator, 1)) != INDIRECT_REF) while (t != NULL_TREE && CLASS_TYPE_P (t)) { /* You're supposed to have one `template <...>' for every template class, but you don't need one for a full specialization. For example: template struct S{}; template <> struct S { void f(); }; void S::f () {} is correct; there shouldn't be a `template <>' for the definition of `S::f'. */ if (CLASSTYPE_TEMPLATE_INFO (t) && (CLASSTYPE_TEMPLATE_INSTANTIATION (t) || uses_template_parms (CLASSTYPE_TI_ARGS (t))) && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (t))) template_count += 1; t = TYPE_MAIN_DECL (t); t = DECL_CONTEXT (t); } if (sname == NULL_TREE) goto done_scoping; if (TREE_CODE (sname) == IDENTIFIER_NODE) { /* This is the `standard' use of the scoping operator: basetype :: member . */ if (ctype == current_class_type) { /* class A { void A::f (); }; Is this ill-formed? */ if (pedantic) pedwarn ("extra qualification `%T::' on member `%s' ignored", ctype, name); } else if (TREE_CODE (type) == FUNCTION_TYPE) { if (NEW_DELETE_OPNAME_P (sname)) /* Overloaded operator new and operator delete are always static functions. */ ; else if (current_class_type == NULL_TREE || friendp) type = build_method_type_directly (ctype, TREE_TYPE (type), TYPE_ARG_TYPES (type)); else { error ("cannot declare member function `%T::%s' within `%T'", ctype, name, current_class_type); return error_mark_node; } } else if (RIDBIT_SETP (RID_TYPEDEF, specbits) || COMPLETE_TYPE_P (complete_type (ctype))) { /* Have to move this code elsewhere in this function. this code is used for i.e., typedef int A::M; M *pm; It is? How? jason 10/2/94 */ if (current_class_type) { error ("cannot declare member `%T::%s' within `%T'", ctype, name, current_class_type); return void_type_node; } } else { cxx_incomplete_type_error (NULL_TREE, ctype); return error_mark_node; } declarator = sname; } else if (TREE_CODE (sname) == SCOPE_REF) abort (); else { done_scoping: declarator = TREE_OPERAND (declarator, 1); if (declarator && TREE_CODE (declarator) == CALL_EXPR) /* In this case, we will deal with it later. */ ; else if (TREE_CODE (type) == FUNCTION_TYPE) type = build_method_type_directly (ctype, TREE_TYPE (type), TYPE_ARG_TYPES (type)); } } break; case BIT_NOT_EXPR: declarator = TREE_OPERAND (declarator, 0); break; case BASELINK: declarator = BASELINK_FUNCTIONS (declarator); break; case RECORD_TYPE: case UNION_TYPE: case ENUMERAL_TYPE: declarator = NULL_TREE; break; case ERROR_MARK: declarator = NULL_TREE; break; default: abort (); } } if (returned_attrs) { if (attrlist) *attrlist = chainon (returned_attrs, *attrlist); else attrlist = &returned_attrs; } /* Now TYPE has the actual type. */ /* Did array size calculations overflow? */ if (TREE_CODE (type) == ARRAY_TYPE && COMPLETE_TYPE_P (type) && TREE_OVERFLOW (TYPE_SIZE (type))) { error ("size of array `%s' is too large", name); /* If we proceed with the array type as it is, we'll eventually crash in tree_low_cst(). */ type = error_mark_node; } if ((decl_context == FIELD || decl_context == PARM) && !processing_template_decl && variably_modified_type_p (type)) { if (decl_context == FIELD) error ("data member may not have variably modified type `%T'", type); else error ("parameter may not have variably modified type `%T'", type); type = error_mark_node; } if (explicitp == 1 || (explicitp && friendp)) { /* [dcl.fct.spec] The explicit specifier shall only be used in declarations of constructors within a class definition. */ error ("only declarations of constructors can be `explicit'"); explicitp = 0; } if (RIDBIT_SETP (RID_MUTABLE, specbits)) { if (decl_context != FIELD || friendp) { error ("non-member `%s' cannot be declared `mutable'", name); RIDBIT_RESET (RID_MUTABLE, specbits); } else if (decl_context == TYPENAME || RIDBIT_SETP (RID_TYPEDEF, specbits)) { error ("non-object member `%s' cannot be declared `mutable'", name); RIDBIT_RESET (RID_MUTABLE, specbits); } else if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE) { error ("function `%s' cannot be declared `mutable'", name); RIDBIT_RESET (RID_MUTABLE, specbits); } else if (staticp) { error ("static `%s' cannot be declared `mutable'", name); RIDBIT_RESET (RID_MUTABLE, specbits); } else if (type_quals & TYPE_QUAL_CONST) { error ("const `%s' cannot be declared `mutable'", name); RIDBIT_RESET (RID_MUTABLE, specbits); } } if (declarator == NULL_TREE || TREE_CODE (declarator) == IDENTIFIER_NODE || (TREE_CODE (declarator) == TEMPLATE_ID_EXPR && (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE))) /* OK */; else if (TREE_CODE (declarator) == TEMPLATE_ID_EXPR) { error ("template-id `%D' used as a declarator", declarator); declarator = dname; } else /* Unexpected declarator format. */ abort (); /* If this is declaring a typedef name, return a TYPE_DECL. */ if (RIDBIT_SETP (RID_TYPEDEF, specbits) && decl_context != TYPENAME) { tree decl; /* Note that the grammar rejects storage classes in typenames, fields or parameters. */ if (current_lang_name == lang_name_java) TYPE_FOR_JAVA (type) = 1; if (decl_context == FIELD) { if (constructor_name_p (declarator, current_class_type)) pedwarn ("ISO C++ forbids nested type `%D' with same name as enclosing class", declarator); decl = build_lang_decl (TYPE_DECL, declarator, type); } else { decl = build_decl (TYPE_DECL, declarator, type); if (in_namespace || ctype) error ("%Jtypedef name may not be a nested-name-specifier", decl); if (!current_function_decl) DECL_CONTEXT (decl) = FROB_CONTEXT (current_namespace); } /* If the user declares "typedef struct {...} foo" then the struct will have an anonymous name. Fill that name in now. Nothing can refer to it, so nothing needs know about the name change. */ if (type != error_mark_node && declarator && TYPE_NAME (type) && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL && TYPE_ANONYMOUS_P (type) /* Don't do this if there are attributes. */ && (!attrlist || !*attrlist) && cp_type_quals (type) == TYPE_UNQUALIFIED) { tree oldname = TYPE_NAME (type); tree t; /* Replace the anonymous name with the real name everywhere. */ lookup_tag_reverse (type, declarator); for (t = TYPE_MAIN_VARIANT (type); t; t = TYPE_NEXT_VARIANT (t)) if (TYPE_NAME (t) == oldname) TYPE_NAME (t) = decl; if (TYPE_LANG_SPECIFIC (type)) TYPE_WAS_ANONYMOUS (type) = 1; /* If this is a typedef within a template class, the nested type is a (non-primary) template. The name for the template needs updating as well. */ if (TYPE_LANG_SPECIFIC (type) && CLASSTYPE_TEMPLATE_INFO (type)) DECL_NAME (CLASSTYPE_TI_TEMPLATE (type)) = TYPE_IDENTIFIER (type); /* FIXME remangle member functions; member functions of a type with external linkage have external linkage. */ } if (quals) { if (ctype == NULL_TREE) { if (TREE_CODE (type) != METHOD_TYPE) error ("%Jinvalid type qualifier for non-member function type", decl); else ctype = TYPE_METHOD_BASETYPE (type); } if (ctype != NULL_TREE) grok_method_quals (ctype, decl, quals); } if (RIDBIT_SETP (RID_SIGNED, specbits) || (typedef_decl && C_TYPEDEF_EXPLICITLY_SIGNED (typedef_decl))) C_TYPEDEF_EXPLICITLY_SIGNED (decl) = 1; bad_specifiers (decl, "type", virtualp, quals != NULL_TREE, inlinep, friendp, raises != NULL_TREE); return decl; } /* Detect the case of an array type of unspecified size which came, as such, direct from a typedef name. We must copy the type, so that the array's domain can be individually set by the object's initializer. */ if (type && typedef_type && TREE_CODE (type) == ARRAY_TYPE && !TYPE_DOMAIN (type) && TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (typedef_type)) type = build_cplus_array_type (TREE_TYPE (type), NULL_TREE); /* Detect where we're using a typedef of function type to declare a function. PARMS will not be set, so we must create it now. */ if (type == typedef_type && TREE_CODE (type) == FUNCTION_TYPE) { tree decls = NULL_TREE; tree args; for (args = TYPE_ARG_TYPES (type); args; args = TREE_CHAIN (args)) { tree decl = cp_build_parm_decl (NULL_TREE, TREE_VALUE (args)); TREE_CHAIN (decl) = decls; decls = decl; } parms = nreverse (decls); } /* If this is a type name (such as, in a cast or sizeof), compute the type and return it now. */ if (decl_context == TYPENAME) { /* Note that the grammar rejects storage classes in typenames, fields or parameters. */ if (type_quals != TYPE_UNQUALIFIED) type_quals = TYPE_UNQUALIFIED; /* Special case: "friend class foo" looks like a TYPENAME context. */ if (friendp) { if (type_quals != TYPE_UNQUALIFIED) { error ("type qualifiers specified for friend class declaration"); type_quals = TYPE_UNQUALIFIED; } if (inlinep) { error ("`inline' specified for friend class declaration"); inlinep = 0; } if (!current_aggr) { /* Don't allow friend declaration without a class-key. */ if (TREE_CODE (type) == TEMPLATE_TYPE_PARM) pedwarn ("template parameters cannot be friends"); else if (TREE_CODE (type) == TYPENAME_TYPE) pedwarn ("friend declaration requires class-key, " "i.e. `friend class %T::%D'", TYPE_CONTEXT (type), TYPENAME_TYPE_FULLNAME (type)); else pedwarn ("friend declaration requires class-key, " "i.e. `friend %#T'", type); } /* Only try to do this stuff if we didn't already give up. */ if (type != integer_type_node) { /* A friendly class? */ if (current_class_type) make_friend_class (current_class_type, TYPE_MAIN_VARIANT (type), /*complain=*/true); else error ("trying to make class `%T' a friend of global scope", type); type = void_type_node; } } else if (quals) { if (ctype == NULL_TREE) { if (TREE_CODE (type) != METHOD_TYPE) error ("invalid qualifiers on non-member function type"); else ctype = TYPE_METHOD_BASETYPE (type); } if (ctype) { tree dummy = build_decl (TYPE_DECL, declarator, type); grok_method_quals (ctype, dummy, quals); type = TREE_TYPE (dummy); } } return type; } else if (declarator == NULL_TREE && decl_context != PARM && decl_context != CATCHPARM && TREE_CODE (type) != UNION_TYPE && ! bitfield) { error ("abstract declarator `%T' used as declaration", type); return error_mark_node; } /* Only functions may be declared using an operator-function-id. */ if (declarator && TREE_CODE (declarator) == IDENTIFIER_NODE && IDENTIFIER_OPNAME_P (declarator) && TREE_CODE (type) != FUNCTION_TYPE && TREE_CODE (type) != METHOD_TYPE) { error ("declaration of `%D' as non-function", declarator); return error_mark_node; } /* We don't check parameter types here because we can emit a better error message later. */ if (decl_context != PARM) type = check_var_type (declarator, type); /* Now create the decl, which may be a VAR_DECL, a PARM_DECL or a FUNCTION_DECL, depending on DECL_CONTEXT and TYPE. */ if (decl_context == PARM || decl_context == CATCHPARM) { if (ctype || in_namespace) error ("cannot use `::' in parameter declaration"); /* A parameter declared as an array of T is really a pointer to T. One declared as a function is really a pointer to a function. One declared as a member is really a pointer to member. */ if (TREE_CODE (type) == ARRAY_TYPE) { /* Transfer const-ness of array into that of type pointed to. */ type = build_pointer_type (TREE_TYPE (type)); type_quals = TYPE_UNQUALIFIED; } else if (TREE_CODE (type) == FUNCTION_TYPE) type = build_pointer_type (type); } { tree decl; if (decl_context == PARM) { decl = cp_build_parm_decl (declarator, type); bad_specifiers (decl, "parameter", virtualp, quals != NULL_TREE, inlinep, friendp, raises != NULL_TREE); } else if (decl_context == FIELD) { /* The C99 flexible array extension. */ if (!staticp && TREE_CODE (type) == ARRAY_TYPE && TYPE_DOMAIN (type) == NULL_TREE) { tree itype = compute_array_index_type (dname, integer_zero_node); type = build_cplus_array_type (TREE_TYPE (type), itype); } if (type == error_mark_node) { /* Happens when declaring arrays of sizes which are error_mark_node, for example. */ decl = NULL_TREE; } else if (in_namespace && !friendp) { /* Something like struct S { int N::j; }; */ error ("invalid use of `::'"); decl = NULL_TREE; } else if (TREE_CODE (type) == FUNCTION_TYPE) { int publicp = 0; tree function_context; /* We catch the others as conflicts with the builtin typedefs. */ if (friendp && declarator == ridpointers[(int) RID_SIGNED]) { error ("function `%D' cannot be declared friend", declarator); friendp = 0; } if (friendp == 0) { if (ctype == NULL_TREE) ctype = current_class_type; if (ctype == NULL_TREE) { error ("can't make `%D' into a method -- not in a class", declarator); return void_type_node; } /* ``A union may [ ... ] not [ have ] virtual functions.'' ARM 9.5 */ if (virtualp && TREE_CODE (ctype) == UNION_TYPE) { error ("function `%D' declared virtual inside a union", declarator); return void_type_node; } if (NEW_DELETE_OPNAME_P (declarator)) { if (virtualp) { error ("`%D' cannot be declared virtual, since it is always static", declarator); virtualp = 0; } } else if (staticp < 2) type = build_method_type_directly (ctype, TREE_TYPE (type), TYPE_ARG_TYPES (type)); } /* Tell grokfndecl if it needs to set TREE_PUBLIC on the node. */ function_context = (ctype != NULL_TREE) ? decl_function_context (TYPE_MAIN_DECL (ctype)) : NULL_TREE; publicp = (! friendp || ! staticp) && function_context == NULL_TREE; decl = grokfndecl (ctype, type, TREE_CODE (declarator) != TEMPLATE_ID_EXPR ? declarator : dname, parms, declarator, virtualp, flags, quals, raises, friendp ? -1 : 0, friendp, publicp, inlinep, funcdef_flag, template_count, in_namespace); if (decl == NULL_TREE) return decl; #if 0 /* This clobbers the attrs stored in `decl' from `attrlist'. */ /* The decl and setting of decl_attr is also turned off. */ decl = build_decl_attribute_variant (decl, decl_attr); #endif /* [class.conv.ctor] A constructor declared without the function-specifier explicit that can be called with a single parameter specifies a conversion from the type of its first parameter to the type of its class. Such a constructor is called a converting constructor. */ if (explicitp == 2) DECL_NONCONVERTING_P (decl) = 1; else if (DECL_CONSTRUCTOR_P (decl)) { /* The constructor can be called with exactly one parameter if there is at least one parameter, and any subsequent parameters have default arguments. Ignore any compiler-added parms. */ tree arg_types = FUNCTION_FIRST_USER_PARMTYPE (decl); if (arg_types == void_list_node || (arg_types && TREE_CHAIN (arg_types) && TREE_CHAIN (arg_types) != void_list_node && !TREE_PURPOSE (TREE_CHAIN (arg_types)))) DECL_NONCONVERTING_P (decl) = 1; } } else if (TREE_CODE (type) == METHOD_TYPE) { /* We only get here for friend declarations of members of other classes. */ /* All method decls are public, so tell grokfndecl to set TREE_PUBLIC, also. */ decl = grokfndecl (ctype, type, TREE_CODE (declarator) != TEMPLATE_ID_EXPR ? declarator : dname, parms, declarator, virtualp, flags, quals, raises, friendp ? -1 : 0, friendp, 1, 0, funcdef_flag, template_count, in_namespace); if (decl == NULL_TREE) return NULL_TREE; } else if (!staticp && !dependent_type_p (type) && !COMPLETE_TYPE_P (complete_type (type)) && (TREE_CODE (type) != ARRAY_TYPE || initialized == 0)) { if (declarator) error ("field `%D' has incomplete type", declarator); else error ("name `%T' has incomplete type", type); /* If we're instantiating a template, tell them which instantiation made the field's type be incomplete. */ if (current_class_type && TYPE_NAME (current_class_type) && IDENTIFIER_TEMPLATE (TYPE_IDENTIFIER (current_class_type)) && declspecs && TREE_VALUE (declspecs) && TREE_TYPE (TREE_VALUE (declspecs)) == type) error (" in instantiation of template `%T'", current_class_type); type = error_mark_node; decl = NULL_TREE; } else { if (friendp) { error ("`%s' is neither function nor member function; cannot be declared friend", IDENTIFIER_POINTER (declarator)); friendp = 0; } decl = NULL_TREE; } if (friendp) { /* Friends are treated specially. */ if (ctype == current_class_type) warning ("member functions are implicitly friends of their class"); else if (decl && DECL_NAME (decl)) { if (template_class_depth (current_class_type) == 0) { decl = check_explicit_specialization (declarator, decl, template_count, 2 * (funcdef_flag != 0) + 4); if (decl == error_mark_node) return error_mark_node; } decl = do_friend (ctype, declarator, decl, *attrlist, flags, quals, funcdef_flag); return decl; } else return void_type_node; } /* Structure field. It may not be a function, except for C++. */ if (decl == NULL_TREE) { if (initialized) { if (!staticp) { /* An attempt is being made to initialize a non-static member. But, from [class.mem]: 4 A member-declarator can contain a constant-initializer only if it declares a static member (_class.static_) of integral or enumeration type, see _class.static.data_. This used to be relatively common practice, but the rest of the compiler does not correctly handle the initialization unless the member is static so we make it static below. */ pedwarn ("ISO C++ forbids initialization of member `%D'", declarator); pedwarn ("making `%D' static", declarator); staticp = 1; } if (uses_template_parms (type)) /* We'll check at instantiation time. */ ; else if (check_static_variable_definition (declarator, type)) /* If we just return the declaration, crashes will sometimes occur. We therefore return void_type_node, as if this was a friend declaration, to cause callers to completely ignore this declaration. */ return void_type_node; } if (staticp) { /* C++ allows static class members. All other work for this is done by grokfield. */ decl = build_lang_decl (VAR_DECL, declarator, type); TREE_STATIC (decl) = 1; /* In class context, 'static' means public access. */ TREE_PUBLIC (decl) = DECL_EXTERNAL (decl) = 1; } else { decl = build_decl (FIELD_DECL, declarator, type); DECL_NONADDRESSABLE_P (decl) = bitfield; if (RIDBIT_SETP (RID_MUTABLE, specbits)) { DECL_MUTABLE_P (decl) = 1; RIDBIT_RESET (RID_MUTABLE, specbits); } } bad_specifiers (decl, "field", virtualp, quals != NULL_TREE, inlinep, friendp, raises != NULL_TREE); } } else if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE) { tree original_name; int publicp = 0; if (! declarator) return NULL_TREE; if (TREE_CODE (declarator) == TEMPLATE_ID_EXPR) original_name = dname; else original_name = declarator; if (RIDBIT_SETP (RID_AUTO, specbits)) error ("storage class `auto' invalid for function `%s'", name); else if (RIDBIT_SETP (RID_REGISTER, specbits)) error ("storage class `register' invalid for function `%s'", name); else if (RIDBIT_SETP (RID_THREAD, specbits)) error ("storage class `__thread' invalid for function `%s'", name); /* Function declaration not at top level. Storage classes other than `extern' are not allowed and `extern' makes no difference. */ if (! toplevel_bindings_p () && (RIDBIT_SETP (RID_STATIC, specbits) || RIDBIT_SETP (RID_INLINE, specbits)) && pedantic) { if (RIDBIT_SETP (RID_STATIC, specbits)) pedwarn ("storage class `static' invalid for function `%s' declared out of global scope", name); else pedwarn ("storage class `inline' invalid for function `%s' declared out of global scope", name); } if (ctype == NULL_TREE) { if (virtualp) { error ("virtual non-class function `%s'", name); virtualp = 0; } } else if (TREE_CODE (type) == FUNCTION_TYPE && staticp < 2 && !NEW_DELETE_OPNAME_P (original_name)) type = build_method_type_directly (ctype, TREE_TYPE (type), TYPE_ARG_TYPES (type)); /* Record presence of `static'. */ publicp = (ctype != NULL_TREE || RIDBIT_SETP (RID_EXTERN, specbits) || !RIDBIT_SETP (RID_STATIC, specbits)); decl = grokfndecl (ctype, type, original_name, parms, declarator, virtualp, flags, quals, raises, 1, friendp, publicp, inlinep, funcdef_flag, template_count, in_namespace); if (decl == NULL_TREE) return NULL_TREE; if (staticp == 1) { int invalid_static = 0; /* Don't allow a static member function in a class, and forbid declaring main to be static. */ if (TREE_CODE (type) == METHOD_TYPE) { pedwarn ("cannot declare member function `%D' to have static linkage", decl); invalid_static = 1; } else if (current_function_decl) { /* FIXME need arm citation */ error ("cannot declare static function inside another function"); invalid_static = 1; } if (invalid_static) { staticp = 0; RIDBIT_RESET (RID_STATIC, specbits); } } } else { /* It's a variable. */ /* An uninitialized decl with `extern' is a reference. */ decl = grokvardecl (type, declarator, &specbits, initialized, (type_quals & TYPE_QUAL_CONST) != 0, ctype ? ctype : in_namespace); bad_specifiers (decl, "variable", virtualp, quals != NULL_TREE, inlinep, friendp, raises != NULL_TREE); if (ctype) { DECL_CONTEXT (decl) = ctype; if (staticp == 1) { pedwarn ("`static' may not be used when defining (as opposed to declaring) a static data member"); staticp = 0; RIDBIT_RESET (RID_STATIC, specbits); } if (RIDBIT_SETP (RID_REGISTER, specbits) && TREE_STATIC (decl)) { error ("static member `%D' declared `register'", decl); RIDBIT_RESET (RID_REGISTER, specbits); } if (RIDBIT_SETP (RID_EXTERN, specbits) && pedantic) { pedwarn ("cannot explicitly declare member `%#D' to have extern linkage", decl); RIDBIT_RESET (RID_EXTERN, specbits); } } } my_friendly_assert (!RIDBIT_SETP (RID_MUTABLE, specbits), 19990927); /* Record `register' declaration for warnings on & and in case doing stupid register allocation. */ if (RIDBIT_SETP (RID_REGISTER, specbits)) DECL_REGISTER (decl) = 1; if (RIDBIT_SETP (RID_EXTERN, specbits)) DECL_THIS_EXTERN (decl) = 1; if (RIDBIT_SETP (RID_STATIC, specbits)) DECL_THIS_STATIC (decl) = 1; /* Record constancy and volatility. There's no need to do this when processing a template; we'll do this for the instantiated declaration based on the type of DECL. */ if (!processing_template_decl) c_apply_type_quals_to_decl (type_quals, decl); return decl; } } /* Subroutine of start_function. Ensure that each of the parameter types (as listed in PARMS) is complete, as is required for a function definition. */ static void require_complete_types_for_parms (tree parms) { for (; parms; parms = TREE_CHAIN (parms)) { if (VOID_TYPE_P (TREE_TYPE (parms))) /* grokparms will have already issued an error. */ TREE_TYPE (parms) = error_mark_node; else if (complete_type_or_else (TREE_TYPE (parms), parms)) { layout_decl (parms, 0); DECL_ARG_TYPE (parms) = type_passed_as (TREE_TYPE (parms)); } } } /* Returns nonzero if T is a local variable. */ int local_variable_p (tree t) { if ((TREE_CODE (t) == VAR_DECL /* A VAR_DECL with a context that is a _TYPE is a static data member. */ && !TYPE_P (CP_DECL_CONTEXT (t)) /* Any other non-local variable must be at namespace scope. */ && !DECL_NAMESPACE_SCOPE_P (t)) || (TREE_CODE (t) == PARM_DECL)) return 1; return 0; } /* Returns nonzero if T is an automatic local variable or a label. (These are the declarations that need to be remapped when the code containing them is duplicated.) */ int nonstatic_local_decl_p (tree t) { return ((local_variable_p (t) && !TREE_STATIC (t)) || TREE_CODE (t) == LABEL_DECL || TREE_CODE (t) == RESULT_DECL); } /* Like local_variable_p, but suitable for use as a tree-walking function. */ static tree local_variable_p_walkfn (tree* tp, int* walk_subtrees ATTRIBUTE_UNUSED , void* data ATTRIBUTE_UNUSED ) { return ((local_variable_p (*tp) && !DECL_ARTIFICIAL (*tp)) ? *tp : NULL_TREE); } /* Check that ARG, which is a default-argument expression for a parameter DECL, is valid. Returns ARG, or ERROR_MARK_NODE, if something goes wrong. DECL may also be a _TYPE node, rather than a DECL, if there is no DECL available. */ tree check_default_argument (tree decl, tree arg) { tree var; tree decl_type; if (TREE_CODE (arg) == DEFAULT_ARG) /* We get a DEFAULT_ARG when looking at an in-class declaration with a default argument. Ignore the argument for now; we'll deal with it after the class is complete. */ return arg; if (processing_template_decl || uses_template_parms (arg)) /* We don't do anything checking until instantiation-time. Note that there may be uninstantiated arguments even for an instantiated function, since default arguments are not instantiated until they are needed. */ return arg; if (TYPE_P (decl)) { decl_type = decl; decl = NULL_TREE; } else decl_type = TREE_TYPE (decl); if (arg == error_mark_node || decl == error_mark_node || TREE_TYPE (arg) == error_mark_node || decl_type == error_mark_node) /* Something already went wrong. There's no need to check further. */ return error_mark_node; /* [dcl.fct.default] A default argument expression is implicitly converted to the parameter type. */ if (!TREE_TYPE (arg) || !can_convert_arg (decl_type, TREE_TYPE (arg), arg)) { if (decl) error ("default argument for `%#D' has type `%T'", decl, TREE_TYPE (arg)); else error ("default argument for parameter of type `%T' has type `%T'", decl_type, TREE_TYPE (arg)); return error_mark_node; } /* [dcl.fct.default] Local variables shall not be used in default argument expressions. The keyword `this' shall not be used in a default argument of a member function. */ var = walk_tree_without_duplicates (&arg, local_variable_p_walkfn, NULL); if (var) { error ("default argument `%E' uses local variable `%D'", arg, var); return error_mark_node; } /* All is well. */ return arg; } /* Decode the list of parameter types for a function type. Given the list of things declared inside the parens, return a list of types. We determine whether ellipsis parms are used by PARMLIST_ELLIPSIS_P flag. If unset, we append void_list_node. A parmlist declared as `(void)' is accepted as the empty parmlist. *PARMS is set to the chain of PARM_DECLs created. */ static tree grokparms (tree first_parm, tree *parms) { tree result = NULL_TREE; tree decls = NULL_TREE; int ellipsis = !first_parm || PARMLIST_ELLIPSIS_P (first_parm); tree parm, chain; int any_error = 0; my_friendly_assert (!first_parm || TREE_PARMLIST (first_parm), 20001115); for (parm = first_parm; parm != NULL_TREE; parm = chain) { tree type = NULL_TREE; tree decl = TREE_VALUE (parm); tree init = TREE_PURPOSE (parm); tree specs, attrs; chain = TREE_CHAIN (parm); /* @@ weak defense against parse errors. */ if (TREE_CODE (decl) != VOID_TYPE && TREE_CODE (decl) != TREE_LIST) { /* Give various messages as the need arises. */ if (TREE_CODE (decl) == STRING_CST) error ("invalid string constant `%E'", decl); else if (TREE_CODE (decl) == INTEGER_CST) error ("invalid integer constant in parameter list, did you forget to give parameter name?"); continue; } if (parm == void_list_node) break; split_specs_attrs (TREE_PURPOSE (decl), &specs, &attrs); decl = grokdeclarator (TREE_VALUE (decl), specs, PARM, init != NULL_TREE, &attrs); if (! decl || TREE_TYPE (decl) == error_mark_node) continue; if (attrs) cplus_decl_attributes (&decl, attrs, 0); type = TREE_TYPE (decl); if (VOID_TYPE_P (type)) { if (same_type_p (type, void_type_node) && !DECL_NAME (decl) && !result && !chain && !ellipsis) /* this is a parmlist of `(void)', which is ok. */ break; cxx_incomplete_type_error (decl, type); /* It's not a good idea to actually create parameters of type `void'; other parts of the compiler assume that a void type terminates the parameter list. */ type = error_mark_node; TREE_TYPE (decl) = error_mark_node; } if (type != error_mark_node) { /* Top-level qualifiers on the parameters are ignored for function types. */ type = cp_build_qualified_type (type, 0); if (TREE_CODE (type) == METHOD_TYPE) { error ("parameter `%D' invalidly declared method type", decl); type = build_pointer_type (type); TREE_TYPE (decl) = type; } else if (abstract_virtuals_error (decl, type)) any_error = 1; /* Seems like a good idea. */ else if (POINTER_TYPE_P (type)) { /* [dcl.fct]/6, parameter types cannot contain pointers (references) to arrays of unknown bound. */ tree t = TREE_TYPE (type); int ptr = TYPE_PTR_P (type); while (1) { if (TYPE_PTR_P (t)) ptr = 1; else if (TREE_CODE (t) != ARRAY_TYPE) break; else if (!TYPE_DOMAIN (t)) break; t = TREE_TYPE (t); } if (TREE_CODE (t) == ARRAY_TYPE) error ("parameter `%D' includes %s to array of unknown bound `%T'", decl, ptr ? "pointer" : "reference", t); } if (!any_error && init) init = check_default_argument (decl, init); else init = NULL_TREE; } TREE_CHAIN (decl) = decls; decls = decl; result = tree_cons (init, type, result); } decls = nreverse (decls); result = nreverse (result); if (!ellipsis) result = chainon (result, void_list_node); *parms = decls; return result; } /* D is a constructor or overloaded `operator='. Let T be the class in which D is declared. Then, this function returns: -1 if D's is an ill-formed constructor or copy assignment operator whose first parameter is of type `T'. 0 if D is not a copy constructor or copy assignment operator. 1 if D is a copy constructor or copy assignment operator whose first parameter is a reference to const qualified T. 2 if D is a copy constructor or copy assignment operator whose first parameter is a reference to non-const qualified T. This function can be used as a predicate. Positive values indicate a copy constructor and nonzero values indicate a copy assignment operator. */ int copy_fn_p (tree d) { tree args; tree arg_type; int result = 1; my_friendly_assert (DECL_FUNCTION_MEMBER_P (d), 20011208); if (DECL_TEMPLATE_INFO (d) && is_member_template (DECL_TI_TEMPLATE (d))) /* Instantiations of template member functions are never copy functions. Note that member functions of templated classes are represented as template functions internally, and we must accept those as copy functions. */ return 0; args = FUNCTION_FIRST_USER_PARMTYPE (d); if (!args) return 0; arg_type = TREE_VALUE (args); if (TYPE_MAIN_VARIANT (arg_type) == DECL_CONTEXT (d)) { /* Pass by value copy assignment operator. */ result = -1; } else if (TREE_CODE (arg_type) == REFERENCE_TYPE && TYPE_MAIN_VARIANT (TREE_TYPE (arg_type)) == DECL_CONTEXT (d)) { if (CP_TYPE_CONST_P (TREE_TYPE (arg_type))) result = 2; } else return 0; args = TREE_CHAIN (args); if (args && args != void_list_node && !TREE_PURPOSE (args)) /* There are more non-optional args. */ return 0; return result; } /* Remember any special properties of member function DECL. */ void grok_special_member_properties (tree decl) { if (!DECL_NONSTATIC_MEMBER_FUNCTION_P(decl)) ; /* Not special. */ else if (DECL_CONSTRUCTOR_P (decl)) { int ctor = copy_fn_p (decl); if (ctor > 0) { /* [class.copy] A non-template constructor for class X is a copy constructor if its first parameter is of type X&, const X&, volatile X& or const volatile X&, and either there are no other parameters or else all other parameters have default arguments. */ TYPE_HAS_INIT_REF (DECL_CONTEXT (decl)) = 1; if (ctor > 1) TYPE_HAS_CONST_INIT_REF (DECL_CONTEXT (decl)) = 1; } else if (sufficient_parms_p (FUNCTION_FIRST_USER_PARMTYPE (decl))) TYPE_HAS_DEFAULT_CONSTRUCTOR (DECL_CONTEXT (decl)) = 1; } else if (DECL_OVERLOADED_OPERATOR_P (decl) == NOP_EXPR) { /* [class.copy] A non-template assignment operator for class X is a copy assignment operator if its parameter is of type X, X&, const X&, volatile X& or const volatile X&. */ int assop = copy_fn_p (decl); if (assop) { TYPE_HAS_ASSIGN_REF (DECL_CONTEXT (decl)) = 1; if (assop != 1) TYPE_HAS_CONST_ASSIGN_REF (DECL_CONTEXT (decl)) = 1; if (DECL_PURE_VIRTUAL_P (decl)) TYPE_HAS_ABSTRACT_ASSIGN_REF (DECL_CONTEXT (decl)) = 1; } } } /* Check a constructor DECL has the correct form. Complains if the class has a constructor of the form X(X). */ int grok_ctor_properties (tree ctype, tree decl) { int ctor_parm = copy_fn_p (decl); if (ctor_parm < 0) { /* [class.copy] A declaration of a constructor for a class X is ill-formed if its first parameter is of type (optionally cv-qualified) X and either there are no other parameters or else all other parameters have default arguments. We *don't* complain about member template instantiations that have this form, though; they can occur as we try to decide what constructor to use during overload resolution. Since overload resolution will never prefer such a constructor to the non-template copy constructor (which is either explicitly or implicitly defined), there's no need to worry about their existence. Theoretically, they should never even be instantiated, but that's hard to forestall. */ error ("invalid constructor; you probably meant `%T (const %T&)'", ctype, ctype); SET_IDENTIFIER_ERROR_LOCUS (DECL_NAME (decl), ctype); return 0; } return 1; } /* An operator with this code is unary, but can also be binary. */ static int ambi_op_p (enum tree_code code) { return (code == INDIRECT_REF || code == ADDR_EXPR || code == CONVERT_EXPR || code == NEGATE_EXPR || code == PREINCREMENT_EXPR || code == PREDECREMENT_EXPR); } /* An operator with this name can only be unary. */ static int unary_op_p (enum tree_code code) { return (code == TRUTH_NOT_EXPR || code == BIT_NOT_EXPR || code == COMPONENT_REF || code == TYPE_EXPR); } /* DECL is a declaration for an overloaded operator. Returns true if the declaration is valid; false otherwise. If COMPLAIN is true, errors are issued for invalid declarations. */ bool -grok_op_properties (tree decl, int friendp, bool complain) +grok_op_properties (tree decl, bool complain) { tree argtypes = TYPE_ARG_TYPES (TREE_TYPE (decl)); tree argtype; int methodp = (TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE); tree name = DECL_NAME (decl); enum tree_code operator_code; int arity; + bool ellipsis_p; bool ok; + tree class_type; /* Assume that the declaration is valid. */ ok = true; - /* Count the number of arguments. */ + /* Count the number of arguments. and check for ellipsis */ for (argtype = argtypes, arity = 0; argtype && argtype != void_list_node; argtype = TREE_CHAIN (argtype)) ++arity; + ellipsis_p = !argtype; - if (current_class_type == NULL_TREE) - friendp = 1; - + class_type = DECL_CONTEXT (decl); + if (class_type && !CLASS_TYPE_P (class_type)) + class_type = NULL_TREE; + if (DECL_CONV_FN_P (decl)) operator_code = TYPE_EXPR; else do { #define DEF_OPERATOR(NAME, CODE, MANGLING, ARITY, ASSN_P) \ if (ansi_opname (CODE) == name) \ { \ operator_code = (CODE); \ break; \ } \ else if (ansi_assopname (CODE) == name) \ { \ operator_code = (CODE); \ DECL_ASSIGNMENT_OPERATOR_P (decl) = 1; \ break; \ } #include "operators.def" #undef DEF_OPERATOR abort (); } while (0); my_friendly_assert (operator_code != LAST_CPLUS_TREE_CODE, 20000526); SET_OVERLOADED_OPERATOR_CODE (decl, operator_code); - if (! friendp) - { - switch (operator_code) - { - case NEW_EXPR: - TYPE_HAS_NEW_OPERATOR (current_class_type) = 1; - break; + if (class_type) + switch (operator_code) + { + case NEW_EXPR: + TYPE_HAS_NEW_OPERATOR (class_type) = 1; + break; - case DELETE_EXPR: - TYPE_GETS_DELETE (current_class_type) |= 1; - break; + case DELETE_EXPR: + TYPE_GETS_DELETE (class_type) |= 1; + break; - case VEC_NEW_EXPR: - TYPE_HAS_ARRAY_NEW_OPERATOR (current_class_type) = 1; - break; + case VEC_NEW_EXPR: + TYPE_HAS_ARRAY_NEW_OPERATOR (class_type) = 1; + break; - case VEC_DELETE_EXPR: - TYPE_GETS_DELETE (current_class_type) |= 2; - break; + case VEC_DELETE_EXPR: + TYPE_GETS_DELETE (class_type) |= 2; + break; - default: - break; - } - } + default: + break; + } if (operator_code == NEW_EXPR || operator_code == VEC_NEW_EXPR) TREE_TYPE (decl) = coerce_new_type (TREE_TYPE (decl)); else if (operator_code == DELETE_EXPR || operator_code == VEC_DELETE_EXPR) TREE_TYPE (decl) = coerce_delete_type (TREE_TYPE (decl)); else { /* An operator function must either be a non-static member function or have at least one parameter of a class, a reference to a class, an enumeration, or a reference to an enumeration. 13.4.0.6 */ if (! methodp || DECL_STATIC_FUNCTION_P (decl)) { if (operator_code == TYPE_EXPR || operator_code == CALL_EXPR || operator_code == COMPONENT_REF || operator_code == ARRAY_REF || operator_code == NOP_EXPR) error ("`%D' must be a nonstatic member function", decl); else { tree p; if (DECL_STATIC_FUNCTION_P (decl)) error ("`%D' must be either a non-static member function or a non-member function", decl); for (p = argtypes; p && p != void_list_node; p = TREE_CHAIN (p)) { tree arg = non_reference (TREE_VALUE (p)); /* IS_AGGR_TYPE, rather than CLASS_TYPE_P, is used because these checks are performed even on template functions. */ if (IS_AGGR_TYPE (arg) || TREE_CODE (arg) == ENUMERAL_TYPE) break; } if (!p || p == void_list_node) { if (!complain) return false; error ("`%D' must have an argument of class or " "enumerated type", decl); ok = false; } } } /* There are no restrictions on the arguments to an overloaded "operator ()". */ if (operator_code == CALL_EXPR) return ok; - if (IDENTIFIER_TYPENAME_P (name) && ! DECL_TEMPLATE_INFO (decl)) + /* Warn about conversion operators that will never be used. */ + if (IDENTIFIER_TYPENAME_P (name) + && ! DECL_TEMPLATE_INFO (decl) + && warn_conversion + /* Warn only declaring the function; there is no need to + warn again about out-of-class definitions. */ + && class_type == current_class_type) { tree t = TREE_TYPE (name); - if (! friendp) + int ref = (TREE_CODE (t) == REFERENCE_TYPE); + const char *what = 0; + + if (ref) + t = TYPE_MAIN_VARIANT (TREE_TYPE (t)); + + if (TREE_CODE (t) == VOID_TYPE) + what = "void"; + else if (class_type) { - int ref = (TREE_CODE (t) == REFERENCE_TYPE); - const char *what = 0; - - if (ref) - t = TYPE_MAIN_VARIANT (TREE_TYPE (t)); - - if (TREE_CODE (t) == VOID_TYPE) - what = "void"; - else if (t == current_class_type) + if (t == class_type) what = "the same type"; /* Don't force t to be complete here. */ else if (IS_AGGR_TYPE (t) && COMPLETE_TYPE_P (t) - && DERIVED_FROM_P (t, current_class_type)) + && DERIVED_FROM_P (t, class_type)) what = "a base class"; - - if (what && warn_conversion) - warning ("conversion to %s%s will never use a type conversion operator", - ref ? "a reference to " : "", what); } + + if (what) + warning ("conversion to %s%s will never use a type conversion operator", + ref ? "a reference to " : "", what); } + if (operator_code == COND_EXPR) { /* 13.4.0.3 */ error ("ISO C++ prohibits overloading operator ?:"); } + else if (ellipsis_p) + error ("`%D' must not have variable number of arguments", decl); else if (ambi_op_p (operator_code)) { if (arity == 1) /* We pick the one-argument operator codes by default, so we don't have to change anything. */ ; else if (arity == 2) { /* If we thought this was a unary operator, we now know it to be a binary operator. */ switch (operator_code) { case INDIRECT_REF: operator_code = MULT_EXPR; break; case ADDR_EXPR: operator_code = BIT_AND_EXPR; break; case CONVERT_EXPR: operator_code = PLUS_EXPR; break; case NEGATE_EXPR: operator_code = MINUS_EXPR; break; case PREINCREMENT_EXPR: operator_code = POSTINCREMENT_EXPR; break; case PREDECREMENT_EXPR: operator_code = POSTDECREMENT_EXPR; break; default: abort (); } SET_OVERLOADED_OPERATOR_CODE (decl, operator_code); if ((operator_code == POSTINCREMENT_EXPR || operator_code == POSTDECREMENT_EXPR) && ! processing_template_decl && ! same_type_p (TREE_VALUE (TREE_CHAIN (argtypes)), integer_type_node)) { if (methodp) error ("postfix `%D' must take `int' as its argument", decl); else error ("postfix `%D' must take `int' as its second argument", decl); } } else { if (methodp) error ("`%D' must take either zero or one argument", decl); else error ("`%D' must take either one or two arguments", decl); } /* More Effective C++ rule 6. */ if (warn_ecpp && (operator_code == POSTINCREMENT_EXPR || operator_code == POSTDECREMENT_EXPR || operator_code == PREINCREMENT_EXPR || operator_code == PREDECREMENT_EXPR)) { tree arg = TREE_VALUE (argtypes); tree ret = TREE_TYPE (TREE_TYPE (decl)); if (methodp || TREE_CODE (arg) == REFERENCE_TYPE) arg = TREE_TYPE (arg); arg = TYPE_MAIN_VARIANT (arg); if (operator_code == PREINCREMENT_EXPR || operator_code == PREDECREMENT_EXPR) { if (TREE_CODE (ret) != REFERENCE_TYPE || !same_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (ret)), arg)) warning ("prefix `%D' should return `%T'", decl, build_reference_type (arg)); } else { if (!same_type_p (TYPE_MAIN_VARIANT (ret), arg)) warning ("postfix `%D' should return `%T'", decl, arg); } } } else if (unary_op_p (operator_code)) { if (arity != 1) { if (methodp) error ("`%D' must take `void'", decl); else error ("`%D' must take exactly one argument", decl); } } else /* if (binary_op_p (operator_code)) */ { if (arity != 2) { if (methodp) error ("`%D' must take exactly one argument", decl); else error ("`%D' must take exactly two arguments", decl); } /* More Effective C++ rule 7. */ if (warn_ecpp && (operator_code == TRUTH_ANDIF_EXPR || operator_code == TRUTH_ORIF_EXPR || operator_code == COMPOUND_EXPR)) warning ("user-defined `%D' always evaluates both arguments", decl); } /* Effective C++ rule 23. */ if (warn_ecpp && arity == 2 && !DECL_ASSIGNMENT_OPERATOR_P (decl) && (operator_code == PLUS_EXPR || operator_code == MINUS_EXPR || operator_code == TRUNC_DIV_EXPR || operator_code == MULT_EXPR || operator_code == TRUNC_MOD_EXPR) && TREE_CODE (TREE_TYPE (TREE_TYPE (decl))) == REFERENCE_TYPE) warning ("`%D' should return by value", decl); /* [over.oper]/8 */ for (; argtypes && argtypes != void_list_node; argtypes = TREE_CHAIN (argtypes)) if (TREE_PURPOSE (argtypes)) { TREE_PURPOSE (argtypes) = NULL_TREE; if (operator_code == POSTINCREMENT_EXPR || operator_code == POSTDECREMENT_EXPR) { if (pedantic) pedwarn ("`%D' cannot have default arguments", decl); } else error ("`%D' cannot have default arguments", decl); } } return ok; } static const char * tag_name (enum tag_types code) { switch (code) { case record_type: return "struct"; case class_type: return "class"; case union_type: - return "union "; + return "union"; case enum_type: return "enum"; + case typename_type: + return "typename"; default: abort (); } } /* Name lookup in an elaborated-type-specifier (after the keyword indicated by TAG_CODE) has found the TYPE_DECL DECL. If the elaborated-type-specifier is invalid, issue a diagnostic and return error_mark_node; otherwise, return the *_TYPE to which it referred. If ALLOW_TEMPLATE_P is true, TYPE may be a class template. */ tree check_elaborated_type_specifier (enum tag_types tag_code, tree decl, bool allow_template_p) { tree type; /* In the case of: struct S { struct S *p; }; name lookup will find the TYPE_DECL for the implicit "S::S" typedef. Adjust for that here. */ if (DECL_SELF_REFERENCE_P (decl)) decl = TYPE_NAME (TREE_TYPE (decl)); type = TREE_TYPE (decl); /* [dcl.type.elab] If the identifier resolves to a typedef-name or a template type-parameter, the elaborated-type-specifier is ill-formed. In other words, the only legitimate declaration to use in the elaborated type specifier is the implicit typedef created when the type is declared. */ - if (!DECL_IMPLICIT_TYPEDEF_P (decl)) + if (!DECL_IMPLICIT_TYPEDEF_P (decl) + && tag_code != typename_type) { error ("using typedef-name `%D' after `%s'", decl, tag_name (tag_code)); return IS_AGGR_TYPE (type) ? type : error_mark_node; } if (TREE_CODE (type) == TEMPLATE_TYPE_PARM) { error ("using template type parameter `%T' after `%s'", type, tag_name (tag_code)); return error_mark_node; } else if (TREE_CODE (type) != RECORD_TYPE && TREE_CODE (type) != UNION_TYPE - && tag_code != enum_type) + && tag_code != enum_type + && tag_code != typename_type) { error ("`%T' referred to as `%s'", type, tag_name (tag_code)); return error_mark_node; } else if (TREE_CODE (type) != ENUMERAL_TYPE && tag_code == enum_type) { error ("`%T' referred to as enum", type); return error_mark_node; } else if (!allow_template_p && TREE_CODE (type) == RECORD_TYPE && CLASSTYPE_IS_TEMPLATE (type)) { /* If a class template appears as elaborated type specifier without a template header such as: template class C {}; void f(class C); // No template header here then the required template argument is missing. */ error ("template argument required for `%s %T'", tag_name (tag_code), DECL_NAME (CLASSTYPE_TI_TEMPLATE (type))); return error_mark_node; } return type; } /* Get the struct, enum or union (TAG_CODE says which) with tag NAME. Define the tag as a forward-reference if it is not defined. If a declaration is given, process it here, and report an error if multiple declarations are not identical. GLOBALIZE is false when this is also a definition. Only look in the current frame for the name (since C++ allows new names in any scope.) TEMPLATE_HEADER_P is true when this declaration is preceded by a set of template parameters. */ tree xref_tag (enum tag_types tag_code, tree name, bool globalize, bool template_header_p) { enum tree_code code; tree t; struct cp_binding_level *b = current_binding_level; tree context = NULL_TREE; timevar_push (TV_NAME_LOOKUP); my_friendly_assert (TREE_CODE (name) == IDENTIFIER_NODE, 0); switch (tag_code) { case record_type: case class_type: code = RECORD_TYPE; break; case union_type: code = UNION_TYPE; break; case enum_type: code = ENUMERAL_TYPE; break; default: abort (); } if (! globalize) { /* If we know we are defining this tag, only look it up in this scope and don't try to find it as a type. */ t = lookup_tag (code, name, b, 1); } else { tree decl = lookup_name (name, 2); if (decl && DECL_CLASS_TEMPLATE_P (decl)) decl = DECL_TEMPLATE_RESULT (decl); if (decl && TREE_CODE (decl) == TYPE_DECL) { /* Two cases we need to consider when deciding if a class template is allowed as an elaborated type specifier: 1. It is a self reference to its own class. 2. It comes with a template header. For example: template class C { class C *c1; // DECL_SELF_REFERENCE_P is true class D; }; template class C; // template_header_p is true template class C::D { class C *c2; // DECL_SELF_REFERENCE_P is true }; */ t = check_elaborated_type_specifier (tag_code, decl, template_header_p | DECL_SELF_REFERENCE_P (decl)); if (t == error_mark_node) POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node); } else t = NULL_TREE; if (t && current_class_type && template_class_depth (current_class_type) && template_header_p) { /* Since GLOBALIZE is nonzero, we are not looking at a definition of this tag. Since, in addition, we are currently processing a (member) template declaration of a template class, we must be very careful; consider: template struct S1 template struct S2 { template friend struct S1; }; Here, the S2::S1 declaration should not be confused with the outer declaration. In particular, the inner version should have a template parameter of level 2, not level 1. This would be particularly important if the member declaration were instead: template friend struct S1; say, when we should tsubst into `U' when instantiating S2. On the other hand, when presented with: template struct S1 { template struct S2 {}; template friend struct S2; }; we must find the inner binding eventually. We accomplish this by making sure that the new type we create to represent this declaration has the right TYPE_CONTEXT. */ context = TYPE_CONTEXT (t); t = NULL_TREE; } } if (! t) { /* If no such tag is yet defined, create a forward-reference node and record it as the "definition". When a real declaration of this type is found, the forward-reference will be altered into a real type. */ if (code == ENUMERAL_TYPE) { error ("use of enum `%#D' without previous declaration", name); POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node); } else { t = make_aggr_type (code); TYPE_CONTEXT (t) = context; pushtag (name, t, globalize); } } else { if (!globalize && processing_template_decl && IS_AGGR_TYPE (t)) redeclare_class_template (t, current_template_parms); else if (!processing_template_decl && CLASS_TYPE_P (t) && CLASSTYPE_IS_TEMPLATE (t)) { error ("redeclaration of `%T' as a non-template", t); t = error_mark_node; } } POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, t); } tree xref_tag_from_type (tree old, tree id, int globalize) { enum tag_types tag_kind; if (TREE_CODE (old) == RECORD_TYPE) tag_kind = (CLASSTYPE_DECLARED_CLASS (old) ? class_type : record_type); else tag_kind = union_type; if (id == NULL_TREE) id = TYPE_IDENTIFIER (old); return xref_tag (tag_kind, id, globalize, false); } /* REF is a type (named NAME), for which we have just seen some baseclasses. BASE_LIST is a list of those baseclasses; the TREE_PURPOSE is an access_* node, and the TREE_VALUE is the type of the base-class. TREE_VIA_VIRTUAL indicates virtual inheritance. CODE_TYPE_NODE indicates whether REF is a class, struct, or union. */ void xref_basetypes (tree ref, tree base_list) { /* In the declaration `A : X, Y, ... Z' we mark all the types (A, X, Y, ..., Z) so we can check for duplicates. */ tree *basep; int i; enum tag_types tag_code; if (ref == error_mark_node) return; if (TREE_CODE (ref) == UNION_TYPE) { error ("derived union `%T' invalid", ref); return; } tag_code = (CLASSTYPE_DECLARED_CLASS (ref) ? class_type : record_type); /* First, make sure that any templates in base-classes are instantiated. This ensures that if we call ourselves recursively we do not get confused about which classes are marked and which are not. */ basep = &base_list; while (*basep) { tree basetype = TREE_VALUE (*basep); if (!(processing_template_decl && uses_template_parms (basetype)) && !complete_type_or_else (basetype, NULL)) /* An incomplete type. Remove it from the list. */ *basep = TREE_CHAIN (*basep); else basep = &TREE_CHAIN (*basep); } SET_CLASSTYPE_MARKED (ref); i = list_length (base_list); if (i) { tree binfo = TYPE_BINFO (ref); tree binfos = make_tree_vec (i); tree accesses = make_tree_vec (i); BINFO_BASETYPES (binfo) = binfos; BINFO_BASEACCESSES (binfo) = accesses; for (i = 0; base_list; base_list = TREE_CHAIN (base_list)) { tree access = TREE_PURPOSE (base_list); int via_virtual = TREE_VIA_VIRTUAL (base_list); tree basetype = TREE_VALUE (base_list); tree base_binfo; if (access == access_default_node) /* The base of a derived struct is public by default. */ access = (tag_code == class_type ? access_private_node : access_public_node); if (basetype && TREE_CODE (basetype) == TYPE_DECL) basetype = TREE_TYPE (basetype); if (!basetype || (TREE_CODE (basetype) != RECORD_TYPE && TREE_CODE (basetype) != TYPENAME_TYPE && TREE_CODE (basetype) != TEMPLATE_TYPE_PARM && TREE_CODE (basetype) != BOUND_TEMPLATE_TEMPLATE_PARM)) { error ("base type `%T' fails to be a struct or class type", basetype); continue; } if (CLASSTYPE_MARKED (basetype)) { if (basetype == ref) error ("recursive type `%T' undefined", basetype); else error ("duplicate base type `%T' invalid", basetype); continue; } if (TYPE_FOR_JAVA (basetype) && (current_lang_depth () == 0)) TYPE_FOR_JAVA (ref) = 1; if (CLASS_TYPE_P (basetype)) { base_binfo = TYPE_BINFO (basetype); /* This flag will be in the binfo of the base type, we must clear it after copying the base binfos. */ BINFO_DEPENDENT_BASE_P (base_binfo) = dependent_type_p (basetype); } else base_binfo = make_binfo (size_zero_node, basetype, NULL_TREE, NULL_TREE); TREE_VEC_ELT (binfos, i) = base_binfo; TREE_VEC_ELT (accesses, i) = access; /* This flag will be in the binfo of the base type, we must clear it after copying the base binfos. */ TREE_VIA_VIRTUAL (base_binfo) = via_virtual; SET_CLASSTYPE_MARKED (basetype); /* We are free to modify these bits because they are meaningless at top level, and BASETYPE is a top-level type. */ if (via_virtual || TYPE_USES_VIRTUAL_BASECLASSES (basetype)) { TYPE_USES_VIRTUAL_BASECLASSES (ref) = 1; /* Converting to a virtual base class requires looking up the offset of the virtual base. */ TYPE_BASE_CONVS_MAY_REQUIRE_CODE_P (ref) = 1; } if (CLASS_TYPE_P (basetype)) { TYPE_HAS_NEW_OPERATOR (ref) |= TYPE_HAS_NEW_OPERATOR (basetype); TYPE_HAS_ARRAY_NEW_OPERATOR (ref) |= TYPE_HAS_ARRAY_NEW_OPERATOR (basetype); TYPE_GETS_DELETE (ref) |= TYPE_GETS_DELETE (basetype); /* If the base-class uses multiple inheritance, so do we. */ TYPE_USES_MULTIPLE_INHERITANCE (ref) |= TYPE_USES_MULTIPLE_INHERITANCE (basetype); /* Likewise, if converting to a base of the base may require code, then we may need to generate code to convert to a base as well. */ TYPE_BASE_CONVS_MAY_REQUIRE_CODE_P (ref) |= TYPE_BASE_CONVS_MAY_REQUIRE_CODE_P (basetype); } i++; } if (i) TREE_VEC_LENGTH (accesses) = TREE_VEC_LENGTH (binfos) = i; else BINFO_BASEACCESSES (binfo) = BINFO_BASETYPES (binfo) = NULL_TREE; if (i > 1) { TYPE_USES_MULTIPLE_INHERITANCE (ref) = 1; /* If there is more than one non-empty they cannot be at the same address. */ TYPE_BASE_CONVS_MAY_REQUIRE_CODE_P (ref) = 1; } } /* Copy the base binfos, collect the virtual bases and set the inheritance order chain. */ copy_base_binfos (TYPE_BINFO (ref), ref, NULL_TREE); CLASSTYPE_VBASECLASSES (ref) = nreverse (CLASSTYPE_VBASECLASSES (ref)); if (TYPE_FOR_JAVA (ref)) { if (TYPE_USES_MULTIPLE_INHERITANCE (ref)) error ("Java class '%T' cannot have multiple bases", ref); if (CLASSTYPE_VBASECLASSES (ref)) error ("Java class '%T' cannot have virtual bases", ref); } /* Unmark all the types. */ while (i--) { tree basetype = BINFO_TYPE (BINFO_BASETYPE (TYPE_BINFO (ref), i)); CLEAR_CLASSTYPE_MARKED (basetype); if (CLASS_TYPE_P (basetype)) { TREE_VIA_VIRTUAL (TYPE_BINFO (basetype)) = 0; BINFO_DEPENDENT_BASE_P (TYPE_BINFO (basetype)) = 0; } } CLEAR_CLASSTYPE_MARKED (ref); } /* Begin compiling the definition of an enumeration type. NAME is its name (or null if anonymous). Returns the type object, as yet incomplete. Also records info about it so that build_enumerator may be used to declare the individual values as they are read. */ tree start_enum (tree name) { tree enumtype = NULL_TREE; struct cp_binding_level *b = current_binding_level; /* If this is the real definition for a previous forward reference, fill in the contents in the same object that used to be the forward reference. */ if (name != NULL_TREE) enumtype = lookup_tag (ENUMERAL_TYPE, name, b, 1); if (enumtype != NULL_TREE && TREE_CODE (enumtype) == ENUMERAL_TYPE) { error ("multiple definition of `%#T'", enumtype); error ("%Jprevious definition here", TYPE_MAIN_DECL (enumtype)); /* Clear out TYPE_VALUES, and start again. */ TYPE_VALUES (enumtype) = NULL_TREE; } else { enumtype = make_node (ENUMERAL_TYPE); pushtag (name, enumtype, 0); } return enumtype; } /* After processing and defining all the values of an enumeration type, install their decls in the enumeration type and finish it off. ENUMTYPE is the type object and VALUES a list of name-value pairs. */ void finish_enum (tree enumtype) { tree values; tree decl; tree value; tree minnode; tree maxnode; tree t; bool unsignedp; int lowprec; int highprec; int precision; integer_type_kind itk; tree underlying_type = NULL_TREE; /* We built up the VALUES in reverse order. */ TYPE_VALUES (enumtype) = nreverse (TYPE_VALUES (enumtype)); /* For an enum defined in a template, just set the type of the values; all further processing is postponed until the template is instantiated. We need to set the type so that tsubst of a CONST_DECL works. */ if (processing_template_decl) { for (values = TYPE_VALUES (enumtype); values; values = TREE_CHAIN (values)) TREE_TYPE (TREE_VALUE (values)) = enumtype; if (at_function_scope_p ()) add_stmt (build_min (TAG_DEFN, enumtype)); return; } /* Determine the minimum and maximum values of the enumerators. */ if (TYPE_VALUES (enumtype)) { minnode = maxnode = NULL_TREE; for (values = TYPE_VALUES (enumtype); values; values = TREE_CHAIN (values)) { decl = TREE_VALUE (values); /* [dcl.enum]: Following the closing brace of an enum-specifier, each enumerator has the type of its enumeration. Prior to the closing brace, the type of each enumerator is the type of its initializing value. */ TREE_TYPE (decl) = enumtype; /* Update the minimum and maximum values, if appropriate. */ value = DECL_INITIAL (decl); /* Figure out what the minimum and maximum values of the enumerators are. */ if (!minnode) minnode = maxnode = value; else if (tree_int_cst_lt (maxnode, value)) maxnode = value; else if (tree_int_cst_lt (value, minnode)) minnode = value; /* Set the TREE_TYPE for the values as well. That's so that when we call decl_constant_value we get an entity of the right type (but with the constant value). But first make a copy so we don't clobber shared INTEGER_CSTs. */ if (TREE_TYPE (value) != enumtype) { value = DECL_INITIAL (decl) = copy_node (value); TREE_TYPE (value) = enumtype; } } } else /* [dcl.enum] If the enumerator-list is empty, the underlying type is as if the enumeration had a single enumerator with value 0. */ minnode = maxnode = integer_zero_node; /* Compute the number of bits require to represent all values of the enumeration. We must do this before the type of MINNODE and MAXNODE are transformed, since min_precision relies on the TREE_TYPE of the value it is passed. */ unsignedp = tree_int_cst_sgn (minnode) >= 0; lowprec = min_precision (minnode, unsignedp); highprec = min_precision (maxnode, unsignedp); precision = MAX (lowprec, highprec); /* Determine the underlying type of the enumeration. [dcl.enum] The underlying type of an enumeration is an integral type that can represent all the enumerator values defined in the enumeration. It is implementation-defined which integral type is used as the underlying type for an enumeration except that the underlying type shall not be larger than int unless the value of an enumerator cannot fit in an int or unsigned int. We use "int" or an "unsigned int" as the underlying type, even if a smaller integral type would work, unless the user has explicitly requested that we use the smallest possible type. */ for (itk = (flag_short_enums ? itk_char : itk_int); itk != itk_none; itk++) { underlying_type = integer_types[itk]; if (TYPE_PRECISION (underlying_type) >= precision && TREE_UNSIGNED (underlying_type) == unsignedp) break; } if (itk == itk_none) { /* DR 377 IF no integral type can represent all the enumerator values, the enumeration is ill-formed. */ error ("no integral type can represent all of the enumerator values " "for `%T'", enumtype); precision = TYPE_PRECISION (long_long_integer_type_node); underlying_type = integer_types[itk_unsigned_long_long]; } /* Compute the minium and maximum values for the type. [dcl.enum] For an enumeration where emin is the smallest enumerator and emax is the largest, the values of the enumeration are the values of the underlying type in the range bmin to bmax, where bmin and bmax are, respectively, the smallest and largest values of the smallest bit- field that can store emin and emax. */ /* The middle-end currently assumes that types with TYPE_PRECISION narrower than their underlying type are suitably zero or sign extended to fill their mode. g++ doesn't make these guarantees. Until the middle-end can represent such paradoxical types, we set the TYPE_PRECISON to the width of the underlying type. */ TYPE_PRECISION (enumtype) = TYPE_PRECISION (underlying_type); set_min_and_max_values_for_integral_type (enumtype, precision, unsignedp); /* [dcl.enum] The value of sizeof() applied to an enumeration type, an object of an enumeration type, or an enumerator, is the value of sizeof() applied to the underlying type. */ TYPE_SIZE (enumtype) = TYPE_SIZE (underlying_type); TYPE_SIZE_UNIT (enumtype) = TYPE_SIZE_UNIT (underlying_type); TYPE_MODE (enumtype) = TYPE_MODE (underlying_type); TYPE_ALIGN (enumtype) = TYPE_ALIGN (underlying_type); TYPE_USER_ALIGN (enumtype) = TYPE_USER_ALIGN (underlying_type); TREE_UNSIGNED (enumtype) = TREE_UNSIGNED (underlying_type); /* Convert each of the enumerators to the type of the underlying type of the enumeration. */ for (values = TYPE_VALUES (enumtype); values; values = TREE_CHAIN (values)) { decl = TREE_VALUE (values); value = perform_implicit_conversion (underlying_type, DECL_INITIAL (decl)); TREE_TYPE (value) = enumtype; DECL_INITIAL (decl) = value; TREE_VALUE (values) = value; } /* Fix up all variant types of this enum type. */ for (t = TYPE_MAIN_VARIANT (enumtype); t; t = TYPE_NEXT_VARIANT (t)) { TYPE_VALUES (t) = TYPE_VALUES (enumtype); TYPE_MIN_VALUE (t) = TYPE_MIN_VALUE (enumtype); TYPE_MAX_VALUE (t) = TYPE_MAX_VALUE (enumtype); TYPE_SIZE (t) = TYPE_SIZE (enumtype); TYPE_SIZE_UNIT (t) = TYPE_SIZE_UNIT (enumtype); TYPE_MODE (t) = TYPE_MODE (enumtype); TYPE_PRECISION (t) = TYPE_PRECISION (enumtype); TYPE_ALIGN (t) = TYPE_ALIGN (enumtype); TYPE_USER_ALIGN (t) = TYPE_USER_ALIGN (enumtype); TREE_UNSIGNED (t) = TREE_UNSIGNED (enumtype); } /* Finish debugging output for this type. */ rest_of_type_compilation (enumtype, namespace_bindings_p ()); } /* Build and install a CONST_DECL for an enumeration constant of the enumeration type ENUMTYPE whose NAME and VALUE (if any) are provided. Assignment of sequential values by default is handled here. */ void build_enumerator (tree name, tree value, tree enumtype) { tree decl; tree context; tree type; /* Remove no-op casts from the value. */ if (value) STRIP_TYPE_NOPS (value); if (! processing_template_decl) { /* Validate and default VALUE. */ if (value != NULL_TREE) { value = decl_constant_value (value); if (TREE_CODE (value) == INTEGER_CST) { value = perform_integral_promotions (value); constant_expression_warning (value); } else { error ("enumerator value for `%D' not integer constant", name); value = NULL_TREE; } } /* Default based on previous value. */ if (value == NULL_TREE) { tree prev_value; if (TYPE_VALUES (enumtype)) { /* The next value is the previous value ... */ prev_value = DECL_INITIAL (TREE_VALUE (TYPE_VALUES (enumtype))); /* ... plus one. */ value = cp_build_binary_op (PLUS_EXPR, prev_value, integer_one_node); if (tree_int_cst_lt (value, prev_value)) error ("overflow in enumeration values at `%D'", name); } else value = integer_zero_node; } /* Remove no-op casts from the value. */ STRIP_TYPE_NOPS (value); } /* C++ associates enums with global, function, or class declarations. */ context = current_scope (); if (!context) context = current_namespace; /* Build the actual enumeration constant. Note that the enumeration constants have the type of their initializers until the enumeration is complete: [ dcl.enum ] Following the closing brace of an enum-specifier, each enumer- ator has the type of its enumeration. Prior to the closing brace, the type of each enumerator is the type of its initializing value. In finish_enum we will reset the type. Of course, if we're processing a template, there may be no value. */ type = value ? TREE_TYPE (value) : NULL_TREE; if (context && context == current_class_type) /* This enum declaration is local to the class. We need the full lang_decl so that we can record DECL_CLASS_CONTEXT, for example. */ decl = build_lang_decl (CONST_DECL, name, type); else /* It's a global enum, or it's local to a function. (Note local to a function could mean local to a class method. */ decl = build_decl (CONST_DECL, name, type); DECL_CONTEXT (decl) = FROB_CONTEXT (context); TREE_CONSTANT (decl) = TREE_READONLY (decl) = 1; DECL_INITIAL (decl) = value; if (context && context == current_class_type) /* In something like `struct S { enum E { i = 7 }; };' we put `i' on the TYPE_FIELDS list for `S'. (That's so that you can say things like `S::i' later.) */ finish_member_declaration (decl); else pushdecl (decl); /* Add this enumeration constant to the list for this type. */ TYPE_VALUES (enumtype) = tree_cons (name, decl, TYPE_VALUES (enumtype)); } /* We're defining DECL. Make sure that it's type is OK. */ static void check_function_type (tree decl, tree current_function_parms) { tree fntype = TREE_TYPE (decl); tree return_type = complete_type (TREE_TYPE (fntype)); /* In a function definition, arg types must be complete. */ require_complete_types_for_parms (current_function_parms); if (!COMPLETE_OR_VOID_TYPE_P (return_type)) { error ("return type `%#T' is incomplete", TREE_TYPE (fntype)); /* Make it return void instead, but don't change the type of the DECL_RESULT, in case we have a named return value. */ if (TREE_CODE (fntype) == METHOD_TYPE) { tree ctype = TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (fntype))); TREE_TYPE (decl) = build_method_type_directly (ctype, void_type_node, FUNCTION_ARG_CHAIN (decl)); } else TREE_TYPE (decl) = build_function_type (void_type_node, TYPE_ARG_TYPES (TREE_TYPE (decl))); TREE_TYPE (decl) = build_exception_variant (fntype, TYPE_RAISES_EXCEPTIONS (fntype)); } else abstract_virtuals_error (decl, TREE_TYPE (fntype)); } /* Create the FUNCTION_DECL for a function definition. DECLSPECS and DECLARATOR are the parts of the declaration; they describe the function's name and the type it returns, but twisted together in a fashion that parallels the syntax of C. FLAGS is a bitwise or of SF_PRE_PARSED (indicating that the DECLARATOR is really the DECL for the function we are about to process and that DECLSPECS should be ignored), SF_INCLASS_INLINE indicating that the function is an inline defined in-class. This function creates a binding context for the function body as well as setting up the FUNCTION_DECL in current_function_decl. Returns 1 on success. If the DECLARATOR is not suitable for a function (it defines a datum instead), we return 0, which tells yyparse to report a parse error. For C++, we must first check whether that datum makes any sense. For example, "class A local_a(1,2);" means that variable local_a is an aggregate of type A, which should have a constructor applied to it with the argument list [1, 2]. */ int start_function (tree declspecs, tree declarator, tree attrs, int flags) { tree decl1; tree ctype = NULL_TREE; tree fntype; tree restype; int doing_friend = 0; struct cp_binding_level *bl; tree current_function_parms; /* Sanity check. */ my_friendly_assert (TREE_CODE (TREE_VALUE (void_list_node)) == VOID_TYPE, 160); my_friendly_assert (TREE_CHAIN (void_list_node) == NULL_TREE, 161); /* This should only be done once on the top most decl. */ if (have_extern_spec) { declspecs = tree_cons (NULL_TREE, get_identifier ("extern"), declspecs); have_extern_spec = false; } if (flags & SF_PRE_PARSED) { decl1 = declarator; fntype = TREE_TYPE (decl1); if (TREE_CODE (fntype) == METHOD_TYPE) ctype = TYPE_METHOD_BASETYPE (fntype); /* ISO C++ 11.4/5. A friend function defined in a class is in the (lexical) scope of the class in which it is defined. */ if (!ctype && DECL_FRIEND_P (decl1)) { ctype = DECL_FRIEND_CONTEXT (decl1); /* CTYPE could be null here if we're dealing with a template; for example, `inline friend float foo()' inside a template will have no CTYPE set. */ if (ctype && TREE_CODE (ctype) != RECORD_TYPE) ctype = NULL_TREE; else doing_friend = 1; } } else { decl1 = grokdeclarator (declarator, declspecs, FUNCDEF, 1, &attrs); /* If the declarator is not suitable for a function definition, cause a syntax error. */ if (decl1 == NULL_TREE || TREE_CODE (decl1) != FUNCTION_DECL) return 0; cplus_decl_attributes (&decl1, attrs, 0); /* If #pragma weak was used, mark the decl weak now. */ if (global_scope_p (current_binding_level)) maybe_apply_pragma_weak (decl1); fntype = TREE_TYPE (decl1); restype = TREE_TYPE (fntype); if (TREE_CODE (fntype) == METHOD_TYPE) ctype = TYPE_METHOD_BASETYPE (fntype); else if (DECL_MAIN_P (decl1)) { /* If this doesn't return integer_type, or a typedef to integer_type, complain. */ if (!same_type_p (TREE_TYPE (TREE_TYPE (decl1)), integer_type_node)) { if (pedantic || warn_return_type) pedwarn ("return type for `main' changed to `int'"); TREE_TYPE (decl1) = fntype = default_function_type; } } } if (DECL_DECLARED_INLINE_P (decl1) && lookup_attribute ("noinline", attrs)) warning ("%Jinline function '%D' given attribute noinline", decl1, decl1); if (DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (decl1)) /* This is a constructor, we must ensure that any default args introduced by this definition are propagated to the clones now. The clones are used directly in overload resolution. */ adjust_clone_args (decl1); /* Sometimes we don't notice that a function is a static member, and build a METHOD_TYPE for it. Fix that up now. */ if (ctype != NULL_TREE && DECL_STATIC_FUNCTION_P (decl1) && TREE_CODE (TREE_TYPE (decl1)) == METHOD_TYPE) { revert_static_member_fn (decl1); ctype = NULL_TREE; } /* Warn if function was previously implicitly declared (but not if we warned then). */ if (! warn_implicit && IDENTIFIER_IMPLICIT_DECL (DECL_NAME (decl1)) != NULL_TREE) cp_warning_at ("`%D' implicitly declared before its definition", IDENTIFIER_IMPLICIT_DECL (DECL_NAME (decl1))); /* Set up current_class_type, and enter the scope of the class, if appropriate. */ if (ctype) push_nested_class (ctype); else if (DECL_STATIC_FUNCTION_P (decl1)) push_nested_class (DECL_CONTEXT (decl1)); /* Now that we have entered the scope of the class, we must restore the bindings for any template parameters surrounding DECL1, if it is an inline member template. (Order is important; consider the case where a template parameter has the same name as a field of the class.) It is not until after this point that PROCESSING_TEMPLATE_DECL is guaranteed to be set up correctly. */ if (flags & SF_INCLASS_INLINE) maybe_begin_member_template_processing (decl1); /* Effective C++ rule 15. */ if (warn_ecpp && DECL_OVERLOADED_OPERATOR_P (decl1) == NOP_EXPR && TREE_CODE (TREE_TYPE (fntype)) == VOID_TYPE) warning ("`operator=' should return a reference to `*this'"); /* Make the init_value nonzero so pushdecl knows this is not tentative. error_mark_node is replaced below (in poplevel) with the BLOCK. */ if (!DECL_INITIAL (decl1)) DECL_INITIAL (decl1) = error_mark_node; /* This function exists in static storage. (This does not mean `static' in the C sense!) */ TREE_STATIC (decl1) = 1; /* We must call push_template_decl after current_class_type is set up. (If we are processing inline definitions after exiting a class scope, current_class_type will be NULL_TREE until set above by push_nested_class.) */ if (processing_template_decl) - decl1 = push_template_decl (decl1); + { + tree newdecl1 = push_template_decl (decl1); + if (newdecl1 != error_mark_node) + decl1 = newdecl1; + } /* We are now in the scope of the function being defined. */ current_function_decl = decl1; /* Save the parm names or decls from this function's declarator where store_parm_decls will find them. */ current_function_parms = DECL_ARGUMENTS (decl1); /* Make sure the parameter and return types are reasonable. When you declare a function, these types can be incomplete, but they must be complete when you define the function. */ if (! processing_template_decl) check_function_type (decl1, current_function_parms); + /* Make sure no default arg is missing. */ + check_default_args (decl1); /* Build the return declaration for the function. */ restype = TREE_TYPE (fntype); /* Promote the value to int before returning it. */ if (c_promoting_integer_type_p (restype)) restype = type_promotes_to (restype); if (DECL_RESULT (decl1) == NULL_TREE) { DECL_RESULT (decl1) = build_decl (RESULT_DECL, 0, TYPE_MAIN_VARIANT (restype)); c_apply_type_quals_to_decl (cp_type_quals (restype), DECL_RESULT (decl1)); } /* Initialize RTL machinery. We cannot do this until CURRENT_FUNCTION_DECL and DECL_RESULT are set up. We do this even when processing a template; this is how we get CFUN set up, and our per-function variables initialized. FIXME factor out the non-RTL stuff. */ bl = current_binding_level; allocate_struct_function (decl1); current_binding_level = bl; /* Even though we're inside a function body, we still don't want to call expand_expr to calculate the size of a variable-sized array. We haven't necessarily assigned RTL to all variables yet, so it's not safe to try to expand expressions involving them. */ immediate_size_expand = 0; cfun->x_dont_save_pending_sizes_p = 1; /* Start the statement-tree, start the tree now. */ begin_stmt_tree (&DECL_SAVED_TREE (decl1)); /* Let the user know we're compiling this function. */ announce_function (decl1); /* Record the decl so that the function name is defined. If we already have a decl for this name, and it is a FUNCTION_DECL, use the old decl. */ if (!processing_template_decl && !(flags & SF_PRE_PARSED)) { /* A specialization is not used to guide overload resolution. */ if (!DECL_FUNCTION_MEMBER_P (decl1) && !(DECL_USE_TEMPLATE (decl1) && PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (decl1)))) { tree olddecl = pushdecl (decl1); if (olddecl == error_mark_node) /* If something went wrong when registering the declaration, use DECL1; we have to have a FUNCTION_DECL to use when parsing the body of the function. */ ; else /* Otherwise, OLDDECL is either a previous declaration of the same function or DECL1 itself. */ decl1 = olddecl; } else { /* We need to set the DECL_CONTEXT. */ if (!DECL_CONTEXT (decl1) && DECL_TEMPLATE_INFO (decl1)) DECL_CONTEXT (decl1) = DECL_CONTEXT (DECL_TI_TEMPLATE (decl1)); - /* And make sure we have enough default args. */ - check_default_args (decl1); } fntype = TREE_TYPE (decl1); } /* Reset these in case the call to pushdecl changed them. */ current_function_decl = decl1; cfun->decl = decl1; /* If we are (erroneously) defining a function that we have already defined before, wipe out what we knew before. */ if (!DECL_PENDING_INLINE_P (decl1)) DECL_SAVED_FUNCTION_DATA (decl1) = NULL; if (ctype && !doing_friend && !DECL_STATIC_FUNCTION_P (decl1)) { /* We know that this was set up by `grokclassfn'. We do not wait until `store_parm_decls', since evil parse errors may never get us to that point. Here we keep the consistency between `current_class_type' and `current_class_ptr'. */ tree t = DECL_ARGUMENTS (decl1); my_friendly_assert (t != NULL_TREE && TREE_CODE (t) == PARM_DECL, 162); my_friendly_assert (TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE, 19990811); cp_function_chain->x_current_class_ref = build_indirect_ref (t, NULL); cp_function_chain->x_current_class_ptr = t; /* Constructors and destructors need to know whether they're "in charge" of initializing virtual base classes. */ t = TREE_CHAIN (t); if (DECL_HAS_IN_CHARGE_PARM_P (decl1)) { current_in_charge_parm = t; t = TREE_CHAIN (t); } if (DECL_HAS_VTT_PARM_P (decl1)) { if (DECL_NAME (t) != vtt_parm_identifier) abort (); current_vtt_parm = t; } } if (DECL_INTERFACE_KNOWN (decl1)) { tree ctx = decl_function_context (decl1); if (DECL_NOT_REALLY_EXTERN (decl1)) DECL_EXTERNAL (decl1) = 0; if (ctx != NULL_TREE && DECL_DECLARED_INLINE_P (ctx) && TREE_PUBLIC (ctx)) /* This is a function in a local class in an extern inline function. */ comdat_linkage (decl1); } /* If this function belongs to an interface, it is public. If it belongs to someone else's interface, it is also external. This only affects inlines and template instantiations. */ else if (interface_unknown == 0 && ! DECL_TEMPLATE_INSTANTIATION (decl1)) { if (DECL_DECLARED_INLINE_P (decl1) || DECL_TEMPLATE_INSTANTIATION (decl1) || processing_template_decl) { DECL_EXTERNAL (decl1) = (interface_only || (DECL_DECLARED_INLINE_P (decl1) && ! flag_implement_inlines && !DECL_VINDEX (decl1))); /* For WIN32 we also want to put these in linkonce sections. */ maybe_make_one_only (decl1); } else DECL_EXTERNAL (decl1) = 0; DECL_NOT_REALLY_EXTERN (decl1) = 0; DECL_INTERFACE_KNOWN (decl1) = 1; } else if (interface_unknown && interface_only && ! DECL_TEMPLATE_INSTANTIATION (decl1)) { /* If MULTIPLE_SYMBOL_SPACES is defined and we saw a #pragma interface, we will have interface_only set but not interface_known. In that case, we don't want to use the normal heuristics because someone will supply a #pragma implementation elsewhere, and deducing it here would produce a conflict. */ comdat_linkage (decl1); DECL_EXTERNAL (decl1) = 0; DECL_INTERFACE_KNOWN (decl1) = 1; DECL_DEFER_OUTPUT (decl1) = 1; } else { /* This is a definition, not a reference. So clear DECL_EXTERNAL. */ DECL_EXTERNAL (decl1) = 0; if ((DECL_DECLARED_INLINE_P (decl1) || DECL_TEMPLATE_INSTANTIATION (decl1)) && ! DECL_INTERFACE_KNOWN (decl1) /* Don't try to defer nested functions for now. */ && ! decl_function_context (decl1)) DECL_DEFER_OUTPUT (decl1) = 1; else DECL_INTERFACE_KNOWN (decl1) = 1; } begin_scope (sk_function_parms, decl1); ++function_depth; if (DECL_DESTRUCTOR_P (decl1)) { dtor_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); DECL_CONTEXT (dtor_label) = current_function_decl; } start_fname_decls (); store_parm_decls (current_function_parms); return 1; } /* Store the parameter declarations into the current function declaration. This is called after parsing the parameter declarations, before digesting the body of the function. Also install to binding contour return value identifier, if any. */ static void store_parm_decls (tree current_function_parms) { tree fndecl = current_function_decl; tree parm; /* This is a chain of any other decls that came in among the parm declarations. If a parm is declared with enum {foo, bar} x; then CONST_DECLs for foo and bar are put here. */ tree nonparms = NULL_TREE; if (current_function_parms) { /* This case is when the function was defined with an ANSI prototype. The parms already have decls, so we need not do anything here except record them as in effect and complain if any redundant old-style parm decls were written. */ tree specparms = current_function_parms; tree next; /* Must clear this because it might contain TYPE_DECLs declared at class level. */ current_binding_level->names = NULL; /* If we're doing semantic analysis, then we'll call pushdecl for each of these. We must do them in reverse order so that they end in the correct forward order. */ specparms = nreverse (specparms); for (parm = specparms; parm; parm = next) { next = TREE_CHAIN (parm); if (TREE_CODE (parm) == PARM_DECL) { if (DECL_NAME (parm) == NULL_TREE || TREE_CODE (parm) != VOID_TYPE) pushdecl (parm); else error ("parameter `%D' declared void", parm); } else { /* If we find an enum constant or a type tag, put it aside for the moment. */ TREE_CHAIN (parm) = NULL_TREE; nonparms = chainon (nonparms, parm); } } /* Get the decls in their original chain order and record in the function. This is all and only the PARM_DECLs that were pushed into scope by the loop above. */ DECL_ARGUMENTS (fndecl) = getdecls (); } else DECL_ARGUMENTS (fndecl) = NULL_TREE; /* Now store the final chain of decls for the arguments as the decl-chain of the current lexical scope. Put the enumerators in as well, at the front so that DECL_ARGUMENTS is not modified. */ current_binding_level->names = chainon (nonparms, DECL_ARGUMENTS (fndecl)); /* Do the starting of the exception specifications, if we have any. */ if (flag_exceptions && !processing_template_decl && flag_enforce_eh_specs && TYPE_RAISES_EXCEPTIONS (TREE_TYPE (current_function_decl))) current_eh_spec_block = begin_eh_spec_block (); } /* We have finished doing semantic analysis on DECL, but have not yet generated RTL for its body. Save away our current state, so that when we want to generate RTL later we know what to do. */ static void save_function_data (tree decl) { struct language_function *f; /* Save the language-specific per-function data so that we can get it back when we really expand this function. */ my_friendly_assert (!DECL_PENDING_INLINE_P (decl), 19990908); /* Make a copy. */ f = ggc_alloc (sizeof (struct language_function)); memcpy (f, cp_function_chain, sizeof (struct language_function)); DECL_SAVED_FUNCTION_DATA (decl) = f; /* Clear out the bits we don't need. */ f->base.x_stmt_tree.x_last_stmt = NULL_TREE; f->base.x_stmt_tree.x_last_expr_type = NULL_TREE; f->x_named_label_uses = NULL; f->bindings = NULL; f->x_local_names = NULL; /* If we've already decided that we cannot inline this function, we must remember that fact when we actually go to expand the function. */ if (current_function_cannot_inline) { f->cannot_inline = current_function_cannot_inline; DECL_INLINE (decl) = 0; } } /* Add a note to mark the beginning of the main body of the constructor. This is used to set up the data structures for the cleanup regions for fully-constructed bases and members. */ static void begin_constructor_body (void) { } /* Add a note to mark the end of the main body of the constructor. This is used to end the cleanup regions for fully-constructed bases and members. */ static void finish_constructor_body (void) { } /* Do all the processing for the beginning of a destructor; set up the vtable pointers and cleanups for bases and members. */ static void begin_destructor_body (void) { tree if_stmt; tree compound_stmt; /* If the dtor is empty, and we know there is not any possible way we could use any vtable entries, before they are possibly set by a base class dtor, we don't have to setup the vtables, as we know that any base class dtor will set up any vtables it needs. We avoid MI, because one base class dtor can do a virtual dispatch to an overridden function that would need to have a non-related vtable set up, we cannot avoid setting up vtables in that case. We could change this to see if there is just one vtable. ??? In the destructor for a class, the vtables are set appropriately for that class. There will be no non-related vtables. jason 2001-12-11. */ if_stmt = begin_if_stmt (); /* If it is not safe to avoid setting up the vtables, then someone will change the condition to be boolean_true_node. (Actually, for now, we do not have code to set the condition appropriately, so we just assume that we always need to initialize the vtables.) */ finish_if_stmt_cond (boolean_true_node, if_stmt); compound_stmt = begin_compound_stmt (/*has_no_scope=*/false); /* Make all virtual function table pointers in non-virtual base classes point to CURRENT_CLASS_TYPE's virtual function tables. */ initialize_vtbl_ptrs (current_class_ptr); finish_compound_stmt (compound_stmt); finish_then_clause (if_stmt); finish_if_stmt (); /* And insert cleanups for our bases and members so that they will be properly destroyed if we throw. */ push_base_cleanups (); } /* At the end of every destructor we generate code to delete the object if necessary. Do that now. */ static void finish_destructor_body (void) { tree exprstmt; /* Any return from a destructor will end up here; that way all base and member cleanups will be run when the function returns. */ add_stmt (build_stmt (LABEL_STMT, dtor_label)); /* In a virtual destructor, we must call delete. */ if (DECL_VIRTUAL_P (current_function_decl)) { tree if_stmt; tree virtual_size = cxx_sizeof (current_class_type); /* [class.dtor] At the point of definition of a virtual destructor (including an implicit definition), non-placement operator delete shall be looked up in the scope of the destructor's class and if found shall be accessible and unambiguous. */ exprstmt = build_op_delete_call (DELETE_EXPR, current_class_ptr, virtual_size, LOOKUP_NORMAL | LOOKUP_SPECULATIVELY, NULL_TREE); if_stmt = begin_if_stmt (); finish_if_stmt_cond (build (BIT_AND_EXPR, integer_type_node, current_in_charge_parm, integer_one_node), if_stmt); finish_expr_stmt (exprstmt); finish_then_clause (if_stmt); finish_if_stmt (); } } /* Do the necessary processing for the beginning of a function body, which in this case includes member-initializers, but not the catch clauses of a function-try-block. Currently, this means opening a binding level for the member-initializers (in a ctor) and member cleanups (in a dtor). In other functions, this isn't necessary, but it doesn't hurt. */ tree begin_function_body (void) { tree stmt; if (processing_template_decl) /* Do nothing now. */; else /* Always keep the BLOCK node associated with the outermost pair of curly braces of a function. These are needed for correct operation of dwarfout.c. */ keep_next_level (true); stmt = begin_compound_stmt (/*has_no_scope=*/false); COMPOUND_STMT_BODY_BLOCK (stmt) = 1; if (processing_template_decl) /* Do nothing now. */; else if (DECL_CONSTRUCTOR_P (current_function_decl)) begin_constructor_body (); else if (DECL_DESTRUCTOR_P (current_function_decl)) begin_destructor_body (); return stmt; } /* Do the processing for the end of a function body. Currently, this means closing out the cleanups for fully-constructed bases and members, and in the case of the destructor, deleting the object if desired. Again, this is only meaningful for [cd]tors, since they are the only functions where there is a significant distinction between the main body and any function catch clauses. Handling, say, main() return semantics here would be wrong, as flowing off the end of a function catch clause for main() would also need to return 0. */ void finish_function_body (tree compstmt) { /* Close the block. */ finish_compound_stmt (compstmt); if (processing_template_decl) /* Do nothing now. */; else if (DECL_CONSTRUCTOR_P (current_function_decl)) finish_constructor_body (); else if (DECL_DESTRUCTOR_P (current_function_decl)) finish_destructor_body (); } /* Finish up a function declaration and compile that function all the way to assembler language output. The free the storage for the function definition. FLAGS is a bitwise or of the following values: 2 - INCLASS_INLINE We just finished processing the body of an in-class inline function definition. (This processing will have taken place after the class definition is complete.) */ tree finish_function (int flags) { tree fndecl = current_function_decl; tree fntype, ctype = NULL_TREE; int inclass_inline = (flags & 2) != 0; int nested; /* When we get some parse errors, we can end up without a current_function_decl, so cope. */ if (fndecl == NULL_TREE) return error_mark_node; if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fndecl) && DECL_VIRTUAL_P (fndecl) && !processing_template_decl) { tree fnclass = DECL_CONTEXT (fndecl); if (fndecl == CLASSTYPE_KEY_METHOD (fnclass)) keyed_classes = tree_cons (NULL_TREE, fnclass, keyed_classes); } nested = function_depth > 1; fntype = TREE_TYPE (fndecl); /* TREE_READONLY (fndecl) = 1; This caused &foo to be of type ptr-to-const-function which then got a warning when stored in a ptr-to-function variable. */ my_friendly_assert (building_stmt_tree (), 20000911); /* For a cloned function, we've already got all the code we need; there's no need to add any extra bits. */ if (!DECL_CLONED_FUNCTION_P (fndecl)) { if (DECL_MAIN_P (current_function_decl)) { /* Make it so that `main' always returns 0 by default. */ #if VMS_TARGET finish_return_stmt (integer_one_node); #else finish_return_stmt (integer_zero_node); #endif } /* Finish dealing with exception specifiers. */ if (flag_exceptions && !processing_template_decl && flag_enforce_eh_specs && TYPE_RAISES_EXCEPTIONS (TREE_TYPE (current_function_decl))) finish_eh_spec_block (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (current_function_decl)), current_eh_spec_block); } finish_fname_decls (); /* If we're saving up tree structure, tie off the function now. */ finish_stmt_tree (&DECL_SAVED_TREE (fndecl)); /* If this function can't throw any exceptions, remember that. */ if (!processing_template_decl && !cp_function_chain->can_throw && !flag_non_call_exceptions) TREE_NOTHROW (fndecl) = 1; /* This must come after expand_function_end because cleanups might have declarations (from inline functions) that need to go into this function's blocks. */ /* If the current binding level isn't the outermost binding level for this function, either there is a bug, or we have experienced syntax errors and the statement tree is malformed. */ if (current_binding_level->kind != sk_function_parms) { /* Make sure we have already experienced errors. */ if (errorcount == 0) abort (); /* Throw away the broken statement tree and extra binding levels. */ DECL_SAVED_TREE (fndecl) = build_stmt (COMPOUND_STMT, NULL_TREE); while (current_binding_level->kind != sk_function_parms) { if (current_binding_level->kind == sk_class) pop_nested_class (); else poplevel (0, 0, 0); } } poplevel (1, 0, 1); /* Statements should always be full-expressions at the outermost set of curly braces for a function. */ my_friendly_assert (stmts_are_full_exprs_p (), 19990831); /* Set up the named return value optimization, if we can. Here, we eliminate the copy from the nrv into the RESULT_DECL and any cleanup for the nrv. genrtl_start_function and declare_return_variable handle making the nrv and RESULT_DECL share space. */ if (current_function_return_value) { tree r = current_function_return_value; tree outer; if (r != error_mark_node /* This is only worth doing for fns that return in memory--and simpler, since we don't have to worry about promoted modes. */ && aggregate_value_p (TREE_TYPE (TREE_TYPE (fndecl)), fndecl) /* Only allow this for variables declared in the outer scope of the function so we know that their lifetime always ends with a return; see g++.dg/opt/nrv6.C. We could be more flexible if we were to do this optimization in tree-ssa. */ /* Skip the artificial function body block. */ && (outer = BLOCK_SUBBLOCKS (BLOCK_SUBBLOCKS (DECL_INITIAL (fndecl))), chain_member (r, BLOCK_VARS (outer)))) { DECL_ALIGN (r) = DECL_ALIGN (DECL_RESULT (fndecl)); walk_tree_without_duplicates (&DECL_SAVED_TREE (fndecl), nullify_returns_r, r); } else /* Clear it so genrtl_start_function and declare_return_variable know we're not optimizing. */ current_function_return_value = NULL_TREE; } /* Remember that we were in class scope. */ if (current_class_name) ctype = current_class_type; /* Must mark the RESULT_DECL as being in this function. */ DECL_CONTEXT (DECL_RESULT (fndecl)) = fndecl; /* Set the BLOCK_SUPERCONTEXT of the outermost function scope to point to the FUNCTION_DECL node itself. */ BLOCK_SUPERCONTEXT (DECL_INITIAL (fndecl)) = fndecl; /* Save away current state, if appropriate. */ if (!processing_template_decl) save_function_data (fndecl); /* If this function calls `setjmp' it cannot be inlined. When `longjmp' is called it is not guaranteed to restore the value of local variables that have been modified since the call to `setjmp'. So, if were to inline this function into some caller `c', then when we `longjmp', we might not restore all variables in `c'. (It might seem, at first blush, that there's no way for this function to modify local variables in `c', but their addresses may have been stored somewhere accessible to this function.) */ if (!processing_template_decl && calls_setjmp_p (fndecl)) DECL_UNINLINABLE (fndecl) = 1; /* Complain if there's just no return statement. */ if (warn_return_type && TREE_CODE (TREE_TYPE (fntype)) != VOID_TYPE && !dependent_type_p (TREE_TYPE (fntype)) && !current_function_returns_value && !current_function_returns_null /* Don't complain if we abort or throw. */ && !current_function_returns_abnormally && !DECL_NAME (DECL_RESULT (fndecl)) /* Normally, with -Wreturn-type, flow will complain. Unless we're an inline function, as we might never be compiled separately. */ && (DECL_INLINE (fndecl) || processing_template_decl)) warning ("no return statement in function returning non-void"); /* We're leaving the context of this function, so zap cfun. It's still in DECL_SAVED_INSNS, and we'll restore it in tree_rest_of_compilation. */ cfun = NULL; current_function_decl = NULL; /* If this is an in-class inline definition, we may have to pop the bindings for the template parameters that we added in maybe_begin_member_template_processing when start_function was called. */ if (inclass_inline) maybe_end_member_template_processing (); /* Leave the scope of the class. */ if (ctype) pop_nested_class (); --function_depth; /* Clean up. */ if (! nested) /* Let the error reporting routines know that we're outside a function. For a nested function, this value is used in cxx_pop_function_context and then reset via pop_function_context. */ current_function_decl = NULL_TREE; return fndecl; } /* Create the FUNCTION_DECL for a function definition. DECLSPECS and DECLARATOR are the parts of the declaration; they describe the return type and the name of the function, but twisted together in a fashion that parallels the syntax of C. This function creates a binding context for the function body as well as setting up the FUNCTION_DECL in current_function_decl. Returns a FUNCTION_DECL on success. If the DECLARATOR is not suitable for a function (it defines a datum instead), we return 0, which tells yyparse to report a parse error. May return void_type_node indicating that this method is actually a friend. See grokfield for more details. Came here with a `.pushlevel' . DO NOT MAKE ANY CHANGES TO THIS CODE WITHOUT MAKING CORRESPONDING CHANGES TO CODE IN `grokfield'. */ tree start_method (tree declspecs, tree declarator, tree attrlist) { tree fndecl = grokdeclarator (declarator, declspecs, MEMFUNCDEF, 0, &attrlist); if (fndecl == error_mark_node) return error_mark_node; if (fndecl == NULL || TREE_CODE (fndecl) != FUNCTION_DECL) { error ("invalid member function declaration"); return error_mark_node; } if (attrlist) cplus_decl_attributes (&fndecl, attrlist, 0); /* Pass friends other than inline friend functions back. */ if (fndecl == void_type_node) return fndecl; if (DECL_IN_AGGR_P (fndecl)) { if (IDENTIFIER_ERROR_LOCUS (DECL_ASSEMBLER_NAME (fndecl)) != current_class_type) { if (DECL_CONTEXT (fndecl) && TREE_CODE( DECL_CONTEXT (fndecl)) != NAMESPACE_DECL) error ("`%D' is already defined in class `%T'", fndecl, DECL_CONTEXT (fndecl)); } return void_type_node; } check_template_shadow (fndecl); DECL_DECLARED_INLINE_P (fndecl) = 1; if (flag_default_inline) DECL_INLINE (fndecl) = 1; /* We process method specializations in finish_struct_1. */ if (processing_template_decl && !DECL_TEMPLATE_SPECIALIZATION (fndecl)) { fndecl = push_template_decl (fndecl); if (fndecl == error_mark_node) return fndecl; } if (! DECL_FRIEND_P (fndecl)) { if (TREE_CHAIN (fndecl)) { fndecl = copy_node (fndecl); TREE_CHAIN (fndecl) = NULL_TREE; } grok_special_member_properties (fndecl); } cp_finish_decl (fndecl, NULL_TREE, NULL_TREE, 0); /* Make a place for the parms. */ begin_scope (sk_function_parms, fndecl); DECL_IN_AGGR_P (fndecl) = 1; return fndecl; } /* Go through the motions of finishing a function definition. We don't compile this method until after the whole class has been processed. FINISH_METHOD must return something that looks as though it came from GROKFIELD (since we are defining a method, after all). This is called after parsing the body of the function definition. STMTS is the chain of statements that makes up the function body. DECL is the ..._DECL that `start_method' provided. */ tree finish_method (tree decl) { tree fndecl = decl; tree old_initial; tree link; if (decl == void_type_node) return decl; old_initial = DECL_INITIAL (fndecl); /* Undo the level for the parms (from start_method). This is like poplevel, but it causes nothing to be saved. Saving information here confuses symbol-table output routines. Besides, this information will be correctly output when this method is actually compiled. */ /* Clear out the meanings of the local variables of this level; also record in each decl which block it belongs to. */ for (link = current_binding_level->names; link; link = TREE_CHAIN (link)) { if (DECL_NAME (link) != NULL_TREE) pop_binding (DECL_NAME (link), link); my_friendly_assert (TREE_CODE (link) != FUNCTION_DECL, 163); DECL_CONTEXT (link) = NULL_TREE; } poplevel (0, 0, 0); DECL_INITIAL (fndecl) = old_initial; /* We used to check if the context of FNDECL was different from current_class_type as another way to get inside here. This didn't work for String.cc in libg++. */ if (DECL_FRIEND_P (fndecl)) { CLASSTYPE_INLINE_FRIENDS (current_class_type) = tree_cons (NULL_TREE, fndecl, CLASSTYPE_INLINE_FRIENDS (current_class_type)); decl = void_type_node; } return decl; } /* VAR is a VAR_DECL. If its type is incomplete, remember VAR so that we can lay it out later, when and if its type becomes complete. */ void maybe_register_incomplete_var (tree var) { my_friendly_assert (TREE_CODE (var) == VAR_DECL, 20020406); /* Keep track of variables with incomplete types. */ if (!processing_template_decl && TREE_TYPE (var) != error_mark_node && DECL_EXTERNAL (var)) { tree inner_type = TREE_TYPE (var); while (TREE_CODE (inner_type) == ARRAY_TYPE) inner_type = TREE_TYPE (inner_type); inner_type = TYPE_MAIN_VARIANT (inner_type); if ((!COMPLETE_TYPE_P (inner_type) && CLASS_TYPE_P (inner_type)) /* RTTI TD entries are created while defining the type_info. */ || (TYPE_LANG_SPECIFIC (inner_type) && TYPE_BEING_DEFINED (inner_type))) incomplete_vars = tree_cons (inner_type, var, incomplete_vars); } } /* Called when a class type (given by TYPE) is defined. If there are any existing VAR_DECLs whose type hsa been completed by this declaration, update them now. */ void complete_vars (tree type) { tree *list = &incomplete_vars; my_friendly_assert (CLASS_TYPE_P (type), 20020406); while (*list) { if (same_type_p (type, TREE_PURPOSE (*list))) { tree var = TREE_VALUE (*list); /* Complete the type of the variable. The VAR_DECL itself will be laid out in expand_expr. */ complete_type (TREE_TYPE (var)); /* Remove this entry from the list. */ *list = TREE_CHAIN (*list); } else list = &TREE_CHAIN (*list); } } /* If DECL is of a type which needs a cleanup, build that cleanup here. */ tree cxx_maybe_build_cleanup (tree decl) { tree type = TREE_TYPE (decl); if (type != error_mark_node && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type)) { int flags = LOOKUP_NORMAL|LOOKUP_DESTRUCTOR; tree rval; if (TREE_CODE (type) == ARRAY_TYPE) rval = decl; else { cxx_mark_addressable (decl); rval = build_unary_op (ADDR_EXPR, decl, 0); } /* Optimize for space over speed here. */ if (! TYPE_USES_VIRTUAL_BASECLASSES (type) || flag_expensive_optimizations) flags |= LOOKUP_NONVIRTUAL; rval = build_delete (TREE_TYPE (rval), rval, sfk_complete_destructor, flags, 0); if (TYPE_USES_VIRTUAL_BASECLASSES (type) && ! TYPE_HAS_DESTRUCTOR (type)) rval = build_compound_expr (rval, build_vbase_delete (type, decl)); return rval; } return NULL_TREE; } /* When a stmt has been parsed, this function is called. */ void finish_stmt (void) { /* Always assume this statement was not an expression statement. If it actually was an expression statement, its our callers responsibility to fix this up. */ last_expr_type = NULL_TREE; } /* DECL was originally constructed as a non-static member function, but turned out to be static. Update it accordingly. */ void revert_static_member_fn (tree decl) { tree tmp; tree function = TREE_TYPE (decl); tree args = TYPE_ARG_TYPES (function); if (cp_type_quals (TREE_TYPE (TREE_VALUE (args))) != TYPE_UNQUALIFIED) error ("static member function `%#D' declared with type qualifiers", decl); args = TREE_CHAIN (args); tmp = build_function_type (TREE_TYPE (function), args); tmp = build_qualified_type (tmp, cp_type_quals (function)); tmp = build_exception_variant (tmp, TYPE_RAISES_EXCEPTIONS (function)); TREE_TYPE (decl) = tmp; if (DECL_ARGUMENTS (decl)) DECL_ARGUMENTS (decl) = TREE_CHAIN (DECL_ARGUMENTS (decl)); DECL_STATIC_FUNCTION_P (decl) = 1; } /* Initialize the variables used during compilation of a C++ function. */ void cxx_push_function_context (struct function * f) { struct language_function *p = ggc_alloc_cleared (sizeof (struct language_function)); f->language = p; /* Whenever we start a new function, we destroy temporaries in the usual way. */ current_stmt_tree ()->stmts_are_full_exprs_p = 1; if (f->decl) { tree fn = f->decl; if (DECL_SAVED_FUNCTION_DATA (fn)) { /* If we already parsed this function, and we're just expanding it now, restore saved state. */ *cp_function_chain = *DECL_SAVED_FUNCTION_DATA (fn); /* If we decided that we didn't want to inline this function, make sure the back-end knows that. */ if (!current_function_cannot_inline) current_function_cannot_inline = cp_function_chain->cannot_inline; /* We don't need the saved data anymore. Unless this is an inline function; we need the named return value info for cp_copy_res_decl_for_inlining. */ if (! DECL_INLINE (fn)) DECL_SAVED_FUNCTION_DATA (fn) = NULL; } } } /* Free the language-specific parts of F, now that we've finished compiling the function. */ void cxx_pop_function_context (struct function * f) { f->language = 0; } /* Return which tree structure is used by T, or TS_CP_GENERIC if T is one of the language-independent trees. */ enum cp_tree_node_structure_enum cp_tree_node_structure (union lang_tree_node * t) { switch (TREE_CODE (&t->generic)) { case DEFAULT_ARG: return TS_CP_DEFAULT_ARG; case IDENTIFIER_NODE: return TS_CP_IDENTIFIER; case OVERLOAD: return TS_CP_OVERLOAD; case TEMPLATE_PARM_INDEX: return TS_CP_TPI; case PTRMEM_CST: return TS_CP_PTRMEM; case BASELINK: return TS_CP_BASELINK; case WRAPPER: return TS_CP_WRAPPER; default: return TS_CP_GENERIC; } } /* Build the void_list_node (void_type_node having been created). */ tree build_void_list_node (void) { tree t = build_tree_list (NULL_TREE, void_type_node); TREE_PARMLIST (t) = 1; return t; } static int cp_missing_noreturn_ok_p (tree decl) { /* A missing noreturn is ok for the `main' function. */ return DECL_MAIN_P (decl); } #include "gt-cp-decl.h" #include "gtype-cp.h" diff --git a/contrib/gcc/cppinit.c b/contrib/gcc/cppinit.c index 6ea1ca21d224..a713cea0a093 100644 --- a/contrib/gcc/cppinit.c +++ b/contrib/gcc/cppinit.c @@ -1,630 +1,634 @@ /* CPP Library. Copyright (C) 1986, 1987, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. Contributed by Per Bothner, 1994-95. Based on CCCP program by Paul Rubin, June 1986 Adapted to ANSI C, Richard Stallman, Jan 1987 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* $FreeBSD$ */ #include "config.h" #include "system.h" #include "cpplib.h" #include "cpphash.h" #include "mkdeps.h" static void init_library (void); static void mark_named_operators (cpp_reader *); static void read_original_filename (cpp_reader *); static void read_original_directory (cpp_reader *); static void post_options (cpp_reader *); /* If we have designated initializers (GCC >2.7) these tables can be initialized, constant data. Otherwise, they have to be filled in at runtime. */ #if HAVE_DESIGNATED_INITIALIZERS #define init_trigraph_map() /* Nothing. */ #define TRIGRAPH_MAP \ __extension__ const uchar _cpp_trigraph_map[UCHAR_MAX + 1] = { #define END }; #define s(p, v) [p] = v, #else #define TRIGRAPH_MAP uchar _cpp_trigraph_map[UCHAR_MAX + 1] = { 0 }; \ static void init_trigraph_map (void) { \ unsigned char *x = _cpp_trigraph_map; #define END } #define s(p, v) x[p] = v; #endif TRIGRAPH_MAP s('=', '#') s(')', ']') s('!', '|') s('(', '[') s('\'', '^') s('>', '}') s('/', '\\') s('<', '{') s('-', '~') END #undef s #undef END #undef TRIGRAPH_MAP /* A set of booleans indicating what CPP features each source language requires. */ struct lang_flags { char c99; char cplusplus; char extended_numbers; char std; char cplusplus_comments; char digraphs; }; static const struct lang_flags lang_defaults[] = { /* c99 c++ xnum std // digr */ /* GNUC89 */ { 0, 0, 1, 0, 1, 1 }, /* GNUC99 */ { 1, 0, 1, 0, 1, 1 }, /* STDC89 */ { 0, 0, 0, 1, 0, 0 }, /* STDC94 */ { 0, 0, 0, 1, 0, 1 }, /* STDC99 */ { 1, 0, 1, 1, 1, 1 }, /* GNUCXX */ { 0, 1, 1, 0, 1, 1 }, /* CXX98 */ { 0, 1, 1, 1, 1, 1 }, /* ASM */ { 0, 0, 1, 0, 1, 0 } }; /* Sets internal flags correctly for a given language. */ void cpp_set_lang (cpp_reader *pfile, enum c_lang lang) { const struct lang_flags *l = &lang_defaults[(int) lang]; CPP_OPTION (pfile, lang) = lang; CPP_OPTION (pfile, c99) = l->c99; CPP_OPTION (pfile, cplusplus) = l->cplusplus; CPP_OPTION (pfile, extended_numbers) = l->extended_numbers; CPP_OPTION (pfile, std) = l->std; CPP_OPTION (pfile, trigraphs) = l->std; CPP_OPTION (pfile, cplusplus_comments) = l->cplusplus_comments; CPP_OPTION (pfile, digraphs) = l->digraphs; } /* Initialize library global state. */ static void init_library (void) { static int initialized = 0; if (! initialized) { initialized = 1; /* Set up the trigraph map. This doesn't need to do anything if we were compiled with a compiler that supports C99 designated initializers. */ init_trigraph_map (); } } /* Initialize a cpp_reader structure. */ cpp_reader * cpp_create_reader (enum c_lang lang, hash_table *table) { cpp_reader *pfile; /* Initialize this instance of the library if it hasn't been already. */ init_library (); pfile = xcalloc (1, sizeof (cpp_reader)); cpp_set_lang (pfile, lang); CPP_OPTION (pfile, warn_multichar) = 1; CPP_OPTION (pfile, discard_comments) = 1; CPP_OPTION (pfile, discard_comments_in_macro_exp) = 1; CPP_OPTION (pfile, show_column) = 1; CPP_OPTION (pfile, tabstop) = 8; CPP_OPTION (pfile, operator_names) = 1; CPP_OPTION (pfile, warn_trigraphs) = 2; CPP_OPTION (pfile, warn_endif_labels) = 1; CPP_OPTION (pfile, warn_deprecated) = 1; CPP_OPTION (pfile, warn_long_long) = !CPP_OPTION (pfile, c99); CPP_OPTION (pfile, dollars_in_ident) = 1; CPP_OPTION (pfile, warn_dollars) = 1; /* Default CPP arithmetic to something sensible for the host for the benefit of dumb users like fix-header. */ CPP_OPTION (pfile, precision) = CHAR_BIT * sizeof (long); CPP_OPTION (pfile, char_precision) = CHAR_BIT; CPP_OPTION (pfile, wchar_precision) = CHAR_BIT * sizeof (int); CPP_OPTION (pfile, int_precision) = CHAR_BIT * sizeof (int); CPP_OPTION (pfile, unsigned_char) = 0; CPP_OPTION (pfile, unsigned_wchar) = 1; CPP_OPTION (pfile, bytes_big_endian) = 1; /* does not matter */ /* Default to locale/UTF-8. */ CPP_OPTION (pfile, narrow_charset) = _cpp_default_encoding (); CPP_OPTION (pfile, wide_charset) = 0; CPP_OPTION (pfile, input_charset) = _cpp_default_encoding (); /* A fake empty "directory" used as the starting point for files looked up without a search path. Name cannot be '/' because we don't want to prepend anything at all to filenames using it. All other entries are correct zero-initialized. */ pfile->no_search_path.name = (char *) ""; /* Initialize the line map. Start at logical line 1, so we can use a line number of zero for special states. */ linemap_init (&pfile->line_maps); pfile->line = 1; /* Initialize lexer state. */ pfile->state.save_comments = ! CPP_OPTION (pfile, discard_comments); /* Set up static tokens. */ pfile->avoid_paste.type = CPP_PADDING; pfile->avoid_paste.val.source = NULL; pfile->eof.type = CPP_EOF; pfile->eof.flags = 0; /* Create a token buffer for the lexer. */ _cpp_init_tokenrun (&pfile->base_run, 250); pfile->cur_run = &pfile->base_run; pfile->cur_token = pfile->base_run.base; /* Initialize the base context. */ pfile->context = &pfile->base_context; pfile->base_context.macro = 0; pfile->base_context.prev = pfile->base_context.next = 0; /* Aligned and unaligned storage. */ pfile->a_buff = _cpp_get_buff (pfile, 0); pfile->u_buff = _cpp_get_buff (pfile, 0); /* The expression parser stack. */ _cpp_expand_op_stack (pfile); /* Initialize the buffer obstack. */ _obstack_begin (&pfile->buffer_ob, 0, 0, (void *(*) (long)) xmalloc, (void (*) (void *)) free); _cpp_init_files (pfile); _cpp_init_hashtable (pfile, table); return pfile; } /* Free resources used by PFILE. Accessing PFILE after this function returns leads to undefined behavior. Returns the error count. */ void cpp_destroy (cpp_reader *pfile) { cpp_context *context, *contextn; tokenrun *run, *runn; free (pfile->op_stack); while (CPP_BUFFER (pfile) != NULL) _cpp_pop_buffer (pfile); if (pfile->out.base) free (pfile->out.base); if (pfile->macro_buffer) { free (pfile->macro_buffer); pfile->macro_buffer = NULL; pfile->macro_buffer_len = 0; } if (pfile->deps) deps_free (pfile->deps); obstack_free (&pfile->buffer_ob, 0); _cpp_destroy_hashtable (pfile); _cpp_cleanup_files (pfile); _cpp_destroy_iconv (pfile); _cpp_free_buff (pfile->a_buff); _cpp_free_buff (pfile->u_buff); _cpp_free_buff (pfile->free_buffs); for (run = &pfile->base_run; run; run = runn) { runn = run->next; free (run->base); if (run != &pfile->base_run) free (run); } for (context = pfile->base_context.next; context; context = contextn) { contextn = context->next; free (context); } linemap_free (&pfile->line_maps); free (pfile); } /* This structure defines one built-in identifier. A node will be entered in the hash table under the name NAME, with value VALUE. There are two tables of these. builtin_array holds all the "builtin" macros: these are handled by builtin_macro() in cppmacro.c. Builtin is somewhat of a misnomer -- the property of interest is that these macros require special code to compute their expansions. The value is a "builtin_type" enumerator. operator_array holds the C++ named operators. These are keywords which act as aliases for punctuators. In C++, they cannot be altered through #define, and #if recognizes them as operators. In C, these are not entered into the hash table at all (but see ). The value is a token-type enumerator. */ struct builtin { const uchar *name; unsigned short len; unsigned short value; }; #define B(n, t) { DSC(n), t } static const struct builtin builtin_array[] = { B("__TIME__", BT_TIME), B("__DATE__", BT_DATE), B("__FILE__", BT_FILE), B("__BASE_FILE__", BT_BASE_FILE), B("__LINE__", BT_SPECLINE), B("__INCLUDE_LEVEL__", BT_INCLUDE_LEVEL), /* Keep builtins not used for -traditional-cpp at the end, and update init_builtins() if any more are added. */ B("_Pragma", BT_PRAGMA), B("__STDC__", BT_STDC), }; static const struct builtin operator_array[] = { B("and", CPP_AND_AND), B("and_eq", CPP_AND_EQ), B("bitand", CPP_AND), B("bitor", CPP_OR), B("compl", CPP_COMPL), B("not", CPP_NOT), B("not_eq", CPP_NOT_EQ), B("or", CPP_OR_OR), B("or_eq", CPP_OR_EQ), B("xor", CPP_XOR), B("xor_eq", CPP_XOR_EQ) }; #undef B /* Mark the C++ named operators in the hash table. */ static void mark_named_operators (cpp_reader *pfile) { const struct builtin *b; for (b = operator_array; b < (operator_array + ARRAY_SIZE (operator_array)); b++) { cpp_hashnode *hp = cpp_lookup (pfile, b->name, b->len); hp->flags |= NODE_OPERATOR; hp->is_directive = 0; hp->directive_index = b->value; } } /* Read the builtins table above and enter them, and language-specific macros, into the hash table. HOSTED is true if this is a hosted environment. */ void cpp_init_builtins (cpp_reader *pfile, int hosted) { const struct builtin *b; size_t n = ARRAY_SIZE (builtin_array); if (CPP_OPTION (pfile, traditional)) n -= 2; for(b = builtin_array; b < builtin_array + n; b++) { cpp_hashnode *hp = cpp_lookup (pfile, b->name, b->len); hp->type = NT_MACRO; hp->flags |= NODE_BUILTIN | NODE_WARN; hp->value.builtin = b->value; } if (CPP_OPTION (pfile, cplusplus)) _cpp_define_builtin (pfile, "__cplusplus 1"); else if (CPP_OPTION (pfile, lang) == CLK_ASM) _cpp_define_builtin (pfile, "__ASSEMBLER__ 1"); else if (CPP_OPTION (pfile, lang) == CLK_STDC94) _cpp_define_builtin (pfile, "__STDC_VERSION__ 199409L"); else if (CPP_OPTION (pfile, c99)) _cpp_define_builtin (pfile, "__STDC_VERSION__ 199901L"); if (hosted) _cpp_define_builtin (pfile, "__STDC_HOSTED__ 1"); else _cpp_define_builtin (pfile, "__STDC_HOSTED__ 0"); if (CPP_OPTION (pfile, objc)) _cpp_define_builtin (pfile, "__OBJC__ 1"); } /* Sanity-checks are dependent on command-line options, so it is called as a subroutine of cpp_read_main_file (). */ #if ENABLE_CHECKING static void sanity_checks (cpp_reader *); static void sanity_checks (cpp_reader *pfile) { cppchar_t test = 0; size_t max_precision = 2 * CHAR_BIT * sizeof (cpp_num_part); /* Sanity checks for assumptions about CPP arithmetic and target type precisions made by cpplib. */ test--; if (test < 1) cpp_error (pfile, CPP_DL_ICE, "cppchar_t must be an unsigned type"); if (CPP_OPTION (pfile, precision) > max_precision) cpp_error (pfile, CPP_DL_ICE, "preprocessor arithmetic has maximum precision of %lu bits;" " target requires %lu bits", (unsigned long) max_precision, (unsigned long) CPP_OPTION (pfile, precision)); if (CPP_OPTION (pfile, precision) < CPP_OPTION (pfile, int_precision)) cpp_error (pfile, CPP_DL_ICE, "CPP arithmetic must be at least as precise as a target int"); if (CPP_OPTION (pfile, char_precision) < 8) cpp_error (pfile, CPP_DL_ICE, "target char is less than 8 bits wide"); if (CPP_OPTION (pfile, wchar_precision) < CPP_OPTION (pfile, char_precision)) cpp_error (pfile, CPP_DL_ICE, "target wchar_t is narrower than target char"); if (CPP_OPTION (pfile, int_precision) < CPP_OPTION (pfile, char_precision)) cpp_error (pfile, CPP_DL_ICE, "target int is narrower than target char"); /* This is assumed in eval_token() and could be fixed if necessary. */ if (sizeof (cppchar_t) > sizeof (cpp_num_part)) cpp_error (pfile, CPP_DL_ICE, "CPP half-integer narrower than CPP character"); if (CPP_OPTION (pfile, wchar_precision) > BITS_PER_CPPCHAR_T) cpp_error (pfile, CPP_DL_ICE, "CPP on this host cannot handle wide character constants over" " %lu bits, but the target requires %lu bits", (unsigned long) BITS_PER_CPPCHAR_T, (unsigned long) CPP_OPTION (pfile, wchar_precision)); } #else # define sanity_checks(PFILE) #endif /* Add a dependency target. Can be called any number of times before cpp_read_main_file(). If no targets have been added before cpp_read_main_file(), then the default target is used. */ void cpp_add_dependency_target (cpp_reader *pfile, const char *target, int quote) { if (!pfile->deps) pfile->deps = deps_init (); deps_add_target (pfile->deps, target, quote); } /* This is called after options have been parsed, and partially processed. */ void cpp_post_options (cpp_reader *pfile) { sanity_checks (pfile); post_options (pfile); /* Mark named operators before handling command line macros. */ if (CPP_OPTION (pfile, cplusplus) && CPP_OPTION (pfile, operator_names)) mark_named_operators (pfile); } /* Setup for processing input from the file named FNAME, or stdin if it is the empty string. Return the original filename on success (e.g. foo.i->foo.c), or NULL on failure. */ const char * cpp_read_main_file (cpp_reader *pfile, const char *fname) { if (CPP_OPTION (pfile, deps.style) != DEPS_NONE) { if (!pfile->deps) pfile->deps = deps_init (); /* Set the default target (if there is none already). */ deps_add_default_target (pfile->deps, fname); } pfile->main_file - = _cpp_find_file (pfile, fname, &pfile->no_search_path, false); + = _cpp_find_file (pfile, fname, &pfile->no_search_path, false, 0); if (_cpp_find_failed (pfile->main_file)) return NULL; _cpp_stack_file (pfile, pfile->main_file, false); /* For foo.i, read the original filename foo.c now, for the benefit of the front ends. */ if (CPP_OPTION (pfile, preprocessed)) { read_original_filename (pfile); + if (!pfile->map) + return NULL; fname = pfile->map->to_file; } return fname; } /* For preprocessed files, if the first tokens are of the form # NUM. handle the directive so we know the original file name. This will generate file_change callbacks, which the front ends must handle appropriately given their state of initialization. */ static void read_original_filename (cpp_reader *pfile) { const cpp_token *token, *token1; /* Lex ahead; if the first tokens are of the form # NUM, then process the directive, otherwise back up. */ token = _cpp_lex_direct (pfile); if (token->type == CPP_HASH) { + pfile->state.in_directive = 1; token1 = _cpp_lex_direct (pfile); _cpp_backup_tokens (pfile, 1); + pfile->state.in_directive = 0; /* If it's a #line directive, handle it. */ if (token1->type == CPP_NUMBER) { _cpp_handle_directive (pfile, token->flags & PREV_WHITE); read_original_directory (pfile); return; } } /* Backup as if nothing happened. */ _cpp_backup_tokens (pfile, 1); } /* For preprocessed files, if the tokens following the first filename line is of the form # "/path/name//", handle the directive so we know the original current directory. */ static void read_original_directory (cpp_reader *pfile) { const cpp_token *hash, *token; /* Lex ahead; if the first tokens are of the form # NUM, then process the directive, otherwise back up. */ hash = _cpp_lex_direct (pfile); if (hash->type != CPP_HASH) { _cpp_backup_tokens (pfile, 1); return; } token = _cpp_lex_direct (pfile); if (token->type != CPP_NUMBER) { _cpp_backup_tokens (pfile, 2); return; } token = _cpp_lex_direct (pfile); if (token->type != CPP_STRING || ! (token->val.str.len >= 5 && token->val.str.text[token->val.str.len-2] == '/' && token->val.str.text[token->val.str.len-3] == '/')) { _cpp_backup_tokens (pfile, 3); return; } if (pfile->cb.dir_change) { char *debugdir = alloca (token->val.str.len - 3); memcpy (debugdir, (const char *) token->val.str.text + 1, token->val.str.len - 4); debugdir[token->val.str.len - 4] = '\0'; pfile->cb.dir_change (pfile, debugdir); } } /* This is called at the end of preprocessing. It pops the last buffer and writes dependency output, and returns the number of errors. Maybe it should also reset state, such that you could call cpp_start_read with a new filename to restart processing. */ int cpp_finish (cpp_reader *pfile, FILE *deps_stream) { /* Warn about unused macros before popping the final buffer. */ if (CPP_OPTION (pfile, warn_unused_macros)) cpp_forall_identifiers (pfile, _cpp_warn_if_unused_macro, NULL); /* cpplex.c leaves the final buffer on the stack. This it so that it returns an unending stream of CPP_EOFs to the client. If we popped the buffer, we'd dereference a NULL buffer pointer and segfault. It's nice to allow the client to do worry-free excess cpp_get_token calls. */ while (pfile->buffer) _cpp_pop_buffer (pfile); /* Don't write the deps file if there are errors. */ if (CPP_OPTION (pfile, deps.style) != DEPS_NONE && deps_stream && pfile->errors == 0) { deps_write (pfile->deps, deps_stream, 72); if (CPP_OPTION (pfile, deps.phony_targets)) deps_phony_targets (pfile->deps, deps_stream); } /* Report on headers that could use multiple include guards. */ if (CPP_OPTION (pfile, print_include_names)) _cpp_report_missing_guards (pfile); return pfile->errors; } static void post_options (cpp_reader *pfile) { /* -Wtraditional is not useful in C++ mode. */ if (CPP_OPTION (pfile, cplusplus)) CPP_OPTION (pfile, warn_traditional) = 0; /* Permanently disable macro expansion if we are rescanning preprocessed text. Read preprocesed source in ISO mode. */ if (CPP_OPTION (pfile, preprocessed)) { pfile->state.prevent_expansion = 1; CPP_OPTION (pfile, traditional) = 0; } if (CPP_OPTION (pfile, warn_trigraphs) == 2) CPP_OPTION (pfile, warn_trigraphs) = !CPP_OPTION (pfile, trigraphs); if (CPP_OPTION (pfile, traditional)) { CPP_OPTION (pfile, cplusplus_comments) = 0; /* Traditional CPP does not accurately track column information. */ CPP_OPTION (pfile, show_column) = 0; CPP_OPTION (pfile, trigraphs) = 0; CPP_OPTION (pfile, warn_trigraphs) = 0; } } diff --git a/contrib/gcc/f/g77spec.c b/contrib/gcc/f/g77spec.c index 10b7d1f4358f..449f2993a45e 100644 --- a/contrib/gcc/f/g77spec.c +++ b/contrib/gcc/f/g77spec.c @@ -1,568 +1,568 @@ /* Specific flags and argument handling of the Fortran front-end. - Copyright (C) 1997, 1999, 2000, 2001, 2002, 2003, 2004 + Copyright (C) 1997, 1999, 2000, 2001, 2002, 2003, 2004, 2006 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* $FreeBSD$ */ /* This file contains a filter for the main `gcc' driver, which is replicated for the `g77' driver by adding this filter. The purpose of this filter is to be basically identical to gcc (in that it faithfully passes all of the original arguments to gcc) but, unless explicitly overridden by the user in certain ways, ensure that the needs of the language supported by this wrapper are met. For GNU Fortran (g77), we do the following to the argument list before passing it to `gcc': 1. Make sure `-lg2c -lm' is at the end of the list. 2. Make sure each time `-lg2c' or `-lm' is seen, it forms part of the series `-lg2c -lm'. #1 and #2 are not done if `-nostdlib' or any option that disables the linking phase is present, or if `-xfoo' is in effect. Note that a lack of source files or -l options disables linking. This program was originally made out of gcc/cp/g++spec.c, but the way it builds the new argument list was rewritten so it is much easier to maintain, improve the way it decides to add or not add extra arguments, etc. And several improvements were made in the handling of arguments, primarily to make it more consistent with `gcc' itself. */ #include "config.h" #include "system.h" #include "coretypes.h" #include "tm.h" #include "gcc.h" #include "intl.h" #ifndef MATH_LIBRARY #define MATH_LIBRARY "-lm" #endif #ifndef MATH_LIBRARY_PROFILE #define MATH_LIBRARY_PROFILE "-lm" #endif #ifndef FORTRAN_INIT #define FORTRAN_INIT "-lfrtbegin" #endif #ifndef FORTRAN_INIT_PROFILE #define FORTRAN_INIT_PROFILE "-lfrtbegin" #endif #ifndef FORTRAN_LIBRARY #define FORTRAN_LIBRARY "-lg2c" #endif #ifndef FORTRAN_LIBRARY_PROFILE #define FORTRAN_LIBRARY_PROFILE "-lg2c" #endif /* Options this driver needs to recognize, not just know how to skip over. */ typedef enum { OPTION_b, /* Aka --prefix. */ OPTION_B, /* Aka --target. */ OPTION_c, /* Aka --compile. */ OPTION_driver, /* Wrapper-specific option. */ OPTION_E, /* Aka --preprocess. */ OPTION_help, /* --help. */ OPTION_i, /* -imacros, -include, -include-*. */ OPTION_l, OPTION_L, /* Aka --library-directory. */ OPTION_M, /* Aka --dependencies. */ OPTION_MM, /* Aka --user-dependencies. */ OPTION_nostdlib, /* Aka --no-standard-libraries, or -nodefaultlibs. */ OPTION_o, /* Aka --output. */ OPTION_p, /* Aka --profile. */ OPTION_S, /* Aka --assemble. */ OPTION_syntax_only, /* -fsyntax-only. */ OPTION_v, /* Aka --verbose. */ OPTION_version, /* --version. */ OPTION_V, /* Aka --use-version. */ OPTION_x, /* Aka --language. */ OPTION_ /* Unrecognized or unimportant. */ } Option; /* The original argument list and related info is copied here. */ static int g77_xargc; static const char *const *g77_xargv; static void lookup_option (Option *, int *, const char **, const char *); static void append_arg (const char *); /* The new argument list will be built here. */ static int g77_newargc; static const char **g77_newargv; #ifndef SWITCH_TAKES_ARG #define SWITCH_TAKES_ARG(CHAR) DEFAULT_SWITCH_TAKES_ARG(CHAR) #endif #ifndef WORD_SWITCH_TAKES_ARG #define WORD_SWITCH_TAKES_ARG(STR) DEFAULT_WORD_SWITCH_TAKES_ARG (STR) #endif /* Assumes text[0] == '-'. Returns number of argv items that belong to (and follow) this one, an option id for options important to the caller, and a pointer to the first char of the arg, if embedded (else returns NULL, meaning no arg or it's the next argv). Note that this also assumes gcc.c's pass converting long options to short ones, where available, has already been run. */ static void lookup_option (Option *xopt, int *xskip, const char **xarg, const char *text) { Option opt = OPTION_; int skip; const char *arg = NULL; if ((skip = SWITCH_TAKES_ARG (text[1]))) skip -= (text[2] != '\0'); /* See gcc.c. */ if (text[1] == 'B') opt = OPTION_B, skip = (text[2] == '\0'), arg = text + 2; else if (text[1] == 'b') opt = OPTION_b, skip = (text[2] == '\0'), arg = text + 2; else if ((text[1] == 'c') && (text[2] == '\0')) opt = OPTION_c, skip = 0; else if ((text[1] == 'E') && (text[2] == '\0')) opt = OPTION_E, skip = 0; else if (text[1] == 'i') opt = OPTION_i, skip = 0; else if (text[1] == 'l') opt = OPTION_l; else if (text[1] == 'L') opt = OPTION_L, arg = text + 2; else if (text[1] == 'o') opt = OPTION_o; else if ((text[1] == 'p') && (text[2] == '\0') || (text[1] == 'p') && (text[2] == 'g') && (text[3] == '\0')) opt = OPTION_p; else if ((text[1] == 'S') && (text[2] == '\0')) opt = OPTION_S, skip = 0; else if (text[1] == 'V') opt = OPTION_V, skip = (text[2] == '\0'); else if ((text[1] == 'v') && (text[2] == '\0')) opt = OPTION_v, skip = 0; else if (text[1] == 'x') opt = OPTION_x, arg = text + 2; else { if ((skip = WORD_SWITCH_TAKES_ARG (text + 1)) != 0) /* See gcc.c. */ ; else if (! strncmp (text, "-fdriver", 8)) /* Really --driver!! */ opt = OPTION_driver; /* Never mind arg, this is unsupported. */ else if (! strcmp (text, "-fhelp")) /* Really --help!! */ opt = OPTION_help; else if (! strcmp (text, "-M")) opt = OPTION_M; else if (! strcmp (text, "-MM")) opt = OPTION_MM; else if (! strcmp (text, "-nostdlib") || ! strcmp (text, "-nodefaultlibs")) opt = OPTION_nostdlib; else if (! strcmp (text, "-fsyntax-only")) opt = OPTION_syntax_only; else if (! strcmp (text, "-dumpversion")) opt = OPTION_version; else if (! strcmp (text, "-fversion")) /* Really --version!! */ opt = OPTION_version; else if (! strcmp (text, "-Xlinker") || ! strcmp (text, "-specs")) skip = 1; else skip = 0; } if (xopt != NULL) *xopt = opt; if (xskip != NULL) *xskip = skip; if (xarg != NULL) { if ((arg != NULL) && (arg[0] == '\0')) *xarg = NULL; else *xarg = arg; } } /* Append another argument to the list being built. As long as it is identical to the corresponding arg in the original list, just increment the new arg count. Otherwise allocate a new list, etc. */ static void append_arg (const char *arg) { static int newargsize; #if 0 fprintf (stderr, "`%s'\n", arg); #endif if (g77_newargv == g77_xargv && g77_newargc < g77_xargc && (arg == g77_xargv[g77_newargc] || ! strcmp (arg, g77_xargv[g77_newargc]))) { ++g77_newargc; return; /* Nothing new here. */ } if (g77_newargv == g77_xargv) { /* Make new arglist. */ int i; newargsize = (g77_xargc << 2) + 20; /* This should handle all. */ g77_newargv = xmalloc (newargsize * sizeof (char *)); /* Copy what has been done so far. */ for (i = 0; i < g77_newargc; ++i) g77_newargv[i] = g77_xargv[i]; } if (g77_newargc == newargsize) fatal ("overflowed output arg list for `%s'", arg); g77_newargv[g77_newargc++] = arg; } void lang_specific_driver (int *in_argc, const char *const **in_argv, int *in_added_libraries ATTRIBUTE_UNUSED) { int argc = *in_argc; const char *const *argv = *in_argv; int i; int verbose = 0; Option opt; int skip; const char *arg; /* This will be NULL if we encounter a situation where we should not link in libf2c. */ const char *library = FORTRAN_LIBRARY; /* 0 => -xnone in effect. 1 => -xfoo in effect. */ int saw_speclang = 0; /* 0 => initial/reset state 1 => last arg was -l 2 => last two args were -l -lm. */ int saw_library = 0; /* 0 => initial/reset state 1 => FORTRAN_INIT linked in */ int use_init = 0; /* By default, we throw on the math library if we have one. */ int need_math = (MATH_LIBRARY[0] != '\0'); /* If non-zero, the user gave us the `-p' or `-pg' flag. */ int saw_profile_flag = 0; /* The number of input and output files in the incoming arg list. */ int n_infiles = 0; int n_outfiles = 0; #if 0 fprintf (stderr, "Incoming:"); for (i = 0; i < argc; i++) fprintf (stderr, " %s", argv[i]); fprintf (stderr, "\n"); #endif g77_xargc = argc; g77_xargv = argv; g77_newargc = 0; g77_newargv = (const char **) argv; /* First pass through arglist. If -nostdlib or a "turn-off-linking" option is anywhere in the command line, don't do any library-option processing (except relating to -x). Also, if -v is specified, but no other options that do anything special (allowing -V version, etc.), remember to add special stuff to make gcc command actually invoke all the different phases of the compilation process so all the version numbers can be seen. Also, here is where all problems with missing arguments to options are caught. If this loop is exited normally, it means all options have the appropriate number of arguments as far as the rest of this program is concerned. */ for (i = 1; i < argc; ++i) { if ((argv[i][0] == '+') && (argv[i][1] == 'e')) { continue; } if ((argv[i][0] != '-') || (argv[i][1] == '\0')) { ++n_infiles; continue; } lookup_option (&opt, &skip, NULL, argv[i]); switch (opt) { case OPTION_nostdlib: case OPTION_c: case OPTION_S: case OPTION_syntax_only: case OPTION_E: case OPTION_M: case OPTION_MM: /* These options disable linking entirely or linking of the standard libraries. */ library = 0; break; case OPTION_l: ++n_infiles; break; case OPTION_o: ++n_outfiles; break; case OPTION_p: saw_profile_flag = 1; library = FORTRAN_LIBRARY_PROFILE; break; case OPTION_v: verbose = 1; break; case OPTION_b: case OPTION_B: case OPTION_L: case OPTION_i: case OPTION_V: /* These options are useful in conjunction with -v to get appropriate version info. */ break; case OPTION_version: printf ("GNU Fortran (GCC) %s\n", version_string); - printf ("Copyright %s 2004 Free Software Foundation, Inc.\n", + printf ("Copyright %s 2006 Free Software Foundation, Inc.\n", _("(C)")); printf ("\n"); printf (_("\ GNU Fortran comes with NO WARRANTY, to the extent permitted by law.\n\ You may redistribute copies of GNU Fortran\n\ under the terms of the GNU General Public License.\n\ For more information about these matters, see the file named COPYING\n\ or type the command `info -f g77 Copying'.\n\ ")); exit (0); break; case OPTION_help: /* Let gcc.c handle this, as it has a really cool facility for handling --help and --verbose --help. */ return; case OPTION_driver: fatal ("--driver no longer supported"); break; default: break; } /* This is the one place we check for missing arguments in the program. */ if (i + skip < argc) i += skip; else fatal ("argument to `%s' missing", argv[i]); } if ((n_outfiles != 0) && (n_infiles == 0)) fatal ("no input files; unwilling to write output files"); /* If there are no input files, no need for the library. */ if (n_infiles == 0) library = 0; /* Second pass through arglist, transforming arguments as appropriate. */ append_arg (argv[0]); /* Start with command name, of course. */ for (i = 1; i < argc; ++i) { if (argv[i][0] == '\0') { append_arg (argv[i]); /* Interesting. Just append as is. */ continue; } if ((argv[i][0] == '-') && (argv[i][1] != 'l')) { /* Not a filename or library. */ if (saw_library == 1 && need_math) /* -l. */ append_arg (saw_profile_flag ? MATH_LIBRARY_PROFILE : MATH_LIBRARY); saw_library = 0; lookup_option (&opt, &skip, &arg, argv[i]); if (argv[i][1] == '\0') { append_arg (argv[i]); /* "-" == Standard input. */ continue; } if (opt == OPTION_x) { /* Track input language. */ const char *lang; if (arg == NULL) lang = argv[i+1]; else lang = arg; saw_speclang = (strcmp (lang, "none") != 0); } append_arg (argv[i]); for (; skip != 0; --skip) append_arg (argv[++i]); continue; } /* A filename/library, not an option. */ if (saw_speclang) saw_library = 0; /* -xfoo currently active. */ else { /* -lfoo or filename. */ if (strcmp (argv[i], MATH_LIBRARY) == 0) { if (saw_library == 1) saw_library = 2; /* -l -lm. */ else { if (0 == use_init) { append_arg (saw_profile_flag ? FORTRAN_INIT_PROFILE : FORTRAN_INIT); use_init = 1; } append_arg (saw_profile_flag ? FORTRAN_LIBRARY_PROFILE : FORTRAN_LIBRARY); } } else if (strcmp (argv[i], FORTRAN_LIBRARY) == 0) saw_library = 1; /* -l. */ else { /* Other library, or filename. */ if (saw_library == 1 && need_math) append_arg (saw_profile_flag ? MATH_LIBRARY_PROFILE : MATH_LIBRARY); saw_library = 0; } } append_arg (argv[i]); } /* Append `-lg2c -lm' as necessary. */ if (library) { /* Doing a link and no -nostdlib. */ if (saw_speclang) append_arg ("-xnone"); switch (saw_library) { case 0: if (0 == use_init) { append_arg (saw_profile_flag ? FORTRAN_INIT_PROFILE : FORTRAN_INIT); use_init = 1; } append_arg (library); case 1: if (need_math) append_arg (saw_profile_flag ? MATH_LIBRARY_PROFILE : MATH_LIBRARY); default: break; } } #ifdef ENABLE_SHARED_LIBGCC if (library) { int i; for (i = 1; i < g77_newargc; i++) if (g77_newargv[i][0] == '-') if (strcmp (g77_newargv[i], "-static-libgcc") == 0 || strcmp (g77_newargv[i], "-static") == 0) break; if (i == g77_newargc) append_arg ("-shared-libgcc"); } #endif if (verbose && g77_newargv != g77_xargv) { fprintf (stderr, "Driving:"); for (i = 0; i < g77_newargc; i++) fprintf (stderr, " %s", g77_newargv[i]); fprintf (stderr, "\n"); } *in_argc = g77_newargc; *in_argv = g77_newargv; } /* Called before linking. Returns 0 on success and -1 on failure. */ int lang_specific_pre_link (void) /* Not used for F77. */ { return 0; } /* Number of extra output files that lang_specific_pre_link may generate. */ int lang_specific_extra_outfiles = 0; /* Not used for F77. */ /* Table of language-specific spec functions. */ const struct spec_function lang_specific_spec_functions[] = { { 0, 0 } }; diff --git a/contrib/gcc/gcc.c b/contrib/gcc/gcc.c index bc83fa9ef0c6..2800d915ca7f 100644 --- a/contrib/gcc/gcc.c +++ b/contrib/gcc/gcc.c @@ -1,7473 +1,7475 @@ /* Compiler driver program that can handle many languages. Copyright (C) 1987, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, + Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. This paragraph is here to try to keep Sun CC from dying. The number of chars here seems crucial!!!! */ /* $FreeBSD$ */ /* This program is the user interface to the C compiler and possibly to other compilers. It is used because compilation is a complicated procedure which involves running several programs and passing temporary files between them, forwarding the users switches to those programs selectively, and deleting the temporary files at the end. CC recognizes how to compile each input file by suffixes in the file names. Once it knows which kind of compilation to perform, the procedure for compilation is specified by a string called a "spec". */ /* A Short Introduction to Adding a Command-Line Option. Before adding a command-line option, consider if it is really necessary. Each additional command-line option adds complexity and is difficult to remove in subsequent versions. In the following, consider adding the command-line argument `--bar'. 1. Each command-line option is specified in the specs file. The notation is described below in the comment entitled "The Specs Language". Read it. 2. In this file, add an entry to "option_map" equating the long `--' argument version and any shorter, single letter version. Read the comments in the declaration of "struct option_map" for an explanation. Do not omit the first `-'. 3. Look in the "specs" file to determine which program or option list should be given the argument, e.g., "cc1_options". Add the appropriate syntax for the shorter option version to the corresponding "const char *" entry in this file. Omit the first `-' from the option. For example, use `-bar', rather than `--bar'. 4. If the argument takes an argument, e.g., `--baz argument1', modify either DEFAULT_SWITCH_TAKES_ARG or DEFAULT_WORD_SWITCH_TAKES_ARG in this file. Omit the first `-' from `--baz'. 5. Document the option in this file's display_help(). If the option is passed to a subprogram, modify its corresponding function, e.g., cppinit.c:print_help() or toplev.c:display_help(), instead. 6. Compile and test. Make sure that your new specs file is being read. For example, use a debugger to investigate the value of "specs_file" in main(). */ #include "config.h" #include "system.h" #include "coretypes.h" #include "multilib.h" /* before tm.h */ #include "tm.h" #include #if ! defined( SIGCHLD ) && defined( SIGCLD ) # define SIGCHLD SIGCLD #endif #include "obstack.h" #include "intl.h" #include "prefix.h" #include "gcc.h" #include "flags.h" #ifdef HAVE_SYS_RESOURCE_H #include #endif #if defined (HAVE_DECL_GETRUSAGE) && !HAVE_DECL_GETRUSAGE extern int getrusage (int, struct rusage *); #endif /* By default there is no special suffix for target executables. */ /* FIXME: when autoconf is fixed, remove the host check - dj */ #if defined(TARGET_EXECUTABLE_SUFFIX) && defined(HOST_EXECUTABLE_SUFFIX) #define HAVE_TARGET_EXECUTABLE_SUFFIX #endif /* By default there is no special suffix for host executables. */ #ifdef HOST_EXECUTABLE_SUFFIX #define HAVE_HOST_EXECUTABLE_SUFFIX #else #define HOST_EXECUTABLE_SUFFIX "" #endif /* By default, the suffix for target object files is ".o". */ #ifdef TARGET_OBJECT_SUFFIX #define HAVE_TARGET_OBJECT_SUFFIX #else #define TARGET_OBJECT_SUFFIX ".o" #endif static const char dir_separator_str[] = { DIR_SEPARATOR, 0 }; /* Most every one is fine with LIBRARY_PATH. For some, it conflicts. */ #ifndef LIBRARY_PATH_ENV #define LIBRARY_PATH_ENV "LIBRARY_PATH" #endif #ifndef HAVE_KILL #define kill(p,s) raise(s) #endif /* If a stage of compilation returns an exit status >= 1, compilation of that file ceases. */ #define MIN_FATAL_STATUS 1 /* Flag set by cppspec.c to 1. */ int is_cpp_driver; /* Flag saying to pass the greatest exit code returned by a sub-process to the calling program. */ static int pass_exit_codes; /* Definition of string containing the arguments given to configure. */ #include "configargs.h" /* Flag saying to print the directories gcc will search through looking for programs, libraries, etc. */ static int print_search_dirs; /* Flag saying to print the full filename of this file as found through our usual search mechanism. */ static const char *print_file_name = NULL; /* As print_file_name, but search for executable file. */ static const char *print_prog_name = NULL; /* Flag saying to print the relative path we'd use to find libgcc.a given the current compiler flags. */ static int print_multi_directory; /* Flag saying to print the relative path we'd use to find OS libraries given the current compiler flags. */ static int print_multi_os_directory; /* Flag saying to print the list of subdirectories and compiler flags used to select them in a standard form. */ static int print_multi_lib; /* Flag saying to print the command line options understood by gcc and its sub-processes. */ static int print_help_list; /* Flag indicating whether we should print the command and arguments */ static int verbose_flag; /* Flag indicating whether we should ONLY print the command and arguments (like verbose_flag) without executing the command. Displayed arguments are quoted so that the generated command line is suitable for execution. This is intended for use in shell scripts to capture the driver-generated command line. */ static int verbose_only_flag; /* Flag indicating to print target specific command line options. */ static int target_help_flag; /* Flag indicating whether we should report subprocess execution times (if this is supported by the system - see pexecute.c). */ static int report_times; /* Nonzero means place this string before uses of /, so that include and library files can be found in an alternate location. */ #ifdef TARGET_SYSTEM_ROOT static const char *target_system_root = TARGET_SYSTEM_ROOT; #else static const char *target_system_root = 0; #endif /* Nonzero means pass the updated target_system_root to the compiler. */ static int target_system_root_changed; /* Nonzero means append this string to target_system_root. */ static const char *target_sysroot_suffix = 0; /* Nonzero means append this string to target_system_root for headers. */ static const char *target_sysroot_hdrs_suffix = 0; /* Nonzero means write "temp" files in source directory and use the source file's name in them, and don't delete them. */ static int save_temps_flag; /* Nonzero means use pipes to communicate between subprocesses. Overridden by either of the above two flags. */ static int use_pipes; /* The compiler version. */ static const char *compiler_version; /* The target version specified with -V */ static const char *const spec_version = DEFAULT_TARGET_VERSION; /* The target machine specified with -b. */ static const char *spec_machine = DEFAULT_TARGET_MACHINE; /* Nonzero if cross-compiling. When -b is used, the value comes from the `specs' file. */ #ifdef CROSS_COMPILE static const char *cross_compile = "1"; #else static const char *cross_compile = "0"; #endif #ifdef MODIFY_TARGET_NAME /* Information on how to alter the target name based on a command-line switch. The only case we support now is simply appending or deleting a string to or from the end of the first part of the configuration name. */ static const struct modify_target { const char *const sw; const enum add_del {ADD, DELETE} add_del; const char *const str; } modify_target[] = MODIFY_TARGET_NAME; #endif /* The number of errors that have occurred; the link phase will not be run if this is nonzero. */ static int error_count = 0; /* Greatest exit code of sub-processes that has been encountered up to now. */ static int greatest_status = 1; /* This is the obstack which we use to allocate many strings. */ static struct obstack obstack; /* This is the obstack to build an environment variable to pass to collect2 that describes all of the relevant switches of what to pass the compiler in building the list of pointers to constructors and destructors. */ static struct obstack collect_obstack; /* These structs are used to collect resource usage information for subprocesses. */ #ifdef HAVE_GETRUSAGE static struct rusage rus, prus; #endif /* Forward declaration for prototypes. */ struct path_prefix; static void init_spec (void); static void store_arg (const char *, int, int); static char *load_specs (const char *); static void read_specs (const char *, int); static void set_spec (const char *, const char *); static struct compiler *lookup_compiler (const char *, size_t, const char *); static char *build_search_list (struct path_prefix *, const char *, int); static void putenv_from_prefixes (struct path_prefix *, const char *); static int access_check (const char *, int); static char *find_a_file (struct path_prefix *, const char *, int, int); static void add_prefix (struct path_prefix *, const char *, const char *, int, int, int *, int); static void add_sysrooted_prefix (struct path_prefix *, const char *, const char *, int, int, int *, int); static void translate_options (int *, const char *const **); static char *skip_whitespace (char *); static void delete_if_ordinary (const char *); static void delete_temp_files (void); static void delete_failure_queue (void); static void clear_failure_queue (void); static int check_live_switch (int, int); static const char *handle_braces (const char *); static inline bool input_suffix_matches (const char *, const char *); static inline bool switch_matches (const char *, const char *, int); static inline void mark_matching_switches (const char *, const char *, int); static inline void process_marked_switches (void); static const char *process_brace_body (const char *, const char *, const char *, int, int); static const struct spec_function *lookup_spec_function (const char *); static const char *eval_spec_function (const char *, const char *); static const char *handle_spec_function (const char *); static char *save_string (const char *, int); static void set_collect_gcc_options (void); static int do_spec_1 (const char *, int, const char *); static int do_spec_2 (const char *); static void do_option_spec (const char *, const char *); static void do_self_spec (const char *); static const char *find_file (const char *); static int is_directory (const char *, const char *, int); static const char *validate_switches (const char *); static void validate_all_switches (void); static inline void validate_switches_from_spec (const char *); static void give_switch (int, int); static int used_arg (const char *, int); static int default_arg (const char *, int); static void set_multilib_dir (void); static void print_multilib_info (void); static void perror_with_name (const char *); static void pfatal_pexecute (const char *, const char *) ATTRIBUTE_NORETURN; static void notice (const char *, ...) ATTRIBUTE_PRINTF_1; static void display_help (void); static void add_preprocessor_option (const char *, int); static void add_assembler_option (const char *, int); static void add_linker_option (const char *, int); static void process_command (int, const char **); static int execute (void); static void alloc_args (void); static void clear_args (void); static void fatal_error (int); #ifdef ENABLE_SHARED_LIBGCC static void init_gcc_specs (struct obstack *, const char *, const char *, const char *); #endif #if defined(HAVE_TARGET_OBJECT_SUFFIX) || defined(HAVE_TARGET_EXECUTABLE_SUFFIX) static const char *convert_filename (const char *, int, int); #endif static const char *if_exists_spec_function (int, const char **); static const char *if_exists_else_spec_function (int, const char **); /* The Specs Language Specs are strings containing lines, each of which (if not blank) is made up of a program name, and arguments separated by spaces. The program name must be exact and start from root, since no path is searched and it is unreliable to depend on the current working directory. Redirection of input or output is not supported; the subprograms must accept filenames saying what files to read and write. In addition, the specs can contain %-sequences to substitute variable text or for conditional text. Here is a table of all defined %-sequences. Note that spaces are not generated automatically around the results of expanding these sequences; therefore, you can concatenate them together or with constant text in a single argument. %% substitute one % into the program name or argument. %i substitute the name of the input file being processed. %b substitute the basename of the input file being processed. This is the substring up to (and not including) the last period and not including the directory. %B same as %b, but include the file suffix (text after the last period). %gSUFFIX substitute a file name that has suffix SUFFIX and is chosen once per compilation, and mark the argument a la %d. To reduce exposure to denial-of-service attacks, the file name is now chosen in a way that is hard to predict even when previously chosen file names are known. For example, `%g.s ... %g.o ... %g.s' might turn into `ccUVUUAU.s ccXYAXZ12.o ccUVUUAU.s'. SUFFIX matches the regexp "[.A-Za-z]*%O"; "%O" is treated exactly as if it had been pre-processed. Previously, %g was simply substituted with a file name chosen once per compilation, without regard to any appended suffix (which was therefore treated just like ordinary text), making such attacks more likely to succeed. %|SUFFIX like %g, but if -pipe is in effect, expands simply to "-". %mSUFFIX like %g, but if -pipe is in effect, expands to nothing. (We have both %| and %m to accommodate differences between system assemblers; see the AS_NEEDS_DASH_FOR_PIPED_INPUT target macro.) %uSUFFIX like %g, but generates a new temporary file name even if %uSUFFIX was already seen. %USUFFIX substitutes the last file name generated with %uSUFFIX, generating a new one if there is no such last file name. In the absence of any %uSUFFIX, this is just like %gSUFFIX, except they don't share the same suffix "space", so `%g.s ... %U.s ... %g.s ... %U.s' would involve the generation of two distinct file names, one for each `%g.s' and another for each `%U.s'. Previously, %U was simply substituted with a file name chosen for the previous %u, without regard to any appended suffix. %jSUFFIX substitutes the name of the HOST_BIT_BUCKET, if any, and if it is writable, and if save-temps is off; otherwise, substitute the name of a temporary file, just like %u. This temporary file is not meant for communication between processes, but rather as a junk disposal mechanism. %.SUFFIX substitutes .SUFFIX for the suffixes of a matched switch's args when it is subsequently output with %*. SUFFIX is terminated by the next space or %. %d marks the argument containing or following the %d as a temporary file name, so that that file will be deleted if CC exits successfully. Unlike %g, this contributes no text to the argument. %w marks the argument containing or following the %w as the "output file" of this compilation. This puts the argument into the sequence of arguments that %o will substitute later. %V indicates that this compilation produces no "output file". %W{...} like %{...} but mark last argument supplied within as a file to be deleted on failure. %o substitutes the names of all the output files, with spaces automatically placed around them. You should write spaces around the %o as well or the results are undefined. %o is for use in the specs for running the linker. Input files whose names have no recognized suffix are not compiled at all, but they are included among the output files, so they will be linked. %O substitutes the suffix for object files. Note that this is handled specially when it immediately follows %g, %u, or %U (with or without a suffix argument) because of the need for those to form complete file names. The handling is such that %O is treated exactly as if it had already been substituted, except that %g, %u, and %U do not currently support additional SUFFIX characters following %O as they would following, for example, `.o'. %I Substitute any of -iprefix (made from GCC_EXEC_PREFIX), -isysroot (made from TARGET_SYSTEM_ROOT), and -isystem (made from COMPILER_PATH and -B options) as necessary. %s current argument is the name of a library or startup file of some sort. Search for that file in a standard list of directories and substitute the full name found. %eSTR Print STR as an error message. STR is terminated by a newline. Use this when inconsistent options are detected. %nSTR Print STR as a notice. STR is terminated by a newline. %x{OPTION} Accumulate an option for %X. %X Output the accumulated linker options specified by compilations. %Y Output the accumulated assembler options specified by compilations. %Z Output the accumulated preprocessor options specified by compilations. %a process ASM_SPEC as a spec. This allows config.h to specify part of the spec for running as. %A process ASM_FINAL_SPEC as a spec. A capital A is actually used here. This can be used to run a post-processor after the assembler has done its job. %D Dump out a -L option for each directory in startfile_prefixes. If multilib_dir is set, extra entries are generated with it affixed. %l process LINK_SPEC as a spec. %L process LIB_SPEC as a spec. %G process LIBGCC_SPEC as a spec. %M output multilib_dir with directory separators replaced with "_"; if multilib_dir is not set or is ".", output "". %S process STARTFILE_SPEC as a spec. A capital S is actually used here. %E process ENDFILE_SPEC as a spec. A capital E is actually used here. %C process CPP_SPEC as a spec. %1 process CC1_SPEC as a spec. %2 process CC1PLUS_SPEC as a spec. %* substitute the variable part of a matched option. (See below.) Note that each comma in the substituted string is replaced by a single space. % argument required. o => argument optional. j => join argument to equivalent, making one word. * => require other text after NAME as an argument. */ const char *const arg_info; }; /* This is the table of mappings. Mappings are tried sequentially for each option encountered; the first one that matches, wins. */ static const struct option_map option_map[] = { {"--all-warnings", "-Wall", 0}, {"--ansi", "-ansi", 0}, {"--assemble", "-S", 0}, {"--assert", "-A", "a"}, {"--classpath", "-fclasspath=", "aj"}, {"--bootclasspath", "-fbootclasspath=", "aj"}, {"--CLASSPATH", "-fclasspath=", "aj"}, {"--comments", "-C", 0}, {"--comments-in-macros", "-CC", 0}, {"--compile", "-c", 0}, {"--debug", "-g", "oj"}, {"--define-macro", "-D", "aj"}, {"--dependencies", "-M", 0}, {"--dump", "-d", "a"}, {"--dumpbase", "-dumpbase", "a"}, {"--entry", "-e", 0}, {"--extra-warnings", "-W", 0}, {"--for-assembler", "-Wa", "a"}, {"--for-linker", "-Xlinker", "a"}, {"--force-link", "-u", "a"}, {"--imacros", "-imacros", "a"}, {"--include", "-include", "a"}, {"--include-barrier", "-I-", 0}, {"--include-directory", "-I", "aj"}, {"--include-directory-after", "-idirafter", "a"}, {"--include-prefix", "-iprefix", "a"}, {"--include-with-prefix", "-iwithprefix", "a"}, {"--include-with-prefix-before", "-iwithprefixbefore", "a"}, {"--include-with-prefix-after", "-iwithprefix", "a"}, {"--language", "-x", "a"}, {"--library-directory", "-L", "a"}, {"--machine", "-m", "aj"}, {"--machine-", "-m", "*j"}, {"--no-integrated-cpp", "-no-integrated-cpp", 0}, {"--no-line-commands", "-P", 0}, {"--no-precompiled-includes", "-noprecomp", 0}, {"--no-standard-includes", "-nostdinc", 0}, {"--no-standard-libraries", "-nostdlib", 0}, {"--no-warnings", "-w", 0}, {"--optimize", "-O", "oj"}, {"--output", "-o", "a"}, {"--output-class-directory", "-foutput-class-dir=", "ja"}, {"--param", "--param", "a"}, {"--pedantic", "-pedantic", 0}, {"--pedantic-errors", "-pedantic-errors", 0}, {"--pie", "-pie", 0}, {"--pipe", "-pipe", 0}, {"--prefix", "-B", "a"}, {"--preprocess", "-E", 0}, {"--print-search-dirs", "-print-search-dirs", 0}, {"--print-file-name", "-print-file-name=", "aj"}, {"--print-libgcc-file-name", "-print-libgcc-file-name", 0}, {"--print-missing-file-dependencies", "-MG", 0}, {"--print-multi-lib", "-print-multi-lib", 0}, {"--print-multi-directory", "-print-multi-directory", 0}, {"--print-multi-os-directory", "-print-multi-os-directory", 0}, {"--print-prog-name", "-print-prog-name=", "aj"}, {"--profile", "-p", 0}, {"--profile-blocks", "-a", 0}, {"--quiet", "-q", 0}, {"--resource", "-fcompile-resource=", "aj"}, {"--save-temps", "-save-temps", 0}, {"--shared", "-shared", 0}, {"--silent", "-q", 0}, {"--specs", "-specs=", "aj"}, {"--static", "-static", 0}, {"--std", "-std=", "aj"}, {"--symbolic", "-symbolic", 0}, {"--time", "-time", 0}, {"--trace-includes", "-H", 0}, {"--traditional", "-traditional", 0}, {"--traditional-cpp", "-traditional-cpp", 0}, {"--trigraphs", "-trigraphs", 0}, {"--undefine-macro", "-U", "aj"}, {"--user-dependencies", "-MM", 0}, {"--verbose", "-v", 0}, {"--warn-", "-W", "*j"}, {"--write-dependencies", "-MD", 0}, {"--write-user-dependencies", "-MMD", 0}, {"--", "-f", "*j"} }; #ifdef TARGET_OPTION_TRANSLATE_TABLE static const struct { const char *const option_found; const char *const replacements; } target_option_translations[] = { TARGET_OPTION_TRANSLATE_TABLE, { 0, 0 } }; #endif /* Translate the options described by *ARGCP and *ARGVP. Make a new vector and store it back in *ARGVP, and store its length in *ARGVC. */ static void translate_options (int *argcp, const char *const **argvp) { int i; int argc = *argcp; const char *const *argv = *argvp; int newvsize = (argc + 2) * 2 * sizeof (const char *); const char **newv = xmalloc (newvsize); int newindex = 0; i = 0; newv[newindex++] = argv[i++]; while (i < argc) { #ifdef TARGET_OPTION_TRANSLATE_TABLE int tott_idx; for (tott_idx = 0; target_option_translations[tott_idx].option_found; tott_idx++) { if (strcmp (target_option_translations[tott_idx].option_found, argv[i]) == 0) { int spaces = 1; const char *sp; char *np; for (sp = target_option_translations[tott_idx].replacements; *sp; sp++) { if (*sp == ' ') spaces ++; } newvsize += spaces * sizeof (const char *); newv = xrealloc (newv, newvsize); sp = target_option_translations[tott_idx].replacements; np = xstrdup (sp); while (1) { while (*np == ' ') np++; if (*np == 0) break; newv[newindex++] = np; while (*np != ' ' && *np) np++; if (*np == 0) break; *np++ = 0; } i ++; break; } } if (target_option_translations[tott_idx].option_found) continue; #endif /* Translate -- options. */ if (argv[i][0] == '-' && argv[i][1] == '-') { size_t j; /* Find a mapping that applies to this option. */ for (j = 0; j < ARRAY_SIZE (option_map); j++) { size_t optlen = strlen (option_map[j].name); size_t arglen = strlen (argv[i]); size_t complen = arglen > optlen ? optlen : arglen; const char *arginfo = option_map[j].arg_info; if (arginfo == 0) arginfo = ""; if (!strncmp (argv[i], option_map[j].name, complen)) { const char *arg = 0; if (arglen < optlen) { size_t k; for (k = j + 1; k < ARRAY_SIZE (option_map); k++) if (strlen (option_map[k].name) >= arglen && !strncmp (argv[i], option_map[k].name, arglen)) { error ("ambiguous abbreviation %s", argv[i]); break; } if (k != ARRAY_SIZE (option_map)) break; } if (arglen > optlen) { /* If the option has an argument, accept that. */ if (argv[i][optlen] == '=') arg = argv[i] + optlen + 1; /* If this mapping requires extra text at end of name, accept that as "argument". */ else if (strchr (arginfo, '*') != 0) arg = argv[i] + optlen; /* Otherwise, extra text at end means mismatch. Try other mappings. */ else continue; } else if (strchr (arginfo, '*') != 0) { error ("incomplete `%s' option", option_map[j].name); break; } /* Handle arguments. */ if (strchr (arginfo, 'a') != 0) { if (arg == 0) { if (i + 1 == argc) { error ("missing argument to `%s' option", option_map[j].name); break; } arg = argv[++i]; } } else if (strchr (arginfo, '*') != 0) ; else if (strchr (arginfo, 'o') == 0) { if (arg != 0) error ("extraneous argument to `%s' option", option_map[j].name); arg = 0; } /* Store the translation as one argv elt or as two. */ if (arg != 0 && strchr (arginfo, 'j') != 0) newv[newindex++] = concat (option_map[j].equivalent, arg, NULL); else if (arg != 0) { newv[newindex++] = option_map[j].equivalent; newv[newindex++] = arg; } else newv[newindex++] = option_map[j].equivalent; break; } } i++; } /* Handle old-fashioned options--just copy them through, with their arguments. */ else if (argv[i][0] == '-') { const char *p = argv[i] + 1; int c = *p; int nskip = 1; if (SWITCH_TAKES_ARG (c) > (p[1] != 0)) nskip += SWITCH_TAKES_ARG (c) - (p[1] != 0); else if (WORD_SWITCH_TAKES_ARG (p)) nskip += WORD_SWITCH_TAKES_ARG (p); else if ((c == 'B' || c == 'b' || c == 'x') && p[1] == 0) nskip += 1; else if (! strcmp (p, "Xlinker")) nskip += 1; else if (! strcmp (p, "Xpreprocessor")) nskip += 1; else if (! strcmp (p, "Xassembler")) nskip += 1; /* Watch out for an option at the end of the command line that is missing arguments, and avoid skipping past the end of the command line. */ if (nskip + i > argc) nskip = argc - i; while (nskip > 0) { newv[newindex++] = argv[i++]; nskip--; } } else /* Ordinary operands, or +e options. */ newv[newindex++] = argv[i++]; } newv[newindex] = 0; *argvp = newv; *argcp = newindex; } static char * skip_whitespace (char *p) { while (1) { /* A fully-blank line is a delimiter in the SPEC file and shouldn't be considered whitespace. */ if (p[0] == '\n' && p[1] == '\n' && p[2] == '\n') return p + 1; else if (*p == '\n' || *p == ' ' || *p == '\t') p++; else if (*p == '#') { while (*p != '\n') p++; p++; } else break; } return p; } /* Structures to keep track of prefixes to try when looking for files. */ struct prefix_list { const char *prefix; /* String to prepend to the path. */ struct prefix_list *next; /* Next in linked list. */ int require_machine_suffix; /* Don't use without machine_suffix. */ /* 2 means try both machine_suffix and just_machine_suffix. */ int *used_flag_ptr; /* 1 if a file was found with this prefix. */ int priority; /* Sort key - priority within list. */ int os_multilib; /* 1 if OS multilib scheme should be used, 0 for GCC multilib scheme. */ }; struct path_prefix { struct prefix_list *plist; /* List of prefixes to try */ int max_len; /* Max length of a prefix in PLIST */ const char *name; /* Name of this list (used in config stuff) */ }; /* List of prefixes to try when looking for executables. */ static struct path_prefix exec_prefixes = { 0, 0, "exec" }; /* List of prefixes to try when looking for startup (crt0) files. */ static struct path_prefix startfile_prefixes = { 0, 0, "startfile" }; /* List of prefixes to try when looking for include files. */ static struct path_prefix include_prefixes = { 0, 0, "include" }; /* Suffix to attach to directories searched for commands. This looks like `MACHINE/VERSION/'. */ static const char *machine_suffix = 0; /* Suffix to attach to directories searched for commands. This is just `MACHINE/'. */ static const char *just_machine_suffix = 0; /* Adjusted value of GCC_EXEC_PREFIX envvar. */ static const char *gcc_exec_prefix; /* Adjusted value of standard_libexec_prefix. */ static const char *gcc_libexec_prefix; /* Default prefixes to attach to command names. */ #ifdef CROSS_COMPILE /* Don't use these prefixes for a cross compiler. */ #undef MD_EXEC_PREFIX #undef MD_STARTFILE_PREFIX #undef MD_STARTFILE_PREFIX_1 #endif /* If no prefixes defined, use the null string, which will disable them. */ #ifndef MD_EXEC_PREFIX #define MD_EXEC_PREFIX "" #endif #ifndef MD_STARTFILE_PREFIX #define MD_STARTFILE_PREFIX "" #endif #ifndef MD_STARTFILE_PREFIX_1 #define MD_STARTFILE_PREFIX_1 "" #endif static const char *const standard_exec_prefix = STANDARD_EXEC_PREFIX; static const char *const standard_exec_prefix_1 = "/usr/libexec/gcc/"; static const char *const standard_exec_prefix_2 = "/usr/lib/gcc/"; static const char *md_exec_prefix = MD_EXEC_PREFIX; static const char *md_startfile_prefix = MD_STARTFILE_PREFIX; static const char *md_startfile_prefix_1 = MD_STARTFILE_PREFIX_1; static const char *const standard_startfile_prefix = STANDARD_STARTFILE_PREFIX; static const char *const standard_startfile_prefix_1 = "/lib/"; static const char *const standard_startfile_prefix_2 = "/usr/lib/"; static const char *const tooldir_base_prefix = TOOLDIR_BASE_PREFIX; static const char *tooldir_prefix; #ifndef FREEBSD_NATIVE static const char *const standard_bindir_prefix = STANDARD_BINDIR_PREFIX; #endif /* not FREEBSD_NATIVE */ static const char *standard_libexec_prefix = STANDARD_LIBEXEC_PREFIX; /* Subdirectory to use for locating libraries. Set by set_multilib_dir based on the compilation options. */ static const char *multilib_dir; /* Subdirectory to use for locating libraries in OS conventions. Set by set_multilib_dir based on the compilation options. */ static const char *multilib_os_dir; /* Structure to keep track of the specs that have been defined so far. These are accessed using %(specname) or %[specname] in a compiler or link spec. */ struct spec_list { /* The following 2 fields must be first */ /* to allow EXTRA_SPECS to be initialized */ const char *name; /* name of the spec. */ const char *ptr; /* available ptr if no static pointer */ /* The following fields are not initialized */ /* by EXTRA_SPECS */ const char **ptr_spec; /* pointer to the spec itself. */ struct spec_list *next; /* Next spec in linked list. */ int name_len; /* length of the name */ int alloc_p; /* whether string was allocated */ }; #define INIT_STATIC_SPEC(NAME,PTR) \ { NAME, NULL, PTR, (struct spec_list *) 0, sizeof (NAME) - 1, 0 } /* List of statically defined specs. */ static struct spec_list static_specs[] = { INIT_STATIC_SPEC ("asm", &asm_spec), INIT_STATIC_SPEC ("asm_debug", &asm_debug), INIT_STATIC_SPEC ("asm_final", &asm_final_spec), INIT_STATIC_SPEC ("asm_options", &asm_options), INIT_STATIC_SPEC ("invoke_as", &invoke_as), INIT_STATIC_SPEC ("cpp", &cpp_spec), INIT_STATIC_SPEC ("cpp_options", &cpp_options), INIT_STATIC_SPEC ("cpp_debug_options", &cpp_debug_options), INIT_STATIC_SPEC ("cpp_unique_options", &cpp_unique_options), INIT_STATIC_SPEC ("trad_capable_cpp", &trad_capable_cpp), INIT_STATIC_SPEC ("cc1", &cc1_spec), INIT_STATIC_SPEC ("cc1_options", &cc1_options), INIT_STATIC_SPEC ("cc1plus", &cc1plus_spec), INIT_STATIC_SPEC ("link_gcc_c_sequence", &link_gcc_c_sequence_spec), INIT_STATIC_SPEC ("endfile", &endfile_spec), INIT_STATIC_SPEC ("link", &link_spec), INIT_STATIC_SPEC ("lib", &lib_spec), INIT_STATIC_SPEC ("libgcc", &libgcc_spec), INIT_STATIC_SPEC ("startfile", &startfile_spec), INIT_STATIC_SPEC ("switches_need_spaces", &switches_need_spaces), INIT_STATIC_SPEC ("cross_compile", &cross_compile), INIT_STATIC_SPEC ("version", &compiler_version), INIT_STATIC_SPEC ("multilib", &multilib_select), INIT_STATIC_SPEC ("multilib_defaults", &multilib_defaults), INIT_STATIC_SPEC ("multilib_extra", &multilib_extra), INIT_STATIC_SPEC ("multilib_matches", &multilib_matches), INIT_STATIC_SPEC ("multilib_exclusions", &multilib_exclusions), INIT_STATIC_SPEC ("multilib_options", &multilib_options), INIT_STATIC_SPEC ("linker", &linker_name_spec), INIT_STATIC_SPEC ("link_libgcc", &link_libgcc_spec), INIT_STATIC_SPEC ("md_exec_prefix", &md_exec_prefix), INIT_STATIC_SPEC ("md_startfile_prefix", &md_startfile_prefix), INIT_STATIC_SPEC ("md_startfile_prefix_1", &md_startfile_prefix_1), INIT_STATIC_SPEC ("startfile_prefix_spec", &startfile_prefix_spec), INIT_STATIC_SPEC ("sysroot_suffix_spec", &sysroot_suffix_spec), INIT_STATIC_SPEC ("sysroot_hdrs_suffix_spec", &sysroot_hdrs_suffix_spec), }; #ifdef EXTRA_SPECS /* additional specs needed */ /* Structure to keep track of just the first two args of a spec_list. That is all that the EXTRA_SPECS macro gives us. */ struct spec_list_1 { const char *const name; const char *const ptr; }; static const struct spec_list_1 extra_specs_1[] = { EXTRA_SPECS }; static struct spec_list *extra_specs = (struct spec_list *) 0; #endif /* List of dynamically allocates specs that have been defined so far. */ static struct spec_list *specs = (struct spec_list *) 0; /* List of static spec functions. */ static const struct spec_function static_spec_functions[] = { { "if-exists", if_exists_spec_function }, { "if-exists-else", if_exists_else_spec_function }, { 0, 0 } }; static int processing_spec_function; /* Add appropriate libgcc specs to OBSTACK, taking into account various permutations of -shared-libgcc, -shared, and such. */ #ifdef ENABLE_SHARED_LIBGCC static void init_gcc_specs (struct obstack *obstack, const char *shared_name, const char *static_name, const char *eh_name) { char *buf; buf = concat ("%{static|static-libgcc:", static_name, " ", eh_name, "}%{!static:%{!static-libgcc:", #ifdef HAVE_LD_AS_NEEDED "%{!shared-libgcc:", static_name, " --as-needed ", shared_name, " --no-as-needed}" "%{shared-libgcc:", shared_name, "%{!shared: ", static_name, "}", #else "%{!shared:%{!shared-libgcc:", static_name, " ", eh_name, "}%{shared-libgcc:", shared_name, " ", static_name, "}}%{shared:", #ifdef LINK_EH_SPEC "%{shared-libgcc:", shared_name, "}%{!shared-libgcc:", static_name, "}", #else shared_name, #endif #endif "}}}", NULL); obstack_grow (obstack, buf, strlen (buf)); free (buf); } #endif /* ENABLE_SHARED_LIBGCC */ /* Initialize the specs lookup routines. */ static void init_spec (void) { struct spec_list *next = (struct spec_list *) 0; struct spec_list *sl = (struct spec_list *) 0; int i; if (specs) return; /* Already initialized. */ if (verbose_flag) notice ("Using built-in specs.\n"); #ifdef EXTRA_SPECS extra_specs = xcalloc (sizeof (struct spec_list), ARRAY_SIZE (extra_specs_1)); for (i = ARRAY_SIZE (extra_specs_1) - 1; i >= 0; i--) { sl = &extra_specs[i]; sl->name = extra_specs_1[i].name; sl->ptr = extra_specs_1[i].ptr; sl->next = next; sl->name_len = strlen (sl->name); sl->ptr_spec = &sl->ptr; next = sl; } #endif /* Initialize here, not in definition. The IRIX 6 O32 cc sometimes chokes on ?: in file-scope variable initializations. */ asm_debug = ASM_DEBUG_SPEC; for (i = ARRAY_SIZE (static_specs) - 1; i >= 0; i--) { sl = &static_specs[i]; sl->next = next; next = sl; } #ifdef ENABLE_SHARED_LIBGCC /* ??? If neither -shared-libgcc nor --static-libgcc was seen, then we should be making an educated guess. Some proposed heuristics for ELF include: (1) If "-Wl,--export-dynamic", then it's a fair bet that the program will be doing dynamic loading, which will likely need the shared libgcc. (2) If "-ldl", then it's also a fair bet that we're doing dynamic loading. (3) For each ET_DYN we're linking against (either through -lfoo or /some/path/foo.so), check to see whether it or one of its dependencies depends on a shared libgcc. (4) If "-shared" If the runtime is fixed to look for program headers instead of calling __register_frame_info at all, for each object, use the shared libgcc if any EH symbol referenced. If crtstuff is fixed to not invoke __register_frame_info automatically, for each object, use the shared libgcc if any non-empty unwind section found. Doing any of this probably requires invoking an external program to do the actual object file scanning. */ { const char *p = libgcc_spec; int in_sep = 1; /* Transform the extant libgcc_spec into one that uses the shared libgcc when given the proper command line arguments. */ while (*p) { if (in_sep && *p == '-' && strncmp (p, "-lgcc", 5) == 0) { init_gcc_specs (&obstack, #ifdef NO_SHARED_LIBGCC_MULTILIB "-lgcc_s" #else "-lgcc_s%M" #endif #ifdef USE_LIBUNWIND_EXCEPTIONS " -lunwind" #endif , "-lgcc", "-lgcc_eh" #ifdef USE_LIBUNWIND_EXCEPTIONS # ifdef HAVE_LD_STATIC_DYNAMIC " %{!static:-Bstatic} -lunwind %{!static:-Bdynamic}" # else " -lunwind" # endif #endif ); p += 5; in_sep = 0; } else if (in_sep && *p == 'l' && strncmp (p, "libgcc.a%s", 10) == 0) { /* Ug. We don't know shared library extensions. Hope that systems that use this form don't do shared libraries. */ init_gcc_specs (&obstack, #ifdef NO_SHARED_LIBGCC_MULTILIB "-lgcc_s" #else "-lgcc_s%M" #endif , "libgcc.a%s", "libgcc_eh.a%s" #ifdef USE_LIBUNWIND_EXCEPTIONS " -lunwind" #endif ); p += 10; in_sep = 0; } else { obstack_1grow (&obstack, *p); in_sep = (*p == ' '); p += 1; } } obstack_1grow (&obstack, '\0'); libgcc_spec = obstack_finish (&obstack); } #endif #ifdef USE_AS_TRADITIONAL_FORMAT /* Prepend "--traditional-format" to whatever asm_spec we had before. */ { static const char tf[] = "--traditional-format "; obstack_grow (&obstack, tf, sizeof(tf) - 1); obstack_grow0 (&obstack, asm_spec, strlen (asm_spec)); asm_spec = obstack_finish (&obstack); } #endif #ifdef LINK_EH_SPEC /* Prepend LINK_EH_SPEC to whatever link_spec we had before. */ obstack_grow (&obstack, LINK_EH_SPEC, sizeof(LINK_EH_SPEC) - 1); obstack_grow0 (&obstack, link_spec, strlen (link_spec)); link_spec = obstack_finish (&obstack); #endif specs = sl; } /* Change the value of spec NAME to SPEC. If SPEC is empty, then the spec is removed; If the spec starts with a + then SPEC is added to the end of the current spec. */ static void set_spec (const char *name, const char *spec) { struct spec_list *sl; const char *old_spec; int name_len = strlen (name); int i; /* If this is the first call, initialize the statically allocated specs. */ if (!specs) { struct spec_list *next = (struct spec_list *) 0; for (i = ARRAY_SIZE (static_specs) - 1; i >= 0; i--) { sl = &static_specs[i]; sl->next = next; next = sl; } specs = sl; } /* See if the spec already exists. */ for (sl = specs; sl; sl = sl->next) if (name_len == sl->name_len && !strcmp (sl->name, name)) break; if (!sl) { /* Not found - make it. */ sl = xmalloc (sizeof (struct spec_list)); sl->name = xstrdup (name); sl->name_len = name_len; sl->ptr_spec = &sl->ptr; sl->alloc_p = 0; *(sl->ptr_spec) = ""; sl->next = specs; specs = sl; } old_spec = *(sl->ptr_spec); *(sl->ptr_spec) = ((spec[0] == '+' && ISSPACE ((unsigned char)spec[1])) ? concat (old_spec, spec + 1, NULL) : xstrdup (spec)); #ifdef DEBUG_SPECS if (verbose_flag) notice ("Setting spec %s to '%s'\n\n", name, *(sl->ptr_spec)); #endif /* Free the old spec. */ if (old_spec && sl->alloc_p) free ((void *) old_spec); sl->alloc_p = 1; } /* Accumulate a command (program name and args), and run it. */ /* Vector of pointers to arguments in the current line of specifications. */ static const char **argbuf; /* Number of elements allocated in argbuf. */ static int argbuf_length; /* Number of elements in argbuf currently in use (containing args). */ static int argbuf_index; /* This is the list of suffixes and codes (%g/%u/%U/%j) and the associated temp file. If the HOST_BIT_BUCKET is used for %j, no entry is made for it here. */ static struct temp_name { const char *suffix; /* suffix associated with the code. */ int length; /* strlen (suffix). */ int unique; /* Indicates whether %g or %u/%U was used. */ const char *filename; /* associated filename. */ int filename_length; /* strlen (filename). */ struct temp_name *next; } *temp_names; /* Number of commands executed so far. */ static int execution_count; /* Number of commands that exited with a signal. */ static int signal_count; /* Name with which this program was invoked. */ static const char *programname; /* Allocate the argument vector. */ static void alloc_args (void) { argbuf_length = 10; argbuf = xmalloc (argbuf_length * sizeof (const char *)); } /* Clear out the vector of arguments (after a command is executed). */ static void clear_args (void) { argbuf_index = 0; } /* Add one argument to the vector at the end. This is done when a space is seen or at the end of the line. If DELETE_ALWAYS is nonzero, the arg is a filename and the file should be deleted eventually. If DELETE_FAILURE is nonzero, the arg is a filename and the file should be deleted if this compilation fails. */ static void store_arg (const char *arg, int delete_always, int delete_failure) { if (argbuf_index + 1 == argbuf_length) argbuf = xrealloc (argbuf, (argbuf_length *= 2) * sizeof (const char *)); argbuf[argbuf_index++] = arg; argbuf[argbuf_index] = 0; if (delete_always || delete_failure) record_temp_file (arg, delete_always, delete_failure); } /* Load specs from a file name named FILENAME, replacing occurrences of various different types of line-endings, \r\n, \n\r and just \r, with a single \n. */ static char * load_specs (const char *filename) { int desc; int readlen; struct stat statbuf; char *buffer; char *buffer_p; char *specs; char *specs_p; if (verbose_flag) notice ("Reading specs from %s\n", filename); /* Open and stat the file. */ desc = open (filename, O_RDONLY, 0); if (desc < 0) pfatal_with_name (filename); if (stat (filename, &statbuf) < 0) pfatal_with_name (filename); /* Read contents of file into BUFFER. */ buffer = xmalloc ((unsigned) statbuf.st_size + 1); readlen = read (desc, buffer, (unsigned) statbuf.st_size); if (readlen < 0) pfatal_with_name (filename); buffer[readlen] = 0; close (desc); specs = xmalloc (readlen + 1); specs_p = specs; for (buffer_p = buffer; buffer_p && *buffer_p; buffer_p++) { int skip = 0; char c = *buffer_p; if (c == '\r') { if (buffer_p > buffer && *(buffer_p - 1) == '\n') /* \n\r */ skip = 1; else if (*(buffer_p + 1) == '\n') /* \r\n */ skip = 1; else /* \r */ c = '\n'; } if (! skip) *specs_p++ = c; } *specs_p = '\0'; free (buffer); return (specs); } /* Read compilation specs from a file named FILENAME, replacing the default ones. A suffix which starts with `*' is a definition for one of the machine-specific sub-specs. The "suffix" should be *asm, *cc1, *cpp, *link, *startfile, etc. The corresponding spec is stored in asm_spec, etc., rather than in the `compilers' vector. Anything invalid in the file is a fatal error. */ static void read_specs (const char *filename, int main_p) { char *buffer; char *p; buffer = load_specs (filename); /* Scan BUFFER for specs, putting them in the vector. */ p = buffer; while (1) { char *suffix; char *spec; char *in, *out, *p1, *p2, *p3; /* Advance P in BUFFER to the next nonblank nocomment line. */ p = skip_whitespace (p); if (*p == 0) break; /* Is this a special command that starts with '%'? */ /* Don't allow this for the main specs file, since it would encourage people to overwrite it. */ if (*p == '%' && !main_p) { p1 = p; while (*p && *p != '\n') p++; /* Skip '\n'. */ p++; if (!strncmp (p1, "%include", sizeof ("%include") - 1) && (p1[sizeof "%include" - 1] == ' ' || p1[sizeof "%include" - 1] == '\t')) { char *new_filename; p1 += sizeof ("%include"); while (*p1 == ' ' || *p1 == '\t') p1++; if (*p1++ != '<' || p[-2] != '>') fatal ("specs %%include syntax malformed after %ld characters", (long) (p1 - buffer + 1)); p[-2] = '\0'; new_filename = find_a_file (&startfile_prefixes, p1, R_OK, 0); read_specs (new_filename ? new_filename : p1, FALSE); continue; } else if (!strncmp (p1, "%include_noerr", sizeof "%include_noerr" - 1) && (p1[sizeof "%include_noerr" - 1] == ' ' || p1[sizeof "%include_noerr" - 1] == '\t')) { char *new_filename; p1 += sizeof "%include_noerr"; while (*p1 == ' ' || *p1 == '\t') p1++; if (*p1++ != '<' || p[-2] != '>') fatal ("specs %%include syntax malformed after %ld characters", (long) (p1 - buffer + 1)); p[-2] = '\0'; new_filename = find_a_file (&startfile_prefixes, p1, R_OK, 0); if (new_filename) read_specs (new_filename, FALSE); else if (verbose_flag) notice ("could not find specs file %s\n", p1); continue; } else if (!strncmp (p1, "%rename", sizeof "%rename" - 1) && (p1[sizeof "%rename" - 1] == ' ' || p1[sizeof "%rename" - 1] == '\t')) { int name_len; struct spec_list *sl; struct spec_list *newsl; /* Get original name. */ p1 += sizeof "%rename"; while (*p1 == ' ' || *p1 == '\t') p1++; if (! ISALPHA ((unsigned char) *p1)) fatal ("specs %%rename syntax malformed after %ld characters", (long) (p1 - buffer)); p2 = p1; while (*p2 && !ISSPACE ((unsigned char) *p2)) p2++; if (*p2 != ' ' && *p2 != '\t') fatal ("specs %%rename syntax malformed after %ld characters", (long) (p2 - buffer)); name_len = p2 - p1; *p2++ = '\0'; while (*p2 == ' ' || *p2 == '\t') p2++; if (! ISALPHA ((unsigned char) *p2)) fatal ("specs %%rename syntax malformed after %ld characters", (long) (p2 - buffer)); /* Get new spec name. */ p3 = p2; while (*p3 && !ISSPACE ((unsigned char) *p3)) p3++; if (p3 != p - 1) fatal ("specs %%rename syntax malformed after %ld characters", (long) (p3 - buffer)); *p3 = '\0'; for (sl = specs; sl; sl = sl->next) if (name_len == sl->name_len && !strcmp (sl->name, p1)) break; if (!sl) fatal ("specs %s spec was not found to be renamed", p1); if (strcmp (p1, p2) == 0) continue; for (newsl = specs; newsl; newsl = newsl->next) if (strcmp (newsl->name, p2) == 0) fatal ("%s: attempt to rename spec '%s' to already defined spec '%s'", filename, p1, p2); if (verbose_flag) { notice ("rename spec %s to %s\n", p1, p2); #ifdef DEBUG_SPECS notice ("spec is '%s'\n\n", *(sl->ptr_spec)); #endif } set_spec (p2, *(sl->ptr_spec)); if (sl->alloc_p) free ((void *) *(sl->ptr_spec)); *(sl->ptr_spec) = ""; sl->alloc_p = 0; continue; } else fatal ("specs unknown %% command after %ld characters", (long) (p1 - buffer)); } /* Find the colon that should end the suffix. */ p1 = p; while (*p1 && *p1 != ':' && *p1 != '\n') p1++; /* The colon shouldn't be missing. */ if (*p1 != ':') fatal ("specs file malformed after %ld characters", (long) (p1 - buffer)); /* Skip back over trailing whitespace. */ p2 = p1; while (p2 > buffer && (p2[-1] == ' ' || p2[-1] == '\t')) p2--; /* Copy the suffix to a string. */ suffix = save_string (p, p2 - p); /* Find the next line. */ p = skip_whitespace (p1 + 1); if (p[1] == 0) fatal ("specs file malformed after %ld characters", (long) (p - buffer)); p1 = p; /* Find next blank line or end of string. */ while (*p1 && !(*p1 == '\n' && (p1[1] == '\n' || p1[1] == '\0'))) p1++; /* Specs end at the blank line and do not include the newline. */ spec = save_string (p, p1 - p); p = p1; /* Delete backslash-newline sequences from the spec. */ in = spec; out = spec; while (*in != 0) { if (in[0] == '\\' && in[1] == '\n') in += 2; else if (in[0] == '#') while (*in && *in != '\n') in++; else *out++ = *in++; } *out = 0; if (suffix[0] == '*') { if (! strcmp (suffix, "*link_command")) link_command_spec = spec; else set_spec (suffix + 1, spec); } else { /* Add this pair to the vector. */ compilers = xrealloc (compilers, (n_compilers + 2) * sizeof (struct compiler)); compilers[n_compilers].suffix = suffix; compilers[n_compilers].spec = spec; n_compilers++; memset (&compilers[n_compilers], 0, sizeof compilers[n_compilers]); } if (*suffix == 0) link_command_spec = spec; } if (link_command_spec == 0) fatal ("spec file has no spec for linking"); } /* Record the names of temporary files we tell compilers to write, and delete them at the end of the run. */ /* This is the common prefix we use to make temp file names. It is chosen once for each run of this program. It is substituted into a spec by %g or %j. Thus, all temp file names contain this prefix. In practice, all temp file names start with this prefix. This prefix comes from the envvar TMPDIR if it is defined; otherwise, from the P_tmpdir macro if that is defined; otherwise, in /usr/tmp or /tmp; or finally the current directory if all else fails. */ static const char *temp_filename; /* Length of the prefix. */ static int temp_filename_length; /* Define the list of temporary files to delete. */ struct temp_file { const char *name; struct temp_file *next; }; /* Queue of files to delete on success or failure of compilation. */ static struct temp_file *always_delete_queue; /* Queue of files to delete on failure of compilation. */ static struct temp_file *failure_delete_queue; /* Record FILENAME as a file to be deleted automatically. ALWAYS_DELETE nonzero means delete it if all compilation succeeds; otherwise delete it in any case. FAIL_DELETE nonzero means delete it if a compilation step fails; otherwise delete it in any case. */ void record_temp_file (const char *filename, int always_delete, int fail_delete) { char *const name = xstrdup (filename); if (always_delete) { struct temp_file *temp; for (temp = always_delete_queue; temp; temp = temp->next) if (! strcmp (name, temp->name)) goto already1; temp = xmalloc (sizeof (struct temp_file)); temp->next = always_delete_queue; temp->name = name; always_delete_queue = temp; already1:; } if (fail_delete) { struct temp_file *temp; for (temp = failure_delete_queue; temp; temp = temp->next) if (! strcmp (name, temp->name)) goto already2; temp = xmalloc (sizeof (struct temp_file)); temp->next = failure_delete_queue; temp->name = name; failure_delete_queue = temp; already2:; } } /* Delete all the temporary files whose names we previously recorded. */ static void delete_if_ordinary (const char *name) { struct stat st; #ifdef DEBUG int i, c; printf ("Delete %s? (y or n) ", name); fflush (stdout); i = getchar (); if (i != '\n') while ((c = getchar ()) != '\n' && c != EOF) ; if (i == 'y' || i == 'Y') #endif /* DEBUG */ if (stat (name, &st) >= 0 && S_ISREG (st.st_mode)) if (unlink (name) < 0) if (verbose_flag) perror_with_name (name); } static void delete_temp_files (void) { struct temp_file *temp; for (temp = always_delete_queue; temp; temp = temp->next) delete_if_ordinary (temp->name); always_delete_queue = 0; } /* Delete all the files to be deleted on error. */ static void delete_failure_queue (void) { struct temp_file *temp; for (temp = failure_delete_queue; temp; temp = temp->next) delete_if_ordinary (temp->name); } static void clear_failure_queue (void) { failure_delete_queue = 0; } /* Build a list of search directories from PATHS. PREFIX is a string to prepend to the list. If CHECK_DIR_P is nonzero we ensure the directory exists. This is used mostly by putenv_from_prefixes so we use `collect_obstack'. It is also used by the --print-search-dirs flag. */ static char * build_search_list (struct path_prefix *paths, const char *prefix, int check_dir_p) { int suffix_len = (machine_suffix) ? strlen (machine_suffix) : 0; int just_suffix_len = (just_machine_suffix) ? strlen (just_machine_suffix) : 0; int first_time = TRUE; struct prefix_list *pprefix; obstack_grow (&collect_obstack, prefix, strlen (prefix)); obstack_1grow (&collect_obstack, '='); for (pprefix = paths->plist; pprefix != 0; pprefix = pprefix->next) { int len = strlen (pprefix->prefix); if (machine_suffix && (! check_dir_p || is_directory (pprefix->prefix, machine_suffix, 0))) { if (!first_time) obstack_1grow (&collect_obstack, PATH_SEPARATOR); first_time = FALSE; obstack_grow (&collect_obstack, pprefix->prefix, len); obstack_grow (&collect_obstack, machine_suffix, suffix_len); } if (just_machine_suffix && pprefix->require_machine_suffix == 2 && (! check_dir_p || is_directory (pprefix->prefix, just_machine_suffix, 0))) { if (! first_time) obstack_1grow (&collect_obstack, PATH_SEPARATOR); first_time = FALSE; obstack_grow (&collect_obstack, pprefix->prefix, len); obstack_grow (&collect_obstack, just_machine_suffix, just_suffix_len); } if (! pprefix->require_machine_suffix) { if (! first_time) obstack_1grow (&collect_obstack, PATH_SEPARATOR); first_time = FALSE; obstack_grow (&collect_obstack, pprefix->prefix, len); } } obstack_1grow (&collect_obstack, '\0'); return obstack_finish (&collect_obstack); } /* Rebuild the COMPILER_PATH and LIBRARY_PATH environment variables for collect. */ static void putenv_from_prefixes (struct path_prefix *paths, const char *env_var) { putenv (build_search_list (paths, env_var, 1)); } /* Check whether NAME can be accessed in MODE. This is like access, except that it never considers directories to be executable. */ static int access_check (const char *name, int mode) { if (mode == X_OK) { struct stat st; if (stat (name, &st) < 0 || S_ISDIR (st.st_mode)) return -1; } return access (name, mode); } /* Search for NAME using the prefix list PREFIXES. MODE is passed to access to check permissions. Return 0 if not found, otherwise return its name, allocated with malloc. */ static char * find_a_file (struct path_prefix *pprefix, const char *name, int mode, int multilib) { char *temp; const char *const file_suffix = ((mode & X_OK) != 0 ? HOST_EXECUTABLE_SUFFIX : ""); struct prefix_list *pl; int len = pprefix->max_len + strlen (name) + strlen (file_suffix) + 1; const char *multilib_name, *multilib_os_name; #ifdef DEFAULT_ASSEMBLER if (! strcmp (name, "as") && access (DEFAULT_ASSEMBLER, mode) == 0) return xstrdup (DEFAULT_ASSEMBLER); #endif #ifdef DEFAULT_LINKER if (! strcmp(name, "ld") && access (DEFAULT_LINKER, mode) == 0) return xstrdup (DEFAULT_LINKER); #endif if (machine_suffix) len += strlen (machine_suffix); multilib_name = name; multilib_os_name = name; if (multilib && multilib_os_dir) { int len1 = multilib_dir ? strlen (multilib_dir) + 1 : 0; int len2 = strlen (multilib_os_dir) + 1; len += len1 > len2 ? len1 : len2; if (multilib_dir) multilib_name = ACONCAT ((multilib_dir, dir_separator_str, name, NULL)); if (strcmp (multilib_os_dir, ".") != 0) multilib_os_name = ACONCAT ((multilib_os_dir, dir_separator_str, name, NULL)); } temp = xmalloc (len); /* Determine the filename to execute (special case for absolute paths). */ if (IS_ABSOLUTE_PATH (name)) { if (access (name, mode) == 0) { strcpy (temp, name); return temp; } } else for (pl = pprefix->plist; pl; pl = pl->next) { const char *this_name = pl->os_multilib ? multilib_os_name : multilib_name; if (machine_suffix) { /* Some systems have a suffix for executable files. So try appending that first. */ if (file_suffix[0] != 0) { strcpy (temp, pl->prefix); strcat (temp, machine_suffix); strcat (temp, multilib_name); strcat (temp, file_suffix); if (access_check (temp, mode) == 0) { if (pl->used_flag_ptr != 0) *pl->used_flag_ptr = 1; return temp; } } /* Now try just the multilib_name. */ strcpy (temp, pl->prefix); strcat (temp, machine_suffix); strcat (temp, multilib_name); if (access_check (temp, mode) == 0) { if (pl->used_flag_ptr != 0) *pl->used_flag_ptr = 1; return temp; } } /* Certain prefixes are tried with just the machine type, not the version. This is used for finding as, ld, etc. */ if (just_machine_suffix && pl->require_machine_suffix == 2) { /* Some systems have a suffix for executable files. So try appending that first. */ if (file_suffix[0] != 0) { strcpy (temp, pl->prefix); strcat (temp, just_machine_suffix); strcat (temp, multilib_name); strcat (temp, file_suffix); if (access_check (temp, mode) == 0) { if (pl->used_flag_ptr != 0) *pl->used_flag_ptr = 1; return temp; } } strcpy (temp, pl->prefix); strcat (temp, just_machine_suffix); strcat (temp, multilib_name); if (access_check (temp, mode) == 0) { if (pl->used_flag_ptr != 0) *pl->used_flag_ptr = 1; return temp; } } /* Certain prefixes can't be used without the machine suffix when the machine or version is explicitly specified. */ if (! pl->require_machine_suffix) { /* Some systems have a suffix for executable files. So try appending that first. */ if (file_suffix[0] != 0) { strcpy (temp, pl->prefix); strcat (temp, this_name); strcat (temp, file_suffix); if (access_check (temp, mode) == 0) { if (pl->used_flag_ptr != 0) *pl->used_flag_ptr = 1; return temp; } } strcpy (temp, pl->prefix); strcat (temp, this_name); if (access_check (temp, mode) == 0) { if (pl->used_flag_ptr != 0) *pl->used_flag_ptr = 1; return temp; } } } free (temp); return 0; } /* Ranking of prefixes in the sort list. -B prefixes are put before all others. */ enum path_prefix_priority { PREFIX_PRIORITY_B_OPT, PREFIX_PRIORITY_LAST }; /* Add an entry for PREFIX in PLIST. The PLIST is kept in ascending order according to PRIORITY. Within each PRIORITY, new entries are appended. If WARN is nonzero, we will warn if no file is found through this prefix. WARN should point to an int which will be set to 1 if this entry is used. COMPONENT is the value to be passed to update_path. REQUIRE_MACHINE_SUFFIX is 1 if this prefix can't be used without the complete value of machine_suffix. 2 means try both machine_suffix and just_machine_suffix. */ static void add_prefix (struct path_prefix *pprefix, const char *prefix, const char *component, /* enum prefix_priority */ int priority, int require_machine_suffix, int *warn, int os_multilib) { struct prefix_list *pl, **prev; int len; for (prev = &pprefix->plist; (*prev) != NULL && (*prev)->priority <= priority; prev = &(*prev)->next) ; /* Keep track of the longest prefix. */ prefix = update_path (prefix, component); len = strlen (prefix); if (len > pprefix->max_len) pprefix->max_len = len; pl = xmalloc (sizeof (struct prefix_list)); pl->prefix = prefix; pl->require_machine_suffix = require_machine_suffix; pl->used_flag_ptr = warn; pl->priority = priority; pl->os_multilib = os_multilib; if (warn) *warn = 0; /* Insert after PREV. */ pl->next = (*prev); (*prev) = pl; } /* Same as add_prefix, but prepending target_system_root to prefix. */ static void add_sysrooted_prefix (struct path_prefix *pprefix, const char *prefix, const char *component, /* enum prefix_priority */ int priority, int require_machine_suffix, int *warn, int os_multilib) { if (!IS_ABSOLUTE_PATH (prefix)) abort (); if (target_system_root) { if (target_sysroot_suffix) prefix = concat (target_sysroot_suffix, prefix, NULL); prefix = concat (target_system_root, prefix, NULL); /* We have to override this because GCC's notion of sysroot moves along with GCC. */ component = "GCC"; } add_prefix (pprefix, prefix, component, priority, require_machine_suffix, warn, os_multilib); } /* Execute the command specified by the arguments on the current line of spec. When using pipes, this includes several piped-together commands with `|' between them. Return 0 if successful, -1 if failed. */ static int execute (void) { int i; int n_commands; /* # of command. */ char *string; struct command { const char *prog; /* program name. */ const char **argv; /* vector of args. */ int pid; /* pid of process for this command. */ }; struct command *commands; /* each command buffer with above info. */ if (processing_spec_function) abort (); /* Count # of piped commands. */ for (n_commands = 1, i = 0; i < argbuf_index; i++) if (strcmp (argbuf[i], "|") == 0) n_commands++; /* Get storage for each command. */ commands = alloca (n_commands * sizeof (struct command)); /* Split argbuf into its separate piped processes, and record info about each one. Also search for the programs that are to be run. */ commands[0].prog = argbuf[0]; /* first command. */ commands[0].argv = &argbuf[0]; string = find_a_file (&exec_prefixes, commands[0].prog, X_OK, 0); if (string) commands[0].argv[0] = string; for (n_commands = 1, i = 0; i < argbuf_index; i++) if (strcmp (argbuf[i], "|") == 0) { /* each command. */ #if defined (__MSDOS__) || defined (OS2) || defined (VMS) fatal ("-pipe not supported"); #endif argbuf[i] = 0; /* termination of command args. */ commands[n_commands].prog = argbuf[i + 1]; commands[n_commands].argv = &argbuf[i + 1]; string = find_a_file (&exec_prefixes, commands[n_commands].prog, X_OK, 0); if (string) commands[n_commands].argv[0] = string; n_commands++; } argbuf[argbuf_index] = 0; /* If -v, print what we are about to do, and maybe query. */ if (verbose_flag) { /* For help listings, put a blank line between sub-processes. */ if (print_help_list) fputc ('\n', stderr); /* Print each piped command as a separate line. */ for (i = 0; i < n_commands; i++) { const char *const *j; if (verbose_only_flag) { for (j = commands[i].argv; *j; j++) { const char *p; fprintf (stderr, " \""); for (p = *j; *p; ++p) { if (*p == '"' || *p == '\\' || *p == '$') fputc ('\\', stderr); fputc (*p, stderr); } fputc ('"', stderr); } } else for (j = commands[i].argv; *j; j++) fprintf (stderr, " %s", *j); /* Print a pipe symbol after all but the last command. */ if (i + 1 != n_commands) fprintf (stderr, " |"); fprintf (stderr, "\n"); } fflush (stderr); if (verbose_only_flag != 0) { /* verbose_only_flag should act as if the spec was executed, so increment execution_count before returning. This prevents spurious warnings about unused linker input files, etc. */ execution_count++; return 0; } #ifdef DEBUG notice ("\nGo ahead? (y or n) "); fflush (stderr); i = getchar (); if (i != '\n') while (getchar () != '\n') ; if (i != 'y' && i != 'Y') return 0; #endif /* DEBUG */ } #ifdef ENABLE_VALGRIND_CHECKING /* Run the each command through valgrind. To simplify prepending the path to valgrind and the option "-q" (for quiet operation unless something triggers), we allocate a separate argv array. */ for (i = 0; i < n_commands; i++) { const char **argv; int argc; int j; for (argc = 0; commands[i].argv[argc] != NULL; argc++) ; argv = alloca ((argc + 3) * sizeof (char *)); argv[0] = VALGRIND_PATH; argv[1] = "-q"; for (j = 2; j < argc + 2; j++) argv[j] = commands[i].argv[j - 2]; argv[j] = NULL; commands[i].argv = argv; commands[i].prog = argv[0]; } #endif /* Run each piped subprocess. */ for (i = 0; i < n_commands; i++) { char *errmsg_fmt, *errmsg_arg; const char *string = commands[i].argv[0]; /* For some bizarre reason, the second argument of execvp() is char *const *, not const char *const *. */ commands[i].pid = pexecute (string, (char *const *) commands[i].argv, programname, temp_filename, &errmsg_fmt, &errmsg_arg, ((i == 0 ? PEXECUTE_FIRST : 0) | (i + 1 == n_commands ? PEXECUTE_LAST : 0) | (string == commands[i].prog ? PEXECUTE_SEARCH : 0) | (verbose_flag ? PEXECUTE_VERBOSE : 0))); if (commands[i].pid == -1) pfatal_pexecute (errmsg_fmt, errmsg_arg); if (string != commands[i].prog) free ((void *) string); } execution_count++; /* Wait for all the subprocesses to finish. We don't care what order they finish in; we know that N_COMMANDS waits will get them all. Ignore subprocesses that we don't know about, since they can be spawned by the process that exec'ed us. */ { int ret_code = 0; #ifdef HAVE_GETRUSAGE struct timeval d; double ut = 0.0, st = 0.0; #endif for (i = 0; i < n_commands;) { int j; int status; int pid; pid = pwait (commands[i].pid, &status, 0); if (pid < 0) abort (); #ifdef HAVE_GETRUSAGE if (report_times) { /* getrusage returns the total resource usage of all children up to now. Copy the previous values into prus, get the current statistics, then take the difference. */ prus = rus; getrusage (RUSAGE_CHILDREN, &rus); d.tv_sec = rus.ru_utime.tv_sec - prus.ru_utime.tv_sec; d.tv_usec = rus.ru_utime.tv_usec - prus.ru_utime.tv_usec; ut = (double) d.tv_sec + (double) d.tv_usec / 1.0e6; d.tv_sec = rus.ru_stime.tv_sec - prus.ru_stime.tv_sec; d.tv_usec = rus.ru_stime.tv_usec - prus.ru_stime.tv_usec; st = (double) d.tv_sec + (double) d.tv_usec / 1.0e6; } #endif for (j = 0; j < n_commands; j++) if (commands[j].pid == pid) { i++; if (WIFSIGNALED (status)) { #ifdef SIGPIPE /* SIGPIPE is a special case. It happens in -pipe mode when the compiler dies before the preprocessor is done, or the assembler dies before the compiler is done. There's generally been an error already, and this is just fallout. So don't generate another error unless we would otherwise have succeeded. */ if (WTERMSIG (status) == SIGPIPE && (signal_count || greatest_status >= MIN_FATAL_STATUS)) ; else #endif fatal ("\ Internal error: %s (program %s)\n\ Please submit a full bug report.\n\ See %s for instructions.", strsignal (WTERMSIG (status)), commands[j].prog, bug_report_url); signal_count++; ret_code = -1; } else if (WIFEXITED (status) && WEXITSTATUS (status) >= MIN_FATAL_STATUS) { if (WEXITSTATUS (status) > greatest_status) greatest_status = WEXITSTATUS (status); ret_code = -1; } #ifdef HAVE_GETRUSAGE if (report_times && ut + st != 0) notice ("# %s %.2f %.2f\n", commands[j].prog, ut, st); #endif break; } } return ret_code; } } /* Find all the switches given to us and make a vector describing them. The elements of the vector are strings, one per switch given. If a switch uses following arguments, then the `part1' field is the switch itself and the `args' field is a null-terminated vector containing the following arguments. The `live_cond' field is: 0 when initialized 1 if the switch is true in a conditional spec, -1 if false (overridden by a later switch) -2 if this switch should be ignored (used in % 2 && name[len - 2] == '.' && name[len - 1] == 'o') { obstack_grow (&obstack, name, len - 2); obstack_grow0 (&obstack, TARGET_OBJECT_SUFFIX, strlen (TARGET_OBJECT_SUFFIX)); name = obstack_finish (&obstack); } #endif #if defined(HAVE_TARGET_EXECUTABLE_SUFFIX) /* If there is no filetype, make it the executable suffix (which includes the "."). But don't get confused if we have just "-o". */ if (! do_exe || TARGET_EXECUTABLE_SUFFIX[0] == 0 || (len == 2 && name[0] == '-')) return name; for (i = len - 1; i >= 0; i--) if (IS_DIR_SEPARATOR (name[i])) break; for (i++; i < len; i++) if (name[i] == '.') return name; obstack_grow (&obstack, name, len); obstack_grow0 (&obstack, TARGET_EXECUTABLE_SUFFIX, strlen (TARGET_EXECUTABLE_SUFFIX)); name = obstack_finish (&obstack); #endif return name; } #endif /* Display the command line switches accepted by gcc. */ static void display_help (void) { printf (_("Usage: %s [options] file...\n"), programname); fputs (_("Options:\n"), stdout); fputs (_(" -pass-exit-codes Exit with highest error code from a phase\n"), stdout); fputs (_(" --help Display this information\n"), stdout); fputs (_(" --target-help Display target specific command line options\n"), stdout); if (! verbose_flag) fputs (_(" (Use '-v --help' to display command line options of sub-processes)\n"), stdout); fputs (_(" -dumpspecs Display all of the built in spec strings\n"), stdout); fputs (_(" -dumpversion Display the version of the compiler\n"), stdout); fputs (_(" -dumpmachine Display the compiler's target processor\n"), stdout); fputs (_(" -print-search-dirs Display the directories in the compiler's search path\n"), stdout); fputs (_(" -print-libgcc-file-name Display the name of the compiler's companion library\n"), stdout); fputs (_(" -print-file-name= Display the full path to library \n"), stdout); fputs (_(" -print-prog-name= Display the full path to compiler component \n"), stdout); fputs (_(" -print-multi-directory Display the root directory for versions of libgcc\n"), stdout); fputs (_("\ -print-multi-lib Display the mapping between command line options and\n\ multiple library search directories\n"), stdout); fputs (_(" -print-multi-os-directory Display the relative path to OS libraries\n"), stdout); fputs (_(" -Wa, Pass comma-separated on to the assembler\n"), stdout); fputs (_(" -Wp, Pass comma-separated on to the preprocessor\n"), stdout); fputs (_(" -Wl, Pass comma-separated on to the linker\n"), stdout); fputs (_(" -Xassembler Pass on to the assembler\n"), stdout); fputs (_(" -Xpreprocessor Pass on to the preprocessor\n"), stdout); fputs (_(" -Xlinker Pass on to the linker\n"), stdout); fputs (_(" -save-temps Do not delete intermediate files\n"), stdout); fputs (_(" -pipe Use pipes rather than intermediate files\n"), stdout); fputs (_(" -time Time the execution of each subprocess\n"), stdout); fputs (_(" -specs= Override built-in specs with the contents of \n"), stdout); fputs (_(" -std= Assume that the input sources are for \n"), stdout); fputs (_(" -B Add to the compiler's search paths\n"), stdout); fputs (_(" -b Run gcc for target , if installed\n"), stdout); fputs (_(" -V Run gcc version number , if installed\n"), stdout); fputs (_(" -v Display the programs invoked by the compiler\n"), stdout); fputs (_(" -### Like -v but options quoted and commands not executed\n"), stdout); fputs (_(" -E Preprocess only; do not compile, assemble or link\n"), stdout); fputs (_(" -S Compile only; do not assemble or link\n"), stdout); fputs (_(" -c Compile and assemble, but do not link\n"), stdout); fputs (_(" -o Place the output into \n"), stdout); fputs (_("\ -x Specify the language of the following input files\n\ Permissible languages include: c c++ assembler none\n\ 'none' means revert to the default behavior of\n\ guessing the language based on the file's extension\n\ "), stdout); printf (_("\ \nOptions starting with -g, -f, -m, -O, -W, or --param are automatically\n\ passed on to the various sub-processes invoked by %s. In order to pass\n\ other options on to these processes the -W options must be used.\n\ "), programname); /* The rest of the options are displayed by invocations of the various sub-processes. */ } static void add_preprocessor_option (const char *option, int len) { n_preprocessor_options++; if (! preprocessor_options) preprocessor_options = xmalloc (n_preprocessor_options * sizeof (char *)); else preprocessor_options = xrealloc (preprocessor_options, n_preprocessor_options * sizeof (char *)); preprocessor_options [n_preprocessor_options - 1] = save_string (option, len); } static void add_assembler_option (const char *option, int len) { n_assembler_options++; if (! assembler_options) assembler_options = xmalloc (n_assembler_options * sizeof (char *)); else assembler_options = xrealloc (assembler_options, n_assembler_options * sizeof (char *)); assembler_options [n_assembler_options - 1] = save_string (option, len); } static void add_linker_option (const char *option, int len) { n_linker_options++; if (! linker_options) linker_options = xmalloc (n_linker_options * sizeof (char *)); else linker_options = xrealloc (linker_options, n_linker_options * sizeof (char *)); linker_options [n_linker_options - 1] = save_string (option, len); } /* Create the vector `switches' and its contents. Store its length in `n_switches'. */ static void process_command (int argc, const char **argv) { int i; const char *temp; char *temp1; const char *spec_lang = 0; int last_language_n_infiles; int have_c = 0; int have_o = 0; int lang_n_infiles = 0; #ifdef MODIFY_TARGET_NAME int is_modify_target_name; int j; #endif GET_ENVIRONMENT (gcc_exec_prefix, "GCC_EXEC_PREFIX"); n_switches = 0; n_infiles = 0; added_libraries = 0; /* Figure compiler version from version string. */ compiler_version = temp1 = xstrdup (version_string); for (; *temp1; ++temp1) { if (*temp1 == ' ') { *temp1 = '\0'; break; } } /* If there is a -V or -b option (or both), process it now, before trying to interpret the rest of the command line. */ if (argc > 1 && argv[1][0] == '-' && (argv[1][1] == 'V' || argv[1][1] == 'b')) { const char *new_version = DEFAULT_TARGET_VERSION; const char *new_machine = DEFAULT_TARGET_MACHINE; const char *progname = argv[0]; char **new_argv; char *new_argv0; int baselen; while (argc > 1 && argv[1][0] == '-' && (argv[1][1] == 'V' || argv[1][1] == 'b')) { char opt = argv[1][1]; const char *arg; if (argv[1][2] != '\0') { arg = argv[1] + 2; argc -= 1; argv += 1; } else if (argc > 2) { arg = argv[2]; argc -= 2; argv += 2; } else fatal ("`-%c' option must have argument", opt); if (opt == 'V') new_version = arg; else new_machine = arg; } for (baselen = strlen (progname); baselen > 0; baselen--) if (IS_DIR_SEPARATOR (progname[baselen-1])) break; new_argv0 = xmemdup (progname, baselen, baselen + concat_length (new_version, new_machine, "-gcc-", NULL) + 1); strcpy (new_argv0 + baselen, new_machine); strcat (new_argv0, "-gcc-"); strcat (new_argv0, new_version); new_argv = xmemdup (argv, (argc + 1) * sizeof (argv[0]), (argc + 1) * sizeof (argv[0])); new_argv[0] = new_argv0; execvp (new_argv0, new_argv); fatal ("couldn't run `%s': %s", new_argv0, xstrerror (errno)); } /* Set up the default search paths. If there is no GCC_EXEC_PREFIX, see if we can create it from the pathname specified in argv[0]. */ gcc_libexec_prefix = standard_libexec_prefix; #ifndef FREEBSD_NATIVE #ifndef VMS /* FIXME: make_relative_prefix doesn't yet work for VMS. */ if (!gcc_exec_prefix) { gcc_exec_prefix = make_relative_prefix (argv[0], standard_bindir_prefix, standard_exec_prefix); gcc_libexec_prefix = make_relative_prefix (argv[0], standard_bindir_prefix, standard_libexec_prefix); if (gcc_exec_prefix) putenv (concat ("GCC_EXEC_PREFIX=", gcc_exec_prefix, NULL)); } else gcc_libexec_prefix = make_relative_prefix (gcc_exec_prefix, standard_exec_prefix, standard_libexec_prefix); #else #endif #endif /* not FREEBSD_NATIVE */ if (gcc_exec_prefix) { int len = strlen (gcc_exec_prefix); if (len > (int) sizeof ("/lib/gcc/") - 1 && (IS_DIR_SEPARATOR (gcc_exec_prefix[len-1]))) { temp = gcc_exec_prefix + len - sizeof ("/lib/gcc/") + 1; if (IS_DIR_SEPARATOR (*temp) && strncmp (temp + 1, "lib", 3) == 0 && IS_DIR_SEPARATOR (temp[4]) && strncmp (temp + 5, "gcc", 3) == 0) len -= sizeof ("/lib/gcc/") - 1; } set_std_prefix (gcc_exec_prefix, len); add_prefix (&exec_prefixes, gcc_libexec_prefix, "GCC", PREFIX_PRIORITY_LAST, 0, NULL, 0); add_prefix (&startfile_prefixes, gcc_exec_prefix, "GCC", PREFIX_PRIORITY_LAST, 0, NULL, 0); } /* COMPILER_PATH and LIBRARY_PATH have values that are lists of directory names with colons. */ GET_ENVIRONMENT (temp, "COMPILER_PATH"); if (temp) { const char *startp, *endp; char *nstore = alloca (strlen (temp) + 3); startp = endp = temp; while (1) { if (*endp == PATH_SEPARATOR || *endp == 0) { strncpy (nstore, startp, endp - startp); if (endp == startp) strcpy (nstore, concat (".", dir_separator_str, NULL)); else if (!IS_DIR_SEPARATOR (endp[-1])) { nstore[endp - startp] = DIR_SEPARATOR; nstore[endp - startp + 1] = 0; } else nstore[endp - startp] = 0; add_prefix (&exec_prefixes, nstore, 0, PREFIX_PRIORITY_LAST, 0, NULL, 0); add_prefix (&include_prefixes, concat (nstore, "include", NULL), 0, PREFIX_PRIORITY_LAST, 0, NULL, 0); if (*endp == 0) break; endp = startp = endp + 1; } else endp++; } } GET_ENVIRONMENT (temp, LIBRARY_PATH_ENV); if (temp && *cross_compile == '0') { const char *startp, *endp; char *nstore = alloca (strlen (temp) + 3); startp = endp = temp; while (1) { if (*endp == PATH_SEPARATOR || *endp == 0) { strncpy (nstore, startp, endp - startp); if (endp == startp) strcpy (nstore, concat (".", dir_separator_str, NULL)); else if (!IS_DIR_SEPARATOR (endp[-1])) { nstore[endp - startp] = DIR_SEPARATOR; nstore[endp - startp + 1] = 0; } else nstore[endp - startp] = 0; add_prefix (&startfile_prefixes, nstore, NULL, PREFIX_PRIORITY_LAST, 0, NULL, 1); if (*endp == 0) break; endp = startp = endp + 1; } else endp++; } } /* Use LPATH like LIBRARY_PATH (for the CMU build program). */ GET_ENVIRONMENT (temp, "LPATH"); if (temp && *cross_compile == '0') { const char *startp, *endp; char *nstore = alloca (strlen (temp) + 3); startp = endp = temp; while (1) { if (*endp == PATH_SEPARATOR || *endp == 0) { strncpy (nstore, startp, endp - startp); if (endp == startp) strcpy (nstore, concat (".", dir_separator_str, NULL)); else if (!IS_DIR_SEPARATOR (endp[-1])) { nstore[endp - startp] = DIR_SEPARATOR; nstore[endp - startp + 1] = 0; } else nstore[endp - startp] = 0; add_prefix (&startfile_prefixes, nstore, NULL, PREFIX_PRIORITY_LAST, 0, NULL, 1); if (*endp == 0) break; endp = startp = endp + 1; } else endp++; } } /* Options specified as if they appeared on the command line. */ temp = getenv ("GCC_OPTIONS"); if ((temp) && (strlen (temp) > 0)) { int len; int optc = 1; int new_argc; const char **new_argv; char *envopts; while (isspace (*temp)) temp++; len = strlen (temp); envopts = (char *) xmalloc (len + 1); strcpy (envopts, temp); for (i = 0; i < (len - 1); i++) if ((isspace (envopts[i])) && ! (isspace (envopts[i+1]))) optc++; new_argv = (const char **) alloca ((optc + argc) * sizeof(char *)); for (i = 0, new_argc = 1; new_argc <= optc; new_argc++) { while (isspace (envopts[i])) i++; new_argv[new_argc] = envopts + i; while (!isspace (envopts[i]) && (envopts[i] != '\0')) i++; envopts[i++] = '\0'; } for (i = 1; i < argc; i++) new_argv[new_argc++] = argv[i]; argv = new_argv; argc = new_argc; } /* Convert new-style -- options to old-style. */ translate_options (&argc, (const char *const **) &argv); /* Do language-specific adjustment/addition of flags. */ lang_specific_driver (&argc, (const char *const **) &argv, &added_libraries); /* Scan argv twice. Here, the first time, just count how many switches there will be in their vector, and how many input files in theirs. Here we also parse the switches that cc itself uses (e.g. -v). */ for (i = 1; i < argc; i++) { if (! strcmp (argv[i], "-dumpspecs")) { struct spec_list *sl; init_spec (); for (sl = specs; sl; sl = sl->next) printf ("*%s:\n%s\n\n", sl->name, *(sl->ptr_spec)); if (link_command_spec) printf ("*link_command:\n%s\n\n", link_command_spec); exit (0); } else if (! strcmp (argv[i], "-dumpversion")) { printf ("%s\n", spec_version); exit (0); } else if (! strcmp (argv[i], "-dumpmachine")) { printf ("%s\n", spec_machine); exit (0); } else if (strcmp (argv[i], "-fversion") == 0) { /* translate_options () has turned --version into -fversion. */ printf (_("%s (GCC) %s\n"), programname, version_string); - printf ("Copyright %s 2004 Free Software Foundation, Inc.\n", + printf ("Copyright %s 2006 Free Software Foundation, Inc.\n", _("(C)")); fputs (_("This is free software; see the source for copying conditions. There is NO\n\ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"), stdout); exit (0); } else if (strcmp (argv[i], "-fhelp") == 0) { /* translate_options () has turned --help into -fhelp. */ print_help_list = 1; /* We will be passing a dummy file on to the sub-processes. */ n_infiles++; n_switches++; /* CPP driver cannot obtain switch from cc1_options. */ if (is_cpp_driver) add_preprocessor_option ("--help", 6); add_assembler_option ("--help", 6); add_linker_option ("--help", 6); } else if (strcmp (argv[i], "-ftarget-help") == 0) { /* translate_options() has turned --target-help into -ftarget-help. */ target_help_flag = 1; /* We will be passing a dummy file on to the sub-processes. */ n_infiles++; n_switches++; /* CPP driver cannot obtain switch from cc1_options. */ if (is_cpp_driver) add_preprocessor_option ("--target-help", 13); add_assembler_option ("--target-help", 13); add_linker_option ("--target-help", 13); } else if (! strcmp (argv[i], "-pass-exit-codes")) { pass_exit_codes = 1; n_switches++; } else if (! strcmp (argv[i], "-print-search-dirs")) print_search_dirs = 1; else if (! strcmp (argv[i], "-print-libgcc-file-name")) print_file_name = "libgcc.a"; else if (! strncmp (argv[i], "-print-file-name=", 17)) print_file_name = argv[i] + 17; else if (! strncmp (argv[i], "-print-prog-name=", 17)) print_prog_name = argv[i] + 17; else if (! strcmp (argv[i], "-print-multi-lib")) print_multi_lib = 1; else if (! strcmp (argv[i], "-print-multi-directory")) print_multi_directory = 1; else if (! strcmp (argv[i], "-print-multi-os-directory")) print_multi_os_directory = 1; else if (! strncmp (argv[i], "-Wa,", 4)) { int prev, j; /* Pass the rest of this option to the assembler. */ /* Split the argument at commas. */ prev = 4; for (j = 4; argv[i][j]; j++) if (argv[i][j] == ',') { add_assembler_option (argv[i] + prev, j - prev); prev = j + 1; } /* Record the part after the last comma. */ add_assembler_option (argv[i] + prev, j - prev); } else if (! strncmp (argv[i], "-Wp,", 4)) { int prev, j; /* Pass the rest of this option to the preprocessor. */ /* Split the argument at commas. */ prev = 4; for (j = 4; argv[i][j]; j++) if (argv[i][j] == ',') { add_preprocessor_option (argv[i] + prev, j - prev); prev = j + 1; } /* Record the part after the last comma. */ add_preprocessor_option (argv[i] + prev, j - prev); } else if (argv[i][0] == '+' && argv[i][1] == 'e') /* The +e options to the C++ front-end. */ n_switches++; else if (strncmp (argv[i], "-Wl,", 4) == 0) { int j; /* Split the argument at commas. */ for (j = 3; argv[i][j]; j++) n_infiles += (argv[i][j] == ','); } else if (strcmp (argv[i], "-Xlinker") == 0) { if (i + 1 == argc) fatal ("argument to `-Xlinker' is missing"); n_infiles++; i++; } else if (strcmp (argv[i], "-Xpreprocessor") == 0) { if (i + 1 == argc) fatal ("argument to `-Xpreprocessor' is missing"); add_preprocessor_option (argv[i+1], strlen (argv[i+1])); } else if (strcmp (argv[i], "-Xassembler") == 0) { if (i + 1 == argc) fatal ("argument to `-Xassembler' is missing"); add_assembler_option (argv[i+1], strlen (argv[i+1])); } else if (strcmp (argv[i], "-l") == 0) { if (i + 1 == argc) fatal ("argument to `-l' is missing"); n_infiles++; i++; } else if (strncmp (argv[i], "-l", 2) == 0) n_infiles++; else if (strcmp (argv[i], "-save-temps") == 0) { save_temps_flag = 1; n_switches++; } else if (strcmp (argv[i], "-specs") == 0) { struct user_specs *user = xmalloc (sizeof (struct user_specs)); if (++i >= argc) fatal ("argument to `-specs' is missing"); user->next = (struct user_specs *) 0; user->filename = argv[i]; if (user_specs_tail) user_specs_tail->next = user; else user_specs_head = user; user_specs_tail = user; } else if (strncmp (argv[i], "-specs=", 7) == 0) { struct user_specs *user = xmalloc (sizeof (struct user_specs)); if (strlen (argv[i]) == 7) fatal ("argument to `-specs=' is missing"); user->next = (struct user_specs *) 0; user->filename = argv[i] + 7; if (user_specs_tail) user_specs_tail->next = user; else user_specs_head = user; user_specs_tail = user; } else if (strcmp (argv[i], "-time") == 0) report_times = 1; else if (strcmp (argv[i], "-pipe") == 0) { /* -pipe has to go into the switches array as well as setting a flag. */ use_pipes = 1; n_switches++; } else if (strcmp (argv[i], "-###") == 0) { /* This is similar to -v except that there is no execution of the commands and the echoed arguments are quoted. It is intended for use in shell scripts to capture the driver-generated command line. */ verbose_only_flag++; verbose_flag++; } else if (argv[i][0] == '-' && argv[i][1] != 0) { const char *p = &argv[i][1]; int c = *p; switch (c) { case 'b': case 'V': fatal ("`-%c' must come at the start of the command line", c); break; case 'B': { const char *value; int len; if (p[1] == 0 && i + 1 == argc) fatal ("argument to `-B' is missing"); if (p[1] == 0) value = argv[++i]; else value = p + 1; len = strlen (value); /* Catch the case where the user has forgotten to append a directory separator to the path. Note, they may be using -B to add an executable name prefix, eg "i386-elf-", in order to distinguish between multiple installations of GCC in the same directory. Hence we must check to see if appending a directory separator actually makes a valid directory name. */ if (! IS_DIR_SEPARATOR (value [len - 1]) && is_directory (value, "", 0)) { char *tmp = xmalloc (len + 2); strcpy (tmp, value); tmp[len] = DIR_SEPARATOR; tmp[++ len] = 0; value = tmp; } /* As a kludge, if the arg is "[foo/]stageN/", just add "[foo/]include" to the include prefix. */ if ((len == 7 || (len > 7 && (IS_DIR_SEPARATOR (value[len - 8])))) && strncmp (value + len - 7, "stage", 5) == 0 && ISDIGIT (value[len - 2]) && (IS_DIR_SEPARATOR (value[len - 1]))) { if (len == 7) add_prefix (&include_prefixes, "include", NULL, PREFIX_PRIORITY_B_OPT, 0, NULL, 0); else { char * string = xmalloc (len + 1); strncpy (string, value, len - 7); strcpy (string + len - 7, "include"); add_prefix (&include_prefixes, string, NULL, PREFIX_PRIORITY_B_OPT, 0, NULL, 0); } } add_prefix (&exec_prefixes, value, NULL, PREFIX_PRIORITY_B_OPT, 0, &warn_B, 0); add_prefix (&startfile_prefixes, value, NULL, PREFIX_PRIORITY_B_OPT, 0, &warn_B, 0); add_prefix (&include_prefixes, concat (value, "include", NULL), NULL, PREFIX_PRIORITY_B_OPT, 0, NULL, 0); n_switches++; } break; case 'v': /* Print our subcommands and print versions. */ n_switches++; /* If they do anything other than exactly `-v', don't set verbose_flag; rather, continue on to give the error. */ if (p[1] != 0) break; verbose_flag++; break; case 'S': case 'c': if (p[1] == 0) { have_c = 1; n_switches++; break; } goto normal_switch; case 'o': have_o = 1; #if defined(HAVE_TARGET_EXECUTABLE_SUFFIX) if (! have_c) { int skip; /* Forward scan, just in case -S or -c is specified after -o. */ int j = i + 1; if (p[1] == 0) ++j; while (j < argc) { if (argv[j][0] == '-') { if (SWITCH_CURTAILS_COMPILATION (argv[j][1]) && argv[j][2] == 0) { have_c = 1; break; } else if ((skip = SWITCH_TAKES_ARG (argv[j][1]))) j += skip - (argv[j][2] != 0); else if ((skip = WORD_SWITCH_TAKES_ARG (argv[j] + 1))) j += skip; } j++; } } #endif #if defined(HAVE_TARGET_EXECUTABLE_SUFFIX) || defined(HAVE_TARGET_OBJECT_SUFFIX) if (p[1] == 0) argv[i + 1] = convert_filename (argv[i + 1], ! have_c, 0); else argv[i] = convert_filename (argv[i], ! have_c, 0); #endif goto normal_switch; default: normal_switch: #ifdef MODIFY_TARGET_NAME is_modify_target_name = 0; for (j = 0; j < ARRAY_SIZE (modify_target); j++) if (! strcmp (argv[i], modify_target[j].sw)) { char *new_name = xmalloc (strlen (modify_target[j].str) + strlen (spec_machine)); const char *p, *r; char *q; int made_addition = 0; is_modify_target_name = 1; for (p = spec_machine, q = new_name; *p != 0; ) { if (modify_target[j].add_del == DELETE && (! strncmp (q, modify_target[j].str, strlen (modify_target[j].str)))) p += strlen (modify_target[j].str); else if (modify_target[j].add_del == ADD && ! made_addition && *p == '-') { for (r = modify_target[j].str; *r != 0; ) *q++ = *r++; made_addition = 1; } *q++ = *p++; } spec_machine = new_name; } if (is_modify_target_name) break; #endif n_switches++; if (SWITCH_TAKES_ARG (c) > (p[1] != 0)) i += SWITCH_TAKES_ARG (c) - (p[1] != 0); else if (WORD_SWITCH_TAKES_ARG (p)) i += WORD_SWITCH_TAKES_ARG (p); } } else { n_infiles++; lang_n_infiles++; } } combine_inputs = (have_c && have_o && lang_n_infiles > 1); if ((save_temps_flag || report_times) && use_pipes) { /* -save-temps overrides -pipe, so that temp files are produced */ if (save_temps_flag) error ("warning: -pipe ignored because -save-temps specified"); /* -time overrides -pipe because we can't get correct stats when multiple children are running at once. */ else if (report_times) error ("warning: -pipe ignored because -time specified"); use_pipes = 0; } /* Set up the search paths before we go looking for config files. */ /* These come before the md prefixes so that we will find gcc's subcommands (such as cpp) rather than those of the host system. */ /* Use 2 as fourth arg meaning try just the machine as a suffix, as well as trying the machine and the version. */ #ifdef FREEBSD_NATIVE add_prefix (&exec_prefixes, PREFIX"/bin/", "BINUTILS", PREFIX_PRIORITY_LAST, 0, warn_std_ptr, 0); #endif /* FREEBSD_NATIVE */ #ifndef OS2 add_prefix (&exec_prefixes, standard_libexec_prefix, "GCC", PREFIX_PRIORITY_LAST, 1, warn_std_ptr, 0); add_prefix (&exec_prefixes, standard_libexec_prefix, "BINUTILS", PREFIX_PRIORITY_LAST, 2, warn_std_ptr, 0); #ifndef FREEBSD_NATIVE add_prefix (&exec_prefixes, standard_exec_prefix, "BINUTILS", PREFIX_PRIORITY_LAST, 2, warn_std_ptr, 0); add_prefix (&exec_prefixes, standard_exec_prefix_1, "BINUTILS", PREFIX_PRIORITY_LAST, 2, warn_std_ptr, 0); add_prefix (&exec_prefixes, standard_exec_prefix_2, "BINUTILS", PREFIX_PRIORITY_LAST, 2, warn_std_ptr, 0); #endif /* FREEBSD_NATIVE */ #endif #ifndef FREEBSD_NATIVE add_prefix (&startfile_prefixes, standard_exec_prefix, "BINUTILS", PREFIX_PRIORITY_LAST, 1, warn_std_ptr, 0); add_prefix (&startfile_prefixes, standard_exec_prefix_2, "BINUTILS", PREFIX_PRIORITY_LAST, 1, warn_std_ptr, 0); #endif /* not FREEBSD_NATIVE */ tooldir_prefix = concat (tooldir_base_prefix, spec_machine, dir_separator_str, NULL); /* If tooldir is relative, base it on exec_prefixes. A relative tooldir lets us move the installed tree as a unit. If GCC_EXEC_PREFIX is defined, then we want to add two relative directories, so that we can search both the user specified directory and the standard place. */ if (!IS_ABSOLUTE_PATH (tooldir_prefix)) { if (gcc_exec_prefix) { char *gcc_exec_tooldir_prefix = concat (gcc_exec_prefix, spec_machine, dir_separator_str, spec_version, dir_separator_str, tooldir_prefix, NULL); add_prefix (&exec_prefixes, concat (gcc_exec_tooldir_prefix, "bin", dir_separator_str, NULL), NULL, PREFIX_PRIORITY_LAST, 0, NULL, 0); add_prefix (&startfile_prefixes, concat (gcc_exec_tooldir_prefix, "lib", dir_separator_str, NULL), NULL, PREFIX_PRIORITY_LAST, 0, NULL, 1); } tooldir_prefix = concat (standard_exec_prefix, spec_machine, dir_separator_str, spec_version, dir_separator_str, tooldir_prefix, NULL); } #ifndef FREEBSD_NATIVE add_prefix (&exec_prefixes, concat (tooldir_prefix, "bin", dir_separator_str, NULL), "BINUTILS", PREFIX_PRIORITY_LAST, 0, NULL, 0); add_prefix (&startfile_prefixes, concat (tooldir_prefix, "lib", dir_separator_str, NULL), "BINUTILS", PREFIX_PRIORITY_LAST, 0, NULL, 1); #if defined(TARGET_SYSTEM_ROOT_RELOCATABLE) && !defined(VMS) /* If the normal TARGET_SYSTEM_ROOT is inside of $exec_prefix, then consider it to relocate with the rest of the GCC installation if GCC_EXEC_PREFIX is set. ``make_relative_prefix'' is not compiled for VMS, so don't call it. */ if (target_system_root && gcc_exec_prefix) { char *tmp_prefix = make_relative_prefix (argv[0], standard_bindir_prefix, target_system_root); if (tmp_prefix && access_check (tmp_prefix, F_OK) == 0) { target_system_root = tmp_prefix; target_system_root_changed = 1; } } #endif #endif /* FREEBSD_NATIVE */ /* More prefixes are enabled in main, after we read the specs file and determine whether this is cross-compilation or not. */ /* Then create the space for the vectors and scan again. */ switches = xmalloc ((n_switches + 1) * sizeof (struct switchstr)); infiles = xmalloc ((n_infiles + 1) * sizeof (struct infile)); n_switches = 0; n_infiles = 0; last_language_n_infiles = -1; /* This, time, copy the text of each switch and store a pointer to the copy in the vector of switches. Store all the infiles in their vector. */ for (i = 1; i < argc; i++) { /* Just skip the switches that were handled by the preceding loop. */ #ifdef MODIFY_TARGET_NAME is_modify_target_name = 0; for (j = 0; j < ARRAY_SIZE (modify_target); j++) if (! strcmp (argv[i], modify_target[j].sw)) is_modify_target_name = 1; if (is_modify_target_name) ; else #endif if (! strncmp (argv[i], "-Wa,", 4)) ; else if (! strncmp (argv[i], "-Wp,", 4)) ; else if (! strcmp (argv[i], "-pass-exit-codes")) ; else if (! strcmp (argv[i], "-print-search-dirs")) ; else if (! strcmp (argv[i], "-print-libgcc-file-name")) ; else if (! strncmp (argv[i], "-print-file-name=", 17)) ; else if (! strncmp (argv[i], "-print-prog-name=", 17)) ; else if (! strcmp (argv[i], "-print-multi-lib")) ; else if (! strcmp (argv[i], "-print-multi-directory")) ; else if (! strcmp (argv[i], "-print-multi-os-directory")) ; else if (! strcmp (argv[i], "-ftarget-help")) ; else if (! strcmp (argv[i], "-fhelp")) ; else if (argv[i][0] == '+' && argv[i][1] == 'e') { /* Compensate for the +e options to the C++ front-end; they're there simply for cfront call-compatibility. We do some magic in default_compilers to pass them down properly. Note we deliberately start at the `+' here, to avoid passing -e0 or -e1 down into the linker. */ switches[n_switches].part1 = &argv[i][0]; switches[n_switches].args = 0; switches[n_switches].live_cond = SWITCH_OK; switches[n_switches].validated = 0; n_switches++; } else if (strncmp (argv[i], "-Wl,", 4) == 0) { int prev, j; /* Split the argument at commas. */ prev = 4; for (j = 4; argv[i][j]; j++) if (argv[i][j] == ',') { infiles[n_infiles].language = "*"; infiles[n_infiles++].name = save_string (argv[i] + prev, j - prev); prev = j + 1; } /* Record the part after the last comma. */ infiles[n_infiles].language = "*"; infiles[n_infiles++].name = argv[i] + prev; } else if (strcmp (argv[i], "-Xlinker") == 0) { infiles[n_infiles].language = "*"; infiles[n_infiles++].name = argv[++i]; } else if (strcmp (argv[i], "-Xassembler") == 0) { infiles[n_infiles].language = "*"; infiles[n_infiles++].name = argv[++i]; } else if (strcmp (argv[i], "-Xpreprocessor") == 0) { infiles[n_infiles].language = "*"; infiles[n_infiles++].name = argv[++i]; } else if (strcmp (argv[i], "-l") == 0) { /* POSIX allows separation of -l and the lib arg; canonicalize by concatenating -l with its arg */ infiles[n_infiles].language = "*"; infiles[n_infiles++].name = concat ("-l", argv[++i], NULL); } else if (strncmp (argv[i], "-l", 2) == 0) { infiles[n_infiles].language = "*"; infiles[n_infiles++].name = argv[i]; } else if (strcmp (argv[i], "-specs") == 0) i++; else if (strncmp (argv[i], "-specs=", 7) == 0) ; else if (strcmp (argv[i], "-time") == 0) ; else if (strcmp (argv[i], "-###") == 0) ; else if (argv[i][0] == '-' && argv[i][1] != 0) { const char *p = &argv[i][1]; int c = *p; if (c == 'x') { if (p[1] == 0 && i + 1 == argc) fatal ("argument to `-x' is missing"); if (p[1] == 0) spec_lang = argv[++i]; else spec_lang = p + 1; if (! strcmp (spec_lang, "none")) /* Suppress the warning if -xnone comes after the last input file, because alternate command interfaces like g++ might find it useful to place -xnone after each input file. */ spec_lang = 0; else last_language_n_infiles = n_infiles; continue; } switches[n_switches].part1 = p; /* Deal with option arguments in separate argv elements. */ if ((SWITCH_TAKES_ARG (c) > (p[1] != 0)) || WORD_SWITCH_TAKES_ARG (p)) { int j = 0; int n_args = WORD_SWITCH_TAKES_ARG (p); if (n_args == 0) { /* Count only the option arguments in separate argv elements. */ n_args = SWITCH_TAKES_ARG (c) - (p[1] != 0); } if (i + n_args >= argc) fatal ("argument to `-%s' is missing", p); switches[n_switches].args = xmalloc ((n_args + 1) * sizeof(const char *)); while (j < n_args) switches[n_switches].args[j++] = argv[++i]; /* Null-terminate the vector. */ switches[n_switches].args[j] = 0; } else if (strchr (switches_need_spaces, c)) { /* On some systems, ld cannot handle some options without a space. So split the option from its argument. */ char *part1 = xmalloc (2); part1[0] = c; part1[1] = '\0'; switches[n_switches].part1 = part1; switches[n_switches].args = xmalloc (2 * sizeof (const char *)); switches[n_switches].args[0] = xstrdup (p+1); switches[n_switches].args[1] = 0; } else switches[n_switches].args = 0; switches[n_switches].live_cond = SWITCH_OK; switches[n_switches].validated = 0; switches[n_switches].ordering = 0; /* These are always valid, since gcc.c itself understands them. */ if (!strcmp (p, "save-temps") || !strcmp (p, "static-libgcc") || !strcmp (p, "shared-libgcc") || !strcmp (p, "pipe")) switches[n_switches].validated = 1; else { char ch = switches[n_switches].part1[0]; if (ch == 'B') switches[n_switches].validated = 1; } n_switches++; } else { #ifdef HAVE_TARGET_OBJECT_SUFFIX argv[i] = convert_filename (argv[i], 0, access (argv[i], F_OK)); #endif if (strcmp (argv[i], "-") != 0 && access (argv[i], F_OK) < 0) { perror_with_name (argv[i]); error_count++; } else { infiles[n_infiles].language = spec_lang; infiles[n_infiles++].name = argv[i]; } } } if (n_infiles == last_language_n_infiles && spec_lang != 0) error ("warning: `-x %s' after last input file has no effect", spec_lang); /* Ensure we only invoke each subprocess once. */ if (target_help_flag || print_help_list) { n_infiles = 1; /* Create a dummy input file, so that we can pass --target-help on to the various sub-processes. */ infiles[0].language = "c"; infiles[0].name = "help-dummy"; if (target_help_flag) { switches[n_switches].part1 = "--target-help"; switches[n_switches].args = 0; switches[n_switches].live_cond = SWITCH_OK; switches[n_switches].validated = 0; n_switches++; } if (print_help_list) { switches[n_switches].part1 = "--help"; switches[n_switches].args = 0; switches[n_switches].live_cond = SWITCH_OK; switches[n_switches].validated = 0; n_switches++; } } switches[n_switches].part1 = 0; infiles[n_infiles].name = 0; } /* Store switches not filtered out by % 0 && !strcmp (argbuf[argbuf_index - 1], "|")) argbuf_index--; set_collect_gcc_options (); if (argbuf_index > 0) value = execute (); } return value; } static int do_spec_2 (const char *spec) { const char *string; int result; clear_args (); arg_going = 0; delete_this_arg = 0; this_is_output_file = 0; this_is_library_file = 0; input_from_pipe = 0; suffix_subst = NULL; result = do_spec_1 (spec, 0, NULL); /* End any pending argument. */ if (arg_going) { obstack_1grow (&obstack, 0); string = obstack_finish (&obstack); if (this_is_library_file) string = find_file (string); store_arg (string, delete_this_arg, this_is_output_file); if (this_is_output_file) outfiles[input_file_number] = string; arg_going = 0; } return result; } /* Process the given spec string and add any new options to the end of the switches/n_switches array. */ static void do_option_spec (const char *name, const char *spec) { unsigned int i, value_count, value_len; const char *p, *q, *value; char *tmp_spec, *tmp_spec_p; if (configure_default_options[0].name == NULL) return; for (i = 0; i < ARRAY_SIZE (configure_default_options); i++) if (strcmp (configure_default_options[i].name, name) == 0) break; if (i == ARRAY_SIZE (configure_default_options)) return; value = configure_default_options[i].value; value_len = strlen (value); /* Compute the size of the final spec. */ value_count = 0; p = spec; while ((p = strstr (p, "%(VALUE)")) != NULL) { p ++; value_count ++; } /* Replace each %(VALUE) by the specified value. */ tmp_spec = alloca (strlen (spec) + 1 + value_count * (value_len - strlen ("%(VALUE)"))); tmp_spec_p = tmp_spec; q = spec; while ((p = strstr (q, "%(VALUE)")) != NULL) { memcpy (tmp_spec_p, q, p - q); tmp_spec_p = tmp_spec_p + (p - q); memcpy (tmp_spec_p, value, value_len); tmp_spec_p += value_len; q = p + strlen ("%(VALUE)"); } strcpy (tmp_spec_p, q); do_self_spec (tmp_spec); } /* Process the given spec string and add any new options to the end of the switches/n_switches array. */ static void do_self_spec (const char *spec) { do_spec_2 (spec); do_spec_1 (" ", 0, NULL); if (argbuf_index > 0) { int i, first; first = n_switches; n_switches += argbuf_index; switches = xrealloc (switches, sizeof (struct switchstr) * (n_switches + 1)); switches[n_switches] = switches[first]; for (i = 0; i < argbuf_index; i++) { struct switchstr *sw; /* Each switch should start with '-'. */ if (argbuf[i][0] != '-') abort (); sw = &switches[i + first]; sw->part1 = &argbuf[i][1]; sw->args = 0; sw->live_cond = SWITCH_OK; sw->validated = 0; sw->ordering = 0; } } } /* Process the sub-spec SPEC as a portion of a larger spec. This is like processing a whole spec except that we do not initialize at the beginning and we do not supply a newline by default at the end. INSWITCH nonzero means don't process %-sequences in SPEC; in this case, % is treated as an ordinary character. This is used while substituting switches. INSWITCH nonzero also causes SPC not to terminate an argument. Value is zero unless a line was finished and the command on that line reported an error. */ static int do_spec_1 (const char *spec, int inswitch, const char *soft_matched_part) { const char *p = spec; int c; int i; const char *string; int value; while ((c = *p++)) /* If substituting a switch, treat all chars like letters. Otherwise, NL, SPC, TAB and % are special. */ switch (inswitch ? 'a' : c) { case '\n': /* End of line: finish any pending argument, then run the pending command if one has been started. */ if (arg_going) { obstack_1grow (&obstack, 0); string = obstack_finish (&obstack); if (this_is_library_file) string = find_file (string); store_arg (string, delete_this_arg, this_is_output_file); if (this_is_output_file) outfiles[input_file_number] = string; } arg_going = 0; if (argbuf_index > 0 && !strcmp (argbuf[argbuf_index - 1], "|")) { /* A `|' before the newline means use a pipe here, but only if -pipe was specified. Otherwise, execute now and don't pass the `|' as an arg. */ if (use_pipes) { input_from_pipe = 1; break; } else argbuf_index--; } set_collect_gcc_options (); if (argbuf_index > 0) { value = execute (); if (value) return value; } /* Reinitialize for a new command, and for a new argument. */ clear_args (); arg_going = 0; delete_this_arg = 0; this_is_output_file = 0; this_is_library_file = 0; input_from_pipe = 0; break; case '|': /* End any pending argument. */ if (arg_going) { obstack_1grow (&obstack, 0); string = obstack_finish (&obstack); if (this_is_library_file) string = find_file (string); store_arg (string, delete_this_arg, this_is_output_file); if (this_is_output_file) outfiles[input_file_number] = string; } /* Use pipe */ obstack_1grow (&obstack, c); arg_going = 1; break; case '\t': case ' ': /* Space or tab ends an argument if one is pending. */ if (arg_going) { obstack_1grow (&obstack, 0); string = obstack_finish (&obstack); if (this_is_library_file) string = find_file (string); store_arg (string, delete_this_arg, this_is_output_file); if (this_is_output_file) outfiles[input_file_number] = string; } /* Reinitialize for a new argument. */ arg_going = 0; delete_this_arg = 0; this_is_output_file = 0; this_is_library_file = 0; break; case '%': switch (c = *p++) { case 0: fatal ("invalid specification! Bug in cc"); case 'b': obstack_grow (&obstack, input_basename, basename_length); arg_going = 1; break; case 'B': obstack_grow (&obstack, input_basename, suffixed_basename_length); arg_going = 1; break; case 'd': delete_this_arg = 2; break; /* Dump out the directories specified with LIBRARY_PATH, followed by the absolute directories that we search for startfiles. */ case 'D': { struct prefix_list *pl = startfile_prefixes.plist; size_t bufsize = 100; char *buffer = xmalloc (bufsize); int idx; for (; pl; pl = pl->next) { #ifdef RELATIVE_PREFIX_NOT_LINKDIR /* Used on systems which record the specified -L dirs and use them to search for dynamic linking. */ /* Relative directories always come from -B, and it is better not to use them for searching at run time. In particular, stage1 loses. */ if (!IS_ABSOLUTE_PATH (pl->prefix)) continue; #endif /* Try subdirectory if there is one. */ if (multilib_dir != NULL || (pl->os_multilib && multilib_os_dir != NULL)) { const char *multi_dir; multi_dir = pl->os_multilib ? multilib_os_dir : multilib_dir; if (machine_suffix && multilib_dir) { if (strlen (pl->prefix) + strlen (machine_suffix) >= bufsize) bufsize = (strlen (pl->prefix) + strlen (machine_suffix)) * 2 + 1; buffer = xrealloc (buffer, bufsize); strcpy (buffer, pl->prefix); strcat (buffer, machine_suffix); if (is_directory (buffer, multilib_dir, 1)) { do_spec_1 ("-L", 0, NULL); #ifdef SPACE_AFTER_L_OPTION do_spec_1 (" ", 0, NULL); #endif do_spec_1 (buffer, 1, NULL); do_spec_1 (multilib_dir, 1, NULL); /* Make this a separate argument. */ do_spec_1 (" ", 0, NULL); } } if (!pl->require_machine_suffix) { if (is_directory (pl->prefix, multi_dir, 1)) { do_spec_1 ("-L", 0, NULL); #ifdef SPACE_AFTER_L_OPTION do_spec_1 (" ", 0, NULL); #endif do_spec_1 (pl->prefix, 1, NULL); do_spec_1 (multi_dir, 1, NULL); /* Make this a separate argument. */ do_spec_1 (" ", 0, NULL); } } } if (machine_suffix) { if (is_directory (pl->prefix, machine_suffix, 1)) { do_spec_1 ("-L", 0, NULL); #ifdef SPACE_AFTER_L_OPTION do_spec_1 (" ", 0, NULL); #endif do_spec_1 (pl->prefix, 1, NULL); /* Remove slash from machine_suffix. */ if (strlen (machine_suffix) >= bufsize) bufsize = strlen (machine_suffix) * 2 + 1; buffer = xrealloc (buffer, bufsize); strcpy (buffer, machine_suffix); idx = strlen (buffer); if (IS_DIR_SEPARATOR (buffer[idx - 1])) buffer[idx - 1] = 0; do_spec_1 (buffer, 1, NULL); /* Make this a separate argument. */ do_spec_1 (" ", 0, NULL); } } if (!pl->require_machine_suffix) { if (is_directory (pl->prefix, "", 1)) { do_spec_1 ("-L", 0, NULL); #ifdef SPACE_AFTER_L_OPTION do_spec_1 (" ", 0, NULL); #endif /* Remove slash from pl->prefix. */ if (strlen (pl->prefix) >= bufsize) bufsize = strlen (pl->prefix) * 2 + 1; buffer = xrealloc (buffer, bufsize); strcpy (buffer, pl->prefix); idx = strlen (buffer); if (IS_DIR_SEPARATOR (buffer[idx - 1])) buffer[idx - 1] = 0; do_spec_1 (buffer, 1, NULL); /* Make this a separate argument. */ do_spec_1 (" ", 0, NULL); } } } free (buffer); } break; case 'e': /* %efoo means report an error with `foo' as error message and don't execute any more commands for this file. */ { const char *q = p; char *buf; while (*p != 0 && *p != '\n') p++; buf = alloca (p - q + 1); strncpy (buf, q, p - q); buf[p - q] = 0; error ("%s", buf); return -1; } break; case 'n': /* %nfoo means report a notice with `foo' on stderr. */ { const char *q = p; char *buf; while (*p != 0 && *p != '\n') p++; buf = alloca (p - q + 1); strncpy (buf, q, p - q); buf[p - q] = 0; notice ("%s\n", buf); if (*p) p++; } break; case 'j': { struct stat st; /* If save_temps_flag is off, and the HOST_BIT_BUCKET is defined, and it is not a directory, and it is writable, use it. Otherwise, treat this like any other temporary file. */ if ((!save_temps_flag) && (stat (HOST_BIT_BUCKET, &st) == 0) && (!S_ISDIR (st.st_mode)) && (access (HOST_BIT_BUCKET, W_OK) == 0)) { obstack_grow (&obstack, HOST_BIT_BUCKET, strlen (HOST_BIT_BUCKET)); delete_this_arg = 0; arg_going = 1; break; } } goto create_temp_file; case '|': if (use_pipes) { obstack_1grow (&obstack, '-'); delete_this_arg = 0; arg_going = 1; /* consume suffix */ while (*p == '.' || ISALPHA ((unsigned char) *p)) p++; if (p[0] == '%' && p[1] == 'O') p += 2; break; } goto create_temp_file; case 'm': if (use_pipes) { /* consume suffix */ while (*p == '.' || ISALPHA ((unsigned char) *p)) p++; if (p[0] == '%' && p[1] == 'O') p += 2; break; } goto create_temp_file; case 'g': case 'u': case 'U': create_temp_file: { struct temp_name *t; int suffix_length; const char *suffix = p; char *saved_suffix = NULL; while (*p == '.' || ISALPHA ((unsigned char) *p)) p++; suffix_length = p - suffix; if (p[0] == '%' && p[1] == 'O') { p += 2; /* We don't support extra suffix characters after %O. */ if (*p == '.' || ISALPHA ((unsigned char) *p)) abort (); if (suffix_length == 0) suffix = TARGET_OBJECT_SUFFIX; else { saved_suffix = xmalloc (suffix_length + strlen (TARGET_OBJECT_SUFFIX)); strncpy (saved_suffix, suffix, suffix_length); strcpy (saved_suffix + suffix_length, TARGET_OBJECT_SUFFIX); } suffix_length += strlen (TARGET_OBJECT_SUFFIX); } /* If the input_filename has the same suffix specified for the %g, %u, or %U, and -save-temps is specified, we could end up using that file as an intermediate thus clobbering the user's source file (.e.g., gcc -save-temps foo.s would clobber foo.s with the output of cpp0). So check for this condition and generate a temp file as the intermediate. */ if (save_temps_flag) { temp_filename_length = basename_length + suffix_length; temp_filename = alloca (temp_filename_length + 1); strncpy ((char *) temp_filename, input_basename, basename_length); strncpy ((char *) temp_filename + basename_length, suffix, suffix_length); *((char *) temp_filename + temp_filename_length) = '\0'; if (strcmp (temp_filename, input_filename) != 0) { struct stat st_temp; /* Note, set_input() resets input_stat_set to 0. */ if (input_stat_set == 0) { input_stat_set = stat (input_filename, &input_stat); if (input_stat_set >= 0) input_stat_set = 1; } /* If we have the stat for the input_filename and we can do the stat for the temp_filename then the they could still refer to the same file if st_dev/st_ino's are the same. */ if (input_stat_set != 1 || stat (temp_filename, &st_temp) < 0 || input_stat.st_dev != st_temp.st_dev || input_stat.st_ino != st_temp.st_ino) { temp_filename = save_string (temp_filename, temp_filename_length + 1); obstack_grow (&obstack, temp_filename, temp_filename_length); arg_going = 1; delete_this_arg = 0; break; } } } /* See if we already have an association of %g/%u/%U and suffix. */ for (t = temp_names; t; t = t->next) if (t->length == suffix_length && strncmp (t->suffix, suffix, suffix_length) == 0 && t->unique == (c == 'u' || c == 'U' || c == 'j')) break; /* Make a new association if needed. %u and %j require one. */ if (t == 0 || c == 'u' || c == 'j') { if (t == 0) { t = xmalloc (sizeof (struct temp_name)); t->next = temp_names; temp_names = t; } t->length = suffix_length; if (saved_suffix) { t->suffix = saved_suffix; saved_suffix = NULL; } else t->suffix = save_string (suffix, suffix_length); t->unique = (c == 'u' || c == 'U' || c == 'j'); temp_filename = make_temp_file (t->suffix); temp_filename_length = strlen (temp_filename); t->filename = temp_filename; t->filename_length = temp_filename_length; } if (saved_suffix) free (saved_suffix); obstack_grow (&obstack, t->filename, t->filename_length); delete_this_arg = 1; } arg_going = 1; break; case 'i': if (combine_inputs) { for (i = 0; (int) i < n_infiles; i++) store_arg (infiles[i].name, 0, 0); } else { obstack_grow (&obstack, input_filename, input_filename_length); arg_going = 1; } break; case 'I': { struct prefix_list *pl = include_prefixes.plist; if (gcc_exec_prefix) { do_spec_1 ("-iprefix", 1, NULL); /* Make this a separate argument. */ do_spec_1 (" ", 0, NULL); do_spec_1 (gcc_exec_prefix, 1, NULL); do_spec_1 (" ", 0, NULL); } if (target_system_root_changed || (target_system_root && target_sysroot_hdrs_suffix)) { do_spec_1 ("-isysroot", 1, NULL); /* Make this a separate argument. */ do_spec_1 (" ", 0, NULL); do_spec_1 (target_system_root, 1, NULL); if (target_sysroot_hdrs_suffix) do_spec_1 (target_sysroot_hdrs_suffix, 1, NULL); do_spec_1 (" ", 0, NULL); } for (; pl; pl = pl->next) { do_spec_1 ("-isystem", 1, NULL); /* Make this a separate argument. */ do_spec_1 (" ", 0, NULL); do_spec_1 (pl->prefix, 1, NULL); do_spec_1 (" ", 0, NULL); } } break; case 'o': { int max = n_infiles; max += lang_specific_extra_outfiles; for (i = 0; i < max; i++) if (outfiles[i]) store_arg (outfiles[i], 0, 0); break; } case 'O': obstack_grow (&obstack, TARGET_OBJECT_SUFFIX, strlen (TARGET_OBJECT_SUFFIX)); arg_going = 1; break; case 's': this_is_library_file = 1; break; case 'V': outfiles[input_file_number] = NULL; break; case 'w': this_is_output_file = 1; break; case 'W': { int cur_index = argbuf_index; /* Handle the {...} following the %W. */ if (*p != '{') abort (); p = handle_braces (p + 1); if (p == 0) return -1; /* End any pending argument. */ if (arg_going) { obstack_1grow (&obstack, 0); string = obstack_finish (&obstack); if (this_is_library_file) string = find_file (string); store_arg (string, delete_this_arg, this_is_output_file); if (this_is_output_file) outfiles[input_file_number] = string; arg_going = 0; } /* If any args were output, mark the last one for deletion on failure. */ if (argbuf_index != cur_index) record_temp_file (argbuf[argbuf_index - 1], 0, 1); break; } /* %x{OPTION} records OPTION for %X to output. */ case 'x': { const char *p1 = p; char *string; /* Skip past the option value and make a copy. */ if (*p != '{') abort (); while (*p++ != '}') ; string = save_string (p1 + 1, p - p1 - 2); /* See if we already recorded this option. */ for (i = 0; i < n_linker_options; i++) if (! strcmp (string, linker_options[i])) { free (string); return 0; } /* This option is new; add it. */ add_linker_option (string, strlen (string)); } break; /* Dump out the options accumulated previously using %x. */ case 'X': for (i = 0; i < n_linker_options; i++) { do_spec_1 (linker_options[i], 1, NULL); /* Make each accumulated option a separate argument. */ do_spec_1 (" ", 0, NULL); } break; /* Dump out the options accumulated previously using -Wa,. */ case 'Y': for (i = 0; i < n_assembler_options; i++) { do_spec_1 (assembler_options[i], 1, NULL); /* Make each accumulated option a separate argument. */ do_spec_1 (" ", 0, NULL); } break; /* Dump out the options accumulated previously using -Wp,. */ case 'Z': for (i = 0; i < n_preprocessor_options; i++) { do_spec_1 (preprocessor_options[i], 1, NULL); /* Make each accumulated option a separate argument. */ do_spec_1 (" ", 0, NULL); } break; /* Here are digits and numbers that just process a certain constant string as a spec. */ case '1': value = do_spec_1 (cc1_spec, 0, NULL); if (value != 0) return value; break; case '2': value = do_spec_1 (cc1plus_spec, 0, NULL); if (value != 0) return value; break; case 'a': value = do_spec_1 (asm_spec, 0, NULL); if (value != 0) return value; break; case 'A': value = do_spec_1 (asm_final_spec, 0, NULL); if (value != 0) return value; break; case 'C': { const char *const spec = (input_file_compiler->cpp_spec ? input_file_compiler->cpp_spec : cpp_spec); value = do_spec_1 (spec, 0, NULL); if (value != 0) return value; } break; case 'E': value = do_spec_1 (endfile_spec, 0, NULL); if (value != 0) return value; break; case 'l': value = do_spec_1 (link_spec, 0, NULL); if (value != 0) return value; break; case 'L': value = do_spec_1 (lib_spec, 0, NULL); if (value != 0) return value; break; case 'G': value = do_spec_1 (libgcc_spec, 0, NULL); if (value != 0) return value; break; case 'M': if (multilib_dir && strcmp (multilib_dir, ".") != 0) { char *p; const char *q; size_t len; len = strlen (multilib_dir); obstack_blank (&obstack, len + 1); p = obstack_next_free (&obstack) - (len + 1); *p++ = '_'; for (q = multilib_dir; *q ; ++q, ++p) *p = (IS_DIR_SEPARATOR (*q) ? '_' : *q); } break; case 'R': /* We assume there is a directory separator at the end of this string. */ if (target_system_root) { obstack_grow (&obstack, target_system_root, strlen (target_system_root)); if (target_sysroot_suffix) obstack_grow (&obstack, target_sysroot_suffix, strlen (target_sysroot_suffix)); } break; case 'S': value = do_spec_1 (startfile_spec, 0, NULL); if (value != 0) return value; break; /* Here we define characters other than letters and digits. */ case '{': p = handle_braces (p); if (p == 0) return -1; break; case ':': p = handle_spec_function (p); if (p == 0) return -1; break; case '%': obstack_1grow (&obstack, '%'); break; case '.': { unsigned len = 0; while (p[len] && p[len] != ' ' && p[len] != '%') len++; suffix_subst = save_string (p - 1, len + 1); p += len; } break; /* Henceforth ignore the option(s) matching the pattern after the %<. */ case '<': { unsigned len = 0; int have_wildcard = 0; int i; while (p[len] && p[len] != ' ' && p[len] != '\t') len++; if (p[len-1] == '*') have_wildcard = 1; for (i = 0; i < n_switches; i++) if (!strncmp (switches[i].part1, p, len - have_wildcard) && (have_wildcard || switches[i].part1[len] == '\0')) { switches[i].live_cond = SWITCH_IGNORE; switches[i].validated = 1; } p += len; } break; case '*': if (soft_matched_part) { do_spec_1 (soft_matched_part, 1, NULL); do_spec_1 (" ", 0, NULL); } else /* Catch the case where a spec string contains something like '%{foo:%*}'. ie there is no * in the pattern on the left hand side of the :. */ error ("spec failure: '%%*' has not been initialized by pattern match"); break; /* Process a string found as the value of a spec given by name. This feature allows individual machine descriptions to add and use their own specs. %[...] modifies -D options the way %P does; %(...) uses the spec unmodified. */ case '[': error ("warning: use of obsolete %%[ operator in specs"); case '(': { const char *name = p; struct spec_list *sl; int len; /* The string after the S/P is the name of a spec that is to be processed. */ while (*p && *p != ')' && *p != ']') p++; /* See if it's in the list. */ for (len = p - name, sl = specs; sl; sl = sl->next) if (sl->name_len == len && !strncmp (sl->name, name, len)) { name = *(sl->ptr_spec); #ifdef DEBUG_SPECS notice ("Processing spec %c%s%c, which is '%s'\n", c, sl->name, (c == '(') ? ')' : ']', name); #endif break; } if (sl) { if (c == '(') { value = do_spec_1 (name, 0, NULL); if (value != 0) return value; } else { char *x = alloca (strlen (name) * 2 + 1); char *buf = x; const char *y = name; int flag = 0; /* Copy all of NAME into BUF, but put __ after every -D and at the end of each arg. */ while (1) { if (! strncmp (y, "-D", 2)) { *x++ = '-'; *x++ = 'D'; *x++ = '_'; *x++ = '_'; y += 2; flag = 1; continue; } else if (flag && (*y == ' ' || *y == '\t' || *y == '=' || *y == '}' || *y == 0)) { *x++ = '_'; *x++ = '_'; flag = 0; } if (*y == 0) break; else *x++ = *y++; } *x = 0; value = do_spec_1 (buf, 0, NULL); if (value != 0) return value; } } /* Discard the closing paren or bracket. */ if (*p) p++; } break; default: error ("spec failure: unrecognized spec option '%c'", c); break; } break; case '\\': /* Backslash: treat next character as ordinary. */ c = *p++; /* Fall through. */ default: /* Ordinary character: put it into the current argument. */ obstack_1grow (&obstack, c); arg_going = 1; } /* End of string. If we are processing a spec function, we need to end any pending argument. */ if (processing_spec_function && arg_going) { obstack_1grow (&obstack, 0); string = obstack_finish (&obstack); if (this_is_library_file) string = find_file (string); store_arg (string, delete_this_arg, this_is_output_file); if (this_is_output_file) outfiles[input_file_number] = string; arg_going = 0; } return 0; } /* Look up a spec function. */ static const struct spec_function * lookup_spec_function (const char *name) { static const struct spec_function * const spec_function_tables[] = { static_spec_functions, lang_specific_spec_functions, }; const struct spec_function *sf; unsigned int i; for (i = 0; i < ARRAY_SIZE (spec_function_tables); i++) { for (sf = spec_function_tables[i]; sf->name != NULL; sf++) if (strcmp (sf->name, name) == 0) return sf; } return NULL; } /* Evaluate a spec function. */ static const char * eval_spec_function (const char *func, const char *args) { const struct spec_function *sf; const char *funcval; /* Saved spec processing context. */ int save_argbuf_index; int save_argbuf_length; const char **save_argbuf; int save_arg_going; int save_delete_this_arg; int save_this_is_output_file; int save_this_is_library_file; int save_input_from_pipe; const char *save_suffix_subst; sf = lookup_spec_function (func); if (sf == NULL) fatal ("unknown spec function `%s'", func); /* Push the spec processing context. */ save_argbuf_index = argbuf_index; save_argbuf_length = argbuf_length; save_argbuf = argbuf; save_arg_going = arg_going; save_delete_this_arg = delete_this_arg; save_this_is_output_file = this_is_output_file; save_this_is_library_file = this_is_library_file; save_input_from_pipe = input_from_pipe; save_suffix_subst = suffix_subst; /* Create a new spec processing context, and build the function arguments. */ alloc_args (); if (do_spec_2 (args) < 0) fatal ("error in args to spec function `%s'", func); /* argbuf_index is an index for the next argument to be inserted, and so contains the count of the args already inserted. */ funcval = (*sf->func) (argbuf_index, argbuf); /* Pop the spec processing context. */ argbuf_index = save_argbuf_index; argbuf_length = save_argbuf_length; free (argbuf); argbuf = save_argbuf; arg_going = save_arg_going; delete_this_arg = save_delete_this_arg; this_is_output_file = save_this_is_output_file; this_is_library_file = save_this_is_library_file; input_from_pipe = save_input_from_pipe; suffix_subst = save_suffix_subst; return funcval; } /* Handle a spec function call of the form: %:function(args) ARGS is processed as a spec in a separate context and split into an argument vector in the normal fashion. The function returns a string containing a spec which we then process in the caller's context, or NULL if no processing is required. */ static const char * handle_spec_function (const char *p) { char *func, *args; const char *endp, *funcval; int count; processing_spec_function++; /* Get the function name. */ for (endp = p; *endp != '\0'; endp++) { if (*endp == '(') /* ) */ break; /* Only allow [A-Za-z0-9], -, and _ in function names. */ if (!ISALNUM (*endp) && !(*endp == '-' || *endp == '_')) fatal ("malformed spec function name"); } if (*endp != '(') /* ) */ fatal ("no arguments for spec function"); func = save_string (p, endp - p); p = ++endp; /* Get the arguments. */ for (count = 0; *endp != '\0'; endp++) { /* ( */ if (*endp == ')') { if (count == 0) break; count--; } else if (*endp == '(') /* ) */ count++; } /* ( */ if (*endp != ')') fatal ("malformed spec function arguments"); args = save_string (p, endp - p); p = ++endp; /* p now points to just past the end of the spec function expression. */ funcval = eval_spec_function (func, args); if (funcval != NULL && do_spec_1 (funcval, 0, NULL) < 0) p = NULL; free (func); free (args); processing_spec_function--; return p; } /* Inline subroutine of handle_braces. Returns true if the current input suffix matches the atom bracketed by ATOM and END_ATOM. */ static inline bool input_suffix_matches (const char *atom, const char *end_atom) { return (input_suffix && !strncmp (input_suffix, atom, end_atom - atom) && input_suffix[end_atom - atom] == '\0'); } /* Inline subroutine of handle_braces. Returns true if a switch matching the atom bracketed by ATOM and END_ATOM appeared on the command line. */ static inline bool switch_matches (const char *atom, const char *end_atom, int starred) { int i; int len = end_atom - atom; int plen = starred ? len : -1; for (i = 0; i < n_switches; i++) if (!strncmp (switches[i].part1, atom, len) && (starred || switches[i].part1[len] == '\0') && check_live_switch (i, plen)) return true; return false; } /* Inline subroutine of handle_braces. Mark all of the switches which match ATOM (extends to END_ATOM; STARRED indicates whether there was a star after the atom) for later processing. */ static inline void mark_matching_switches (const char *atom, const char *end_atom, int starred) { int i; int len = end_atom - atom; int plen = starred ? len : -1; for (i = 0; i < n_switches; i++) if (!strncmp (switches[i].part1, atom, len) && (starred || switches[i].part1[len] == '\0') && check_live_switch (i, plen)) switches[i].ordering = 1; } /* Inline subroutine of handle_braces. Process all the currently marked switches through give_switch, and clear the marks. */ static inline void process_marked_switches (void) { int i; for (i = 0; i < n_switches; i++) if (switches[i].ordering == 1) { switches[i].ordering = 0; give_switch (i, 0); } } /* Handle a %{ ... } construct. P points just inside the leading {. Returns a pointer one past the end of the brace block, or 0 if we call do_spec_1 and that returns -1. */ static const char * handle_braces (const char *p) { const char *atom, *end_atom; const char *d_atom = NULL, *d_end_atom = NULL; bool a_is_suffix; bool a_is_starred; bool a_is_negated; bool a_matched; bool a_must_be_last = false; bool ordered_set = false; bool disjunct_set = false; bool disj_matched = false; bool disj_starred = true; bool n_way_choice = false; bool n_way_matched = false; #define SKIP_WHITE() do { while (*p == ' ' || *p == '\t') p++; } while (0) do { if (a_must_be_last) abort (); /* Scan one "atom" (S in the description above of %{}, possibly with !, ., or * modifiers). */ a_matched = a_is_suffix = a_is_starred = a_is_negated = false; SKIP_WHITE(); if (*p == '!') p++, a_is_negated = true; SKIP_WHITE(); if (*p == '.') p++, a_is_suffix = true; atom = p; while (ISIDNUM(*p) || *p == '-' || *p == '+' || *p == '=' || *p == ',' || *p == '.' || *p == '@') p++; end_atom = p; if (*p == '*') p++, a_is_starred = 1; SKIP_WHITE(); if (*p == '&' || *p == '}') { /* Substitute the switch(es) indicated by the current atom. */ ordered_set = true; if (disjunct_set || n_way_choice || a_is_negated || a_is_suffix || atom == end_atom) abort (); mark_matching_switches (atom, end_atom, a_is_starred); if (*p == '}') process_marked_switches (); } else if (*p == '|' || *p == ':') { /* Substitute some text if the current atom appears as a switch or suffix. */ disjunct_set = true; if (ordered_set) abort (); if (atom == end_atom) { if (!n_way_choice || disj_matched || *p == '|' || a_is_negated || a_is_suffix || a_is_starred) abort (); /* An empty term may appear as the last choice of an N-way choice set; it means "otherwise". */ a_must_be_last = true; disj_matched = !n_way_matched; disj_starred = false; } else { if (a_is_suffix && a_is_starred) abort (); if (!a_is_starred) disj_starred = false; /* Don't bother testing this atom if we already have a match. */ if (!disj_matched && !n_way_matched) { if (a_is_suffix) a_matched = input_suffix_matches (atom, end_atom); else a_matched = switch_matches (atom, end_atom, a_is_starred); if (a_matched != a_is_negated) { disj_matched = true; d_atom = atom; d_end_atom = end_atom; } } } if (*p == ':') { /* Found the body, that is, the text to substitute if the current disjunction matches. */ p = process_brace_body (p + 1, d_atom, d_end_atom, disj_starred, disj_matched && !n_way_matched); if (p == 0) return 0; /* If we have an N-way choice, reset state for the next disjunction. */ if (*p == ';') { n_way_choice = true; n_way_matched |= disj_matched; disj_matched = false; disj_starred = true; d_atom = d_end_atom = NULL; } } } else abort (); } while (*p++ != '}'); return p; #undef SKIP_WHITE } /* Subroutine of handle_braces. Scan and process a brace substitution body (X in the description of %{} syntax). P points one past the colon; ATOM and END_ATOM bracket the first atom which was found to be true (present) in the current disjunction; STARRED indicates whether all the atoms in the current disjunction were starred (for syntax validation); MATCHED indicates whether the disjunction matched or not, and therefore whether or not the body is to be processed through do_spec_1 or just skipped. Returns a pointer to the closing } or ;, or 0 if do_spec_1 returns -1. */ static const char * process_brace_body (const char *p, const char *atom, const char *end_atom, int starred, int matched) { const char *body, *end_body; unsigned int nesting_level; bool have_subst = false; /* Locate the closing } or ;, honoring nested braces. Trim trailing whitespace. */ body = p; nesting_level = 1; for (;;) { if (*p == '{') nesting_level++; else if (*p == '}') { if (!--nesting_level) break; } else if (*p == ';' && nesting_level == 1) break; else if (*p == '%' && p[1] == '*' && nesting_level == 1) have_subst = true; else if (*p == '\0') abort (); p++; } end_body = p; while (end_body[-1] == ' ' || end_body[-1] == '\t') end_body--; if (have_subst && !starred) abort (); if (matched) { /* Copy the substitution body to permanent storage and execute it. If have_subst is false, this is a simple matter of running the body through do_spec_1... */ char *string = save_string (body, end_body - body); if (!have_subst) { if (do_spec_1 (string, 0, NULL) < 0) return 0; } else { /* ... but if have_subst is true, we have to process the body once for each matching switch, with %* set to the variant part of the switch. */ unsigned int hard_match_len = end_atom - atom; int i; for (i = 0; i < n_switches; i++) if (!strncmp (switches[i].part1, atom, hard_match_len) && check_live_switch (i, hard_match_len)) { if (do_spec_1 (string, 0, &switches[i].part1[hard_match_len]) < 0) return 0; /* Pass any arguments this switch has. */ give_switch (i, 1); suffix_subst = NULL; } } } return p; } /* Return 0 iff switch number SWITCHNUM is obsoleted by a later switch on the command line. PREFIX_LENGTH is the length of XXX in an {XXX*} spec, or -1 if either exact match or %* is used. A -O switch is obsoleted by a later -O switch. A -f, -m, or -W switch whose value does not begin with "no-" is obsoleted by the same value with the "no-", similarly for a switch with the "no-" prefix. */ static int check_live_switch (int switchnum, int prefix_length) { const char *name = switches[switchnum].part1; int i; /* In the common case of {*}, a negating switch would always match, so ignore that case. We will just send the conflicting switches to the compiler phase. */ if (prefix_length >= 0 && prefix_length <= 1) return 1; /* If we already processed this switch and determined if it was live or not, return our past determination. */ if (switches[switchnum].live_cond != 0) return switches[switchnum].live_cond > 0; /* Now search for duplicate in a manner that depends on the name. */ switch (*name) { case 'O': for (i = switchnum + 1; i < n_switches; i++) if (switches[i].part1[0] == 'O') { switches[switchnum].validated = 1; switches[switchnum].live_cond = SWITCH_FALSE; return 0; } break; case 'W': case 'f': case 'm': if (! strncmp (name + 1, "no-", 3)) { /* We have Xno-YYY, search for XYYY. */ for (i = switchnum + 1; i < n_switches; i++) if (switches[i].part1[0] == name[0] && ! strcmp (&switches[i].part1[1], &name[4])) { switches[switchnum].validated = 1; switches[switchnum].live_cond = SWITCH_FALSE; return 0; } } else { /* We have XYYY, search for Xno-YYY. */ for (i = switchnum + 1; i < n_switches; i++) if (switches[i].part1[0] == name[0] && switches[i].part1[1] == 'n' && switches[i].part1[2] == 'o' && switches[i].part1[3] == '-' && !strcmp (&switches[i].part1[4], &name[1])) { switches[switchnum].validated = 1; switches[switchnum].live_cond = SWITCH_FALSE; return 0; } } break; } /* Otherwise the switch is live. */ switches[switchnum].live_cond = SWITCH_LIVE; return 1; } /* Pass a switch to the current accumulating command in the same form that we received it. SWITCHNUM identifies the switch; it is an index into the vector of switches gcc received, which is `switches'. This cannot fail since it never finishes a command line. If OMIT_FIRST_WORD is nonzero, then we omit .part1 of the argument. */ static void give_switch (int switchnum, int omit_first_word) { if (switches[switchnum].live_cond == SWITCH_IGNORE) return; if (!omit_first_word) { do_spec_1 ("-", 0, NULL); do_spec_1 (switches[switchnum].part1, 1, NULL); } if (switches[switchnum].args != 0) { const char **p; for (p = switches[switchnum].args; *p; p++) { const char *arg = *p; do_spec_1 (" ", 0, NULL); if (suffix_subst) { unsigned length = strlen (arg); int dot = 0; while (length-- && !IS_DIR_SEPARATOR (arg[length])) if (arg[length] == '.') { ((char *)arg)[length] = 0; dot = 1; break; } do_spec_1 (arg, 1, NULL); if (dot) ((char *)arg)[length] = '.'; do_spec_1 (suffix_subst, 1, NULL); } else do_spec_1 (arg, 1, NULL); } } do_spec_1 (" ", 0, NULL); switches[switchnum].validated = 1; } /* Search for a file named NAME trying various prefixes including the user's -B prefix and some standard ones. Return the absolute file name found. If nothing is found, return NAME. */ static const char * find_file (const char *name) { char *newname; /* Try multilib_dir if it is defined. */ if (multilib_os_dir != NULL) { newname = find_a_file (&startfile_prefixes, name, R_OK, 1); /* If we don't find it in the multi library dir, then fall through and look for it in the normal places. */ if (newname != NULL) return newname; } newname = find_a_file (&startfile_prefixes, name, R_OK, 0); return newname ? newname : name; } /* Determine whether a directory exists. If LINKER, return 0 for certain fixed names not needed by the linker. If not LINKER, it is only important to return 0 if the host machine has a small ARG_MAX limit. */ static int is_directory (const char *path1, const char *path2, int linker) { int len1 = strlen (path1); int len2 = strlen (path2); char *path = alloca (3 + len1 + len2); char *cp; struct stat st; #ifndef SMALL_ARG_MAX if (! linker) return 1; #endif /* Construct the path from the two parts. Ensure the string ends with "/.". The resulting path will be a directory even if the given path is a symbolic link. */ memcpy (path, path1, len1); memcpy (path + len1, path2, len2); cp = path + len1 + len2; if (!IS_DIR_SEPARATOR (cp[-1])) *cp++ = DIR_SEPARATOR; *cp++ = '.'; *cp = '\0'; #ifndef FREEBSD_NATIVE /* Exclude directories that the linker is known to search. */ if (linker && ((cp - path == 6 && strcmp (path, concat (dir_separator_str, "lib", dir_separator_str, ".", NULL)) == 0) || (cp - path == 10 && strcmp (path, concat (dir_separator_str, "usr", dir_separator_str, "lib", dir_separator_str, ".", NULL)) == 0))) return 0; #endif /* FREEBSD_NATIVE */ return (stat (path, &st) >= 0 && S_ISDIR (st.st_mode)); } /* Set up the various global variables to indicate that we're processing the input file named FILENAME. */ void set_input (const char *filename) { const char *p; input_filename = filename; input_filename_length = strlen (input_filename); input_basename = input_filename; #ifdef HAVE_DOS_BASED_FILE_SYSTEM /* Skip drive name so 'x:foo' is handled properly. */ if (input_basename[1] == ':') input_basename += 2; #endif for (p = input_basename; *p; p++) if (IS_DIR_SEPARATOR (*p)) input_basename = p + 1; /* Find a suffix starting with the last period, and set basename_length to exclude that suffix. */ basename_length = strlen (input_basename); suffixed_basename_length = basename_length; p = input_basename + basename_length; while (p != input_basename && *p != '.') --p; if (*p == '.' && p != input_basename) { basename_length = p - input_basename; input_suffix = p + 1; } else input_suffix = ""; /* If a spec for 'g', 'u', or 'U' is seen with -save-temps then we will need to do a stat on the input_filename. The INPUT_STAT_SET signals that the stat is needed. */ input_stat_set = 0; } /* On fatal signals, delete all the temporary files. */ static void fatal_error (int signum) { signal (signum, SIG_DFL); delete_failure_queue (); delete_temp_files (); /* Get the same signal again, this time not handled, so its normal effect occurs. */ kill (getpid (), signum); } extern int main (int, const char **); int main (int argc, const char **argv) { size_t i; int value; int linker_was_run = 0; int num_linker_inputs = 0; char *explicit_link_files; char *specs_file; const char *p; struct user_specs *uptr; p = argv[0] + strlen (argv[0]); while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1])) --p; programname = p; xmalloc_set_program_name (programname); #ifdef GCC_DRIVER_HOST_INITIALIZATION /* Perform host dependent initialization when needed. */ GCC_DRIVER_HOST_INITIALIZATION; #endif gcc_init_libintl (); if (signal (SIGINT, SIG_IGN) != SIG_IGN) signal (SIGINT, fatal_error); #ifdef SIGHUP if (signal (SIGHUP, SIG_IGN) != SIG_IGN) signal (SIGHUP, fatal_error); #endif if (signal (SIGTERM, SIG_IGN) != SIG_IGN) signal (SIGTERM, fatal_error); #ifdef SIGPIPE if (signal (SIGPIPE, SIG_IGN) != SIG_IGN) signal (SIGPIPE, fatal_error); #endif #ifdef SIGCHLD /* We *MUST* set SIGCHLD to SIG_DFL so that the wait4() call will receive the signal. A different setting is inheritable */ signal (SIGCHLD, SIG_DFL); #endif /* Allocate the argument vector. */ alloc_args (); obstack_init (&obstack); /* Build multilib_select, et. al from the separate lines that make up each multilib selection. */ { const char *const *q = multilib_raw; int need_space; obstack_init (&multilib_obstack); while ((p = *q++) != (char *) 0) obstack_grow (&multilib_obstack, p, strlen (p)); obstack_1grow (&multilib_obstack, 0); multilib_select = obstack_finish (&multilib_obstack); q = multilib_matches_raw; while ((p = *q++) != (char *) 0) obstack_grow (&multilib_obstack, p, strlen (p)); obstack_1grow (&multilib_obstack, 0); multilib_matches = obstack_finish (&multilib_obstack); q = multilib_exclusions_raw; while ((p = *q++) != (char *) 0) obstack_grow (&multilib_obstack, p, strlen (p)); obstack_1grow (&multilib_obstack, 0); multilib_exclusions = obstack_finish (&multilib_obstack); need_space = FALSE; for (i = 0; i < ARRAY_SIZE (multilib_defaults_raw); i++) { if (need_space) obstack_1grow (&multilib_obstack, ' '); obstack_grow (&multilib_obstack, multilib_defaults_raw[i], strlen (multilib_defaults_raw[i])); need_space = TRUE; } obstack_1grow (&multilib_obstack, 0); multilib_defaults = obstack_finish (&multilib_obstack); } /* Set up to remember the pathname of gcc and any options needed for collect. We use argv[0] instead of programname because we need the complete pathname. */ obstack_init (&collect_obstack); obstack_grow (&collect_obstack, "COLLECT_GCC=", sizeof ("COLLECT_GCC=") - 1); obstack_grow (&collect_obstack, argv[0], strlen (argv[0]) + 1); putenv (obstack_finish (&collect_obstack)); #ifdef INIT_ENVIRONMENT /* Set up any other necessary machine specific environment variables. */ putenv (INIT_ENVIRONMENT); #endif /* Make a table of what switches there are (switches, n_switches). Make a table of specified input files (infiles, n_infiles). Decode switches that are handled locally. */ process_command (argc, argv); /* Initialize the vector of specs to just the default. This means one element containing 0s, as a terminator. */ compilers = xmalloc (sizeof default_compilers); memcpy (compilers, default_compilers, sizeof default_compilers); n_compilers = n_default_compilers; /* Read specs from a file if there is one. */ #ifdef FREEBSD_NATIVE just_machine_suffix = ""; #else /* FREEBSD_NATIVE */ machine_suffix = concat (spec_machine, dir_separator_str, spec_version, dir_separator_str, NULL); just_machine_suffix = concat (spec_machine, dir_separator_str, NULL); #endif /* FREEBSD_NATIVE */ specs_file = find_a_file (&startfile_prefixes, "specs", R_OK, 0); /* Read the specs file unless it is a default one. */ if (specs_file != 0 && strcmp (specs_file, "specs")) read_specs (specs_file, TRUE); else init_spec (); /* We need to check standard_exec_prefix/just_machine_suffix/specs for any override of as, ld and libraries. */ specs_file = (char *) alloca (strlen (FBSD_DATA_PREFIX) + strlen (just_machine_suffix) + sizeof ("specs")); strcpy (specs_file, FBSD_DATA_PREFIX); strcat (specs_file, just_machine_suffix); strcat (specs_file, "specs"); if (access (specs_file, R_OK) == 0) read_specs (specs_file, TRUE); /* Process any configure-time defaults specified for the command line options, via OPTION_DEFAULT_SPECS. */ for (i = 0; i < ARRAY_SIZE (option_default_specs); i++) do_option_spec (option_default_specs[i].name, option_default_specs[i].spec); /* Process DRIVER_SELF_SPECS, adding any new options to the end of the command line. */ for (i = 0; i < ARRAY_SIZE (driver_self_specs); i++) do_self_spec (driver_self_specs[i]); /* If not cross-compiling, look for executables in the standard places. */ if (*cross_compile == '0') { if (*md_exec_prefix) { add_prefix (&exec_prefixes, md_exec_prefix, "GCC", PREFIX_PRIORITY_LAST, 0, NULL, 0); } } /* Process sysroot_suffix_spec. */ if (*sysroot_suffix_spec != 0 && do_spec_2 (sysroot_suffix_spec) == 0) { if (argbuf_index > 1) error ("spec failure: more than one arg to SYSROOT_SUFFIX_SPEC."); else if (argbuf_index == 1) target_sysroot_suffix = xstrdup (argbuf[argbuf_index -1]); } /* Process sysroot_hdrs_suffix_spec. */ if (*sysroot_hdrs_suffix_spec != 0 && do_spec_2 (sysroot_hdrs_suffix_spec) == 0) { if (argbuf_index > 1) error ("spec failure: more than one arg to SYSROOT_HEADERS_SUFFIX_SPEC."); else if (argbuf_index == 1) target_sysroot_hdrs_suffix = xstrdup (argbuf[argbuf_index -1]); } /* Look for startfiles in the standard places. */ if (*startfile_prefix_spec != 0 && do_spec_2 (startfile_prefix_spec) == 0 && do_spec_1 (" ", 0, NULL) == 0) { int ndx; for (ndx = 0; ndx < argbuf_index; ndx++) add_sysrooted_prefix (&startfile_prefixes, argbuf[ndx], "BINUTILS", PREFIX_PRIORITY_LAST, 0, NULL, 1); } /* We should eventually get rid of all these and stick to startfile_prefix_spec exclusively. */ else if (*cross_compile == '0' || target_system_root) { if (*md_exec_prefix) add_sysrooted_prefix (&startfile_prefixes, md_exec_prefix, "GCC", PREFIX_PRIORITY_LAST, 0, NULL, 1); if (*md_startfile_prefix) add_sysrooted_prefix (&startfile_prefixes, md_startfile_prefix, "GCC", PREFIX_PRIORITY_LAST, 0, NULL, 1); if (*md_startfile_prefix_1) add_sysrooted_prefix (&startfile_prefixes, md_startfile_prefix_1, "GCC", PREFIX_PRIORITY_LAST, 0, NULL, 1); /* If standard_startfile_prefix is relative, base it on standard_exec_prefix. This lets us move the installed tree as a unit. If GCC_EXEC_PREFIX is defined, base standard_startfile_prefix on that as well. If the prefix is relative, only search it for native compilers; otherwise we will search a directory containing host libraries. */ if (IS_ABSOLUTE_PATH (standard_startfile_prefix)) add_sysrooted_prefix (&startfile_prefixes, standard_startfile_prefix, "BINUTILS", PREFIX_PRIORITY_LAST, 0, NULL, 1); else if (*cross_compile == '0') { if (gcc_exec_prefix) add_prefix (&startfile_prefixes, concat (gcc_exec_prefix, machine_suffix, standard_startfile_prefix, NULL), NULL, PREFIX_PRIORITY_LAST, 0, NULL, 1); add_prefix (&startfile_prefixes, concat (standard_exec_prefix, machine_suffix, standard_startfile_prefix, NULL), NULL, PREFIX_PRIORITY_LAST, 0, NULL, 1); } #ifndef FREEBSD_NATIVE add_sysrooted_prefix (&startfile_prefixes, standard_startfile_prefix_1, "BINUTILS", PREFIX_PRIORITY_LAST, 0, NULL, 1); add_sysrooted_prefix (&startfile_prefixes, standard_startfile_prefix_2, "BINUTILS", PREFIX_PRIORITY_LAST, 0, NULL, 1); #endif /* not FREEBSD_NATIVE */ #if 0 /* Can cause surprises, and one can use -B./ instead. */ add_prefix (&startfile_prefixes, "./", NULL, PREFIX_PRIORITY_LAST, 1, NULL, 0); #endif } /* Process any user specified specs in the order given on the command line. */ for (uptr = user_specs_head; uptr; uptr = uptr->next) { char *filename = find_a_file (&startfile_prefixes, uptr->filename, R_OK, 0); read_specs (filename ? filename : uptr->filename, FALSE); } /* If we have a GCC_EXEC_PREFIX envvar, modify it for cpp's sake. */ if (gcc_exec_prefix) gcc_exec_prefix = concat (gcc_exec_prefix, spec_machine, dir_separator_str, spec_version, dir_separator_str, NULL); /* Now we have the specs. Set the `valid' bits for switches that match anything in any spec. */ validate_all_switches (); /* Now that we have the switches and the specs, set the subdirectory based on the options. */ set_multilib_dir (); /* Warn about any switches that no pass was interested in. */ for (i = 0; (int) i < n_switches; i++) if (! switches[i].validated) error ("unrecognized option `-%s'", switches[i].part1); /* Obey some of the options. */ if (print_search_dirs) { printf (_("install: %s%s\n"), standard_exec_prefix, machine_suffix); printf (_("programs: %s\n"), build_search_list (&exec_prefixes, "", 0)); printf (_("libraries: %s\n"), build_search_list (&startfile_prefixes, "", 0)); return (0); } if (print_file_name) { printf ("%s\n", find_file (print_file_name)); return (0); } if (print_prog_name) { char *newname = find_a_file (&exec_prefixes, print_prog_name, X_OK, 0); printf ("%s\n", (newname ? newname : print_prog_name)); return (0); } if (print_multi_lib) { print_multilib_info (); return (0); } if (print_multi_directory) { if (multilib_dir == NULL) printf (".\n"); else printf ("%s\n", multilib_dir); return (0); } if (print_multi_os_directory) { if (multilib_os_dir == NULL) printf (".\n"); else printf ("%s\n", multilib_os_dir); return (0); } if (target_help_flag) { /* Print if any target specific options. */ /* We do not exit here. Instead we have created a fake input file called 'target-dummy' which needs to be compiled, and we pass this on to the various sub-processes, along with the --target-help switch. */ } if (print_help_list) { display_help (); if (! verbose_flag) { printf (_("\nFor bug reporting instructions, please see:\n")); printf ("%s.\n", bug_report_url); return (0); } /* We do not exit here. Instead we have created a fake input file called 'help-dummy' which needs to be compiled, and we pass this on the various sub-processes, along with the --help switch. */ } if (verbose_flag) { int n; const char *thrmod; notice ("Configured with: %s\n", configuration_arguments); #ifdef THREAD_MODEL_SPEC /* We could have defined THREAD_MODEL_SPEC to "%*" by default, but there's no point in doing all this processing just to get thread_model back. */ obstack_init (&obstack); do_spec_1 (THREAD_MODEL_SPEC, 0, thread_model); obstack_1grow (&obstack, '\0'); thrmod = obstack_finish (&obstack); #else thrmod = thread_model; #endif notice ("Thread model: %s\n", thrmod); /* compiler_version is truncated at the first space when initialized from version string, so truncate version_string at the first space before comparing. */ for (n = 0; version_string[n]; n++) if (version_string[n] == ' ') break; if (! strncmp (version_string, compiler_version, n) && compiler_version[n] == 0) notice ("gcc version %s\n", version_string); else notice ("gcc driver version %s executing gcc version %s\n", version_string, compiler_version); if (n_infiles == 0) return (0); } if (n_infiles == added_libraries) fatal ("No input files specified"); /* Make a place to record the compiler output file names that correspond to the input files. */ i = n_infiles; i += lang_specific_extra_outfiles; outfiles = xcalloc (i, sizeof (char *)); /* Record which files were specified explicitly as link input. */ explicit_link_files = xcalloc (1, n_infiles); if (combine_inputs) { int lang_n_infiles = 0; for (i = 0; (int) i < n_infiles; i++) { const char *name = infiles[i].name; struct compiler *compiler = lookup_compiler (name, strlen (name), infiles[i].language); if (compiler == NULL) error ("%s: linker input file unused because linking not done", name); else if (lang_n_infiles > 0 && compiler != input_file_compiler) fatal ("cannot specify -o with -c or -S and multiple languages"); else { lang_n_infiles++; input_file_compiler = compiler; } } } for (i = 0; (int) i < (combine_inputs ? 1 : n_infiles); i++) { int this_file_error = 0; /* Tell do_spec what to substitute for %i. */ input_file_number = i; set_input (infiles[i].name); /* Use the same thing in %o, unless cp->spec says otherwise. */ outfiles[i] = input_filename; /* Figure out which compiler from the file's suffix. */ if (! combine_inputs) input_file_compiler = lookup_compiler (infiles[i].name, input_filename_length, infiles[i].language); if (input_file_compiler) { /* Ok, we found an applicable compiler. Run its spec. */ if (input_file_compiler->spec[0] == '#') { error ("%s: %s compiler not installed on this system", input_filename, &input_file_compiler->spec[1]); this_file_error = 1; } else { value = do_spec (input_file_compiler->spec); if (value < 0) this_file_error = 1; } } /* If this file's name does not contain a recognized suffix, record it as explicit linker input. */ else explicit_link_files[i] = 1; /* Clear the delete-on-failure queue, deleting the files in it if this compilation failed. */ if (this_file_error) { delete_failure_queue (); error_count++; } /* If this compilation succeeded, don't delete those files later. */ clear_failure_queue (); } /* Reset the output file name to the first input file name, for use with %b in LINK_SPEC on a target that prefers not to emit a.out by default. */ if (n_infiles > 0) set_input (infiles[0].name); if (error_count == 0) { /* Make sure INPUT_FILE_NUMBER points to first available open slot. */ input_file_number = n_infiles; if (lang_specific_pre_link ()) error_count++; } /* Determine if there are any linker input files. */ num_linker_inputs = 0; for (i = 0; (int) i < n_infiles; i++) if (explicit_link_files[i] || outfiles[i] != NULL) num_linker_inputs++; /* Run ld to link all the compiler output files. */ if (num_linker_inputs > 0 && error_count == 0) { int tmp = execution_count; /* We'll use ld if we can't find collect2. */ if (! strcmp (linker_name_spec, "collect2")) { char *s = find_a_file (&exec_prefixes, "collect2", X_OK, 0); if (s == NULL) linker_name_spec = "ld"; } /* Rebuild the COMPILER_PATH and LIBRARY_PATH environment variables for collect. */ putenv_from_prefixes (&exec_prefixes, "COMPILER_PATH"); putenv_from_prefixes (&startfile_prefixes, LIBRARY_PATH_ENV); value = do_spec (link_command_spec); if (value < 0) error_count = 1; linker_was_run = (tmp != execution_count); } /* If options said don't run linker, complain about input files to be given to the linker. */ if (! linker_was_run && error_count == 0) for (i = 0; (int) i < n_infiles; i++) if (explicit_link_files[i]) error ("%s: linker input file unused because linking not done", outfiles[i]); /* Delete some or all of the temporary files we made. */ if (error_count) delete_failure_queue (); delete_temp_files (); if (print_help_list) { printf (("\nFor bug reporting instructions, please see:\n")); printf ("%s\n", bug_report_url); } return (signal_count != 0 ? 2 : error_count > 0 ? (pass_exit_codes ? greatest_status : 1) : 0); } /* Find the proper compilation spec for the file name NAME, whose length is LENGTH. LANGUAGE is the specified language, or 0 if this file is to be passed to the linker. */ static struct compiler * lookup_compiler (const char *name, size_t length, const char *language) { struct compiler *cp; /* If this was specified by the user to be a linker input, indicate that. */ if (language != 0 && language[0] == '*') return 0; /* Otherwise, look for the language, if one is spec'd. */ if (language != 0) { for (cp = compilers + n_compilers - 1; cp >= compilers; cp--) if (cp->suffix[0] == '@' && !strcmp (cp->suffix + 1, language)) return cp; error ("language %s not recognized", language); return 0; } /* Look for a suffix. */ for (cp = compilers + n_compilers - 1; cp >= compilers; cp--) { if (/* The suffix `-' matches only the file name `-'. */ (!strcmp (cp->suffix, "-") && !strcmp (name, "-")) || (strlen (cp->suffix) < length /* See if the suffix matches the end of NAME. */ && !strcmp (cp->suffix, name + length - strlen (cp->suffix)) )) break; } #if defined (OS2) ||defined (HAVE_DOS_BASED_FILE_SYSTEM) /* look again, but case-insensitively this time. */ if (cp < compilers) for (cp = compilers + n_compilers - 1; cp >= compilers; cp--) { if (/* The suffix `-' matches only the file name `-'. */ (!strcmp (cp->suffix, "-") && !strcmp (name, "-")) || (strlen (cp->suffix) < length /* See if the suffix matches the end of NAME. */ && ((!strcmp (cp->suffix, name + length - strlen (cp->suffix)) || !strpbrk (cp->suffix, "ABCDEFGHIJKLMNOPQRSTUVWXYZ")) && !strcasecmp (cp->suffix, name + length - strlen (cp->suffix))) )) break; } #endif if (cp >= compilers) { if (cp->spec[0] != '@') /* A non-alias entry: return it. */ return cp; /* An alias entry maps a suffix to a language. Search for the language; pass 0 for NAME and LENGTH to avoid infinite recursion if language not found. */ return lookup_compiler (NULL, 0, cp->spec + 1); } return 0; } static char * save_string (const char *s, int len) { char *result = xmalloc (len + 1); memcpy (result, s, len); result[len] = 0; return result; } void pfatal_with_name (const char *name) { perror_with_name (name); delete_temp_files (); exit (1); } static void perror_with_name (const char *name) { error ("%s: %s", name, xstrerror (errno)); } static void pfatal_pexecute (const char *errmsg_fmt, const char *errmsg_arg) { if (errmsg_arg) { int save_errno = errno; /* Space for trailing '\0' is in %s. */ char *msg = xmalloc (strlen (errmsg_fmt) + strlen (errmsg_arg)); sprintf (msg, errmsg_fmt, errmsg_arg); errmsg_fmt = msg; errno = save_errno; } pfatal_with_name (errmsg_fmt); } /* Output an error message and exit. */ void fancy_abort (void) { fatal ("internal gcc abort"); } /* Output an error message and exit. */ void fatal (const char *msgid, ...) { va_list ap; va_start (ap, msgid); fprintf (stderr, "%s: ", programname); vfprintf (stderr, _(msgid), ap); va_end (ap); fprintf (stderr, "\n"); delete_temp_files (); exit (1); } void error (const char *msgid, ...) { va_list ap; va_start (ap, msgid); fprintf (stderr, "%s: ", programname); vfprintf (stderr, _(msgid), ap); va_end (ap); fprintf (stderr, "\n"); } static void notice (const char *msgid, ...) { va_list ap; va_start (ap, msgid); vfprintf (stderr, _(msgid), ap); va_end (ap); } static inline void validate_switches_from_spec (const char *spec) { const char *p = spec; char c; while ((c = *p++)) if (c == '%' && (*p == '{' || *p == '<' || (*p == 'W' && *++p == '{'))) /* We have a switch spec. */ p = validate_switches (p + 1); } static void validate_all_switches (void) { struct compiler *comp; struct spec_list *spec; for (comp = compilers; comp->spec; comp++) validate_switches_from_spec (comp->spec); /* Look through the linked list of specs read from the specs file. */ for (spec = specs; spec; spec = spec->next) validate_switches_from_spec (*spec->ptr_spec); validate_switches_from_spec (link_command_spec); } /* Look at the switch-name that comes after START and mark as valid all supplied switches that match it. */ static const char * validate_switches (const char *start) { const char *p = start; const char *atom; size_t len; int i; bool suffix = false; bool starred = false; #define SKIP_WHITE() do { while (*p == ' ' || *p == '\t') p++; } while (0) next_member: SKIP_WHITE (); if (*p == '!') p++; SKIP_WHITE (); if (*p == '.') suffix = true, p++; atom = p; while (ISIDNUM (*p) || *p == '-' || *p == '+' || *p == '=' || *p == ',' || *p == '.' || *p == '@') p++; len = p - atom; if (*p == '*') starred = true, p++; SKIP_WHITE (); if (!suffix) { /* Mark all matching switches as valid. */ for (i = 0; i < n_switches; i++) if (!strncmp (switches[i].part1, atom, len) && (starred || switches[i].part1[len] == 0)) switches[i].validated = 1; } if (*p) p++; if (*p && (p[-1] == '|' || p[-1] == '&')) goto next_member; if (*p && p[-1] == ':') { while (*p && *p != ';' && *p != '}') { if (*p == '%') { p++; if (*p == '{' || *p == '<') p = validate_switches (p+1); else if (p[0] == 'W' && p[1] == '{') p = validate_switches (p+2); } else p++; } if (*p) p++; if (*p && p[-1] == ';') goto next_member; } return p; #undef SKIP_WHITE } struct mdswitchstr { const char *str; int len; }; static struct mdswitchstr *mdswitches; static int n_mdswitches; /* Check whether a particular argument was used. The first time we canonicalize the switches to keep only the ones we care about. */ static int used_arg (const char *p, int len) { struct mswitchstr { const char *str; const char *replace; int len; int rep_len; }; static struct mswitchstr *mswitches; static int n_mswitches; int i, j; if (!mswitches) { struct mswitchstr *matches; const char *q; int cnt = 0; /* Break multilib_matches into the component strings of string and replacement string. */ for (q = multilib_matches; *q != '\0'; q++) if (*q == ';') cnt++; matches = alloca ((sizeof (struct mswitchstr)) * cnt); i = 0; q = multilib_matches; while (*q != '\0') { matches[i].str = q; while (*q != ' ') { if (*q == '\0') abort (); q++; } matches[i].len = q - matches[i].str; matches[i].replace = ++q; while (*q != ';' && *q != '\0') { if (*q == ' ') abort (); q++; } matches[i].rep_len = q - matches[i].replace; i++; if (*q == ';') q++; } /* Now build a list of the replacement string for switches that we care about. Make sure we allocate at least one entry. This prevents xmalloc from calling fatal, and prevents us from re-executing this block of code. */ mswitches = xmalloc (sizeof (struct mswitchstr) * (n_mdswitches + (n_switches ? n_switches : 1))); for (i = 0; i < n_switches; i++) { int xlen = strlen (switches[i].part1); for (j = 0; j < cnt; j++) if (xlen == matches[j].len && ! strncmp (switches[i].part1, matches[j].str, xlen)) { mswitches[n_mswitches].str = matches[j].replace; mswitches[n_mswitches].len = matches[j].rep_len; mswitches[n_mswitches].replace = (char *) 0; mswitches[n_mswitches].rep_len = 0; n_mswitches++; break; } } /* Add MULTILIB_DEFAULTS switches too, as long as they were not present on the command line nor any options mutually incompatible with them. */ for (i = 0; i < n_mdswitches; i++) { const char *r; for (q = multilib_options; *q != '\0'; q++) { while (*q == ' ') q++; r = q; while (strncmp (q, mdswitches[i].str, mdswitches[i].len) != 0 || strchr (" /", q[mdswitches[i].len]) == NULL) { while (*q != ' ' && *q != '/' && *q != '\0') q++; if (*q != '/') break; q++; } if (*q != ' ' && *q != '\0') { while (*r != ' ' && *r != '\0') { q = r; while (*q != ' ' && *q != '/' && *q != '\0') q++; if (used_arg (r, q - r)) break; if (*q != '/') { mswitches[n_mswitches].str = mdswitches[i].str; mswitches[n_mswitches].len = mdswitches[i].len; mswitches[n_mswitches].replace = (char *) 0; mswitches[n_mswitches].rep_len = 0; n_mswitches++; break; } r = q + 1; } break; } } } } for (i = 0; i < n_mswitches; i++) if (len == mswitches[i].len && ! strncmp (p, mswitches[i].str, len)) return 1; return 0; } static int default_arg (const char *p, int len) { int i; for (i = 0; i < n_mdswitches; i++) if (len == mdswitches[i].len && ! strncmp (p, mdswitches[i].str, len)) return 1; return 0; } /* Work out the subdirectory to use based on the options. The format of multilib_select is a list of elements. Each element is a subdirectory name followed by a list of options followed by a semicolon. The format of multilib_exclusions is the same, but without the preceding directory. First gcc will check the exclusions, if none of the options beginning with an exclamation point are present, and all of the other options are present, then we will ignore this completely. Passing that, gcc will consider each multilib_select in turn using the same rules for matching the options. If a match is found, that subdirectory will be used. */ static void set_multilib_dir (void) { const char *p; unsigned int this_path_len; const char *this_path, *this_arg; const char *start, *end; int not_arg; int ok, ndfltok, first; n_mdswitches = 0; start = multilib_defaults; while (*start == ' ' || *start == '\t') start++; while (*start != '\0') { n_mdswitches++; while (*start != ' ' && *start != '\t' && *start != '\0') start++; while (*start == ' ' || *start == '\t') start++; } if (n_mdswitches) { int i = 0; mdswitches = xmalloc (sizeof (struct mdswitchstr) * n_mdswitches); for (start = multilib_defaults; *start != '\0'; start = end + 1) { while (*start == ' ' || *start == '\t') start++; if (*start == '\0') break; for (end = start + 1; *end != ' ' && *end != '\t' && *end != '\0'; end++) ; obstack_grow (&multilib_obstack, start, end - start); obstack_1grow (&multilib_obstack, 0); mdswitches[i].str = obstack_finish (&multilib_obstack); mdswitches[i++].len = end - start; if (*end == '\0') break; } } p = multilib_exclusions; while (*p != '\0') { /* Ignore newlines. */ if (*p == '\n') { ++p; continue; } /* Check the arguments. */ ok = 1; while (*p != ';') { if (*p == '\0') abort (); if (! ok) { ++p; continue; } this_arg = p; while (*p != ' ' && *p != ';') { if (*p == '\0') abort (); ++p; } if (*this_arg != '!') not_arg = 0; else { not_arg = 1; ++this_arg; } ok = used_arg (this_arg, p - this_arg); if (not_arg) ok = ! ok; if (*p == ' ') ++p; } if (ok) return; ++p; } first = 1; p = multilib_select; while (*p != '\0') { /* Ignore newlines. */ if (*p == '\n') { ++p; continue; } /* Get the initial path. */ this_path = p; while (*p != ' ') { if (*p == '\0') abort (); ++p; } this_path_len = p - this_path; /* Check the arguments. */ ok = 1; ndfltok = 1; ++p; while (*p != ';') { if (*p == '\0') abort (); if (! ok) { ++p; continue; } this_arg = p; while (*p != ' ' && *p != ';') { if (*p == '\0') abort (); ++p; } if (*this_arg != '!') not_arg = 0; else { not_arg = 1; ++this_arg; } /* If this is a default argument, we can just ignore it. This is true even if this_arg begins with '!'. Beginning with '!' does not mean that this argument is necessarily inappropriate for this library: it merely means that there is a more specific library which uses this argument. If this argument is a default, we need not consider that more specific library. */ ok = used_arg (this_arg, p - this_arg); if (not_arg) ok = ! ok; if (! ok) ndfltok = 0; if (default_arg (this_arg, p - this_arg)) ok = 1; if (*p == ' ') ++p; } if (ok && first) { if (this_path_len != 1 || this_path[0] != '.') { char *new_multilib_dir = xmalloc (this_path_len + 1); char *q; strncpy (new_multilib_dir, this_path, this_path_len); new_multilib_dir[this_path_len] = '\0'; q = strchr (new_multilib_dir, ':'); if (q != NULL) *q = '\0'; multilib_dir = new_multilib_dir; } first = 0; } if (ndfltok) { const char *q = this_path, *end = this_path + this_path_len; while (q < end && *q != ':') q++; if (q < end) { char *new_multilib_os_dir = xmalloc (end - q); memcpy (new_multilib_os_dir, q + 1, end - q - 1); new_multilib_os_dir[end - q - 1] = '\0'; multilib_os_dir = new_multilib_os_dir; break; } } ++p; } if (multilib_dir == NULL && multilib_os_dir != NULL && strcmp (multilib_os_dir, ".") == 0) { free ((char *) multilib_os_dir); multilib_os_dir = NULL; } else if (multilib_dir != NULL && multilib_os_dir == NULL) multilib_os_dir = multilib_dir; } /* Print out the multiple library subdirectory selection information. This prints out a series of lines. Each line looks like SUBDIRECTORY;@OPTION@OPTION, with as many options as is required. Only the desired options are printed out, the negative matches. The options are print without a leading dash. There are no spaces to make it easy to use the information in the shell. Each subdirectory is printed only once. This assumes the ordering generated by the genmultilib script. Also, we leave out ones that match the exclusions. */ static void print_multilib_info (void) { const char *p = multilib_select; const char *last_path = 0, *this_path; int skip; unsigned int last_path_len = 0; while (*p != '\0') { skip = 0; /* Ignore newlines. */ if (*p == '\n') { ++p; continue; } /* Get the initial path. */ this_path = p; while (*p != ' ') { if (*p == '\0') abort (); ++p; } /* When --disable-multilib was used but target defines MULTILIB_OSDIRNAMES, entries starting with .: are there just to find multilib_os_dir, so skip them from output. */ if (this_path[0] == '.' && this_path[1] == ':') skip = 1; /* Check for matches with the multilib_exclusions. We don't bother with the '!' in either list. If any of the exclusion rules match all of its options with the select rule, we skip it. */ { const char *e = multilib_exclusions; const char *this_arg; while (*e != '\0') { int m = 1; /* Ignore newlines. */ if (*e == '\n') { ++e; continue; } /* Check the arguments. */ while (*e != ';') { const char *q; int mp = 0; if (*e == '\0') abort (); if (! m) { ++e; continue; } this_arg = e; while (*e != ' ' && *e != ';') { if (*e == '\0') abort (); ++e; } q = p + 1; while (*q != ';') { const char *arg; int len = e - this_arg; if (*q == '\0') abort (); arg = q; while (*q != ' ' && *q != ';') { if (*q == '\0') abort (); ++q; } if (! strncmp (arg, this_arg, (len < q - arg) ? q - arg : len) || default_arg (this_arg, e - this_arg)) { mp = 1; break; } if (*q == ' ') ++q; } if (! mp) m = 0; if (*e == ' ') ++e; } if (m) { skip = 1; break; } if (*e != '\0') ++e; } } if (! skip) { /* If this is a duplicate, skip it. */ skip = (last_path != 0 && (unsigned int) (p - this_path) == last_path_len && ! strncmp (last_path, this_path, last_path_len)); last_path = this_path; last_path_len = p - this_path; } /* If this directory requires any default arguments, we can skip it. We will already have printed a directory identical to this one which does not require that default argument. */ if (! skip) { const char *q; q = p + 1; while (*q != ';') { const char *arg; if (*q == '\0') abort (); if (*q == '!') arg = NULL; else arg = q; while (*q != ' ' && *q != ';') { if (*q == '\0') abort (); ++q; } if (arg != NULL && default_arg (arg, q - arg)) { skip = 1; break; } if (*q == ' ') ++q; } } if (! skip) { const char *p1; for (p1 = last_path; p1 < p && *p1 != ':'; p1++) putchar (*p1); putchar (';'); } ++p; while (*p != ';') { int use_arg; if (*p == '\0') abort (); if (skip) { ++p; continue; } use_arg = *p != '!'; if (use_arg) putchar ('@'); while (*p != ' ' && *p != ';') { if (*p == '\0') abort (); if (use_arg) putchar (*p); ++p; } if (*p == ' ') ++p; } if (! skip) { /* If there are extra options, print them now. */ if (multilib_extra && *multilib_extra) { int print_at = TRUE; const char *q; for (q = multilib_extra; *q != '\0'; q++) { if (*q == ' ') print_at = TRUE; else { if (print_at) putchar ('@'); putchar (*q); print_at = FALSE; } } } putchar ('\n'); } ++p; } } /* if-exists built-in spec function. Checks to see if the file specified by the absolute pathname in ARGS exists. Returns that pathname if found. The usual use for this function is to check for a library file (whose name has been expanded with %s). */ static const char * if_exists_spec_function (int argc, const char **argv) { /* Must have only one argument. */ if (argc == 1 && IS_ABSOLUTE_PATH (argv[0]) && ! access (argv[0], R_OK)) return argv[0]; return NULL; } /* if-exists-else built-in spec function. This is like if-exists, but takes an additional argument which is returned if the first argument does not exist. */ static const char * if_exists_else_spec_function (int argc, const char **argv) { /* Must have exactly two arguments. */ if (argc != 2) return NULL; if (IS_ABSOLUTE_PATH (argv[0]) && ! access (argv[0], R_OK)) return argv[0]; return argv[1]; } diff --git a/contrib/gcc/toplev.c b/contrib/gcc/toplev.c index acb44f6952d1..da20354f7fda 100644 --- a/contrib/gcc/toplev.c +++ b/contrib/gcc/toplev.c @@ -1,4708 +1,4712 @@ /* Top level of GCC compilers (cc1, cc1plus, etc.) Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* $FreeBSD$ */ /* This is the top level of cc1/c++. It parses command args, opens files, invokes the various passes in the proper order, and counts the time used by each. Error messages and low-level interface to malloc also handled here. */ #include "config.h" #undef FLOAT /* This is for hpux. They should change hpux. */ #undef FFS /* Some systems define this in param.h. */ #include "system.h" #include "coretypes.h" #include "tm.h" #include #ifdef HAVE_SYS_RESOURCE_H # include #endif #ifdef HAVE_SYS_TIMES_H # include #endif #include "input.h" #include "tree.h" #include "rtl.h" #include "tm_p.h" #include "flags.h" #include "insn-attr.h" #include "insn-config.h" #include "insn-flags.h" #include "hard-reg-set.h" #include "recog.h" #include "output.h" #include "except.h" #include "function.h" #include "toplev.h" #include "expr.h" #include "basic-block.h" #include "intl.h" #include "ggc.h" #include "graph.h" #include "loop.h" #include "regs.h" #include "timevar.h" #include "diagnostic.h" #include "params.h" #include "reload.h" #include "dwarf2asm.h" #include "integrate.h" #include "real.h" #include "debug.h" #include "target.h" #include "langhooks.h" #include "cfglayout.h" #include "cfgloop.h" #include "hosthooks.h" #include "cgraph.h" #include "opts.h" #include "coverage.h" #include "value-prof.h" #include "alloc-pool.h" #if defined (DWARF2_UNWIND_INFO) || defined (DWARF2_DEBUGGING_INFO) #include "dwarf2out.h" #endif #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO) #include "dbxout.h" #endif #ifdef SDB_DEBUGGING_INFO #include "sdbout.h" #endif #ifdef XCOFF_DEBUGGING_INFO #include "xcoffout.h" /* Needed for external data declarations for e.g. AIX 4.x. */ #endif #ifndef HAVE_conditional_execution #define HAVE_conditional_execution 0 #endif /* Carry information from ASM_DECLARE_OBJECT_NAME to ASM_FINISH_DECLARE_OBJECT. */ extern int size_directive_output; extern tree last_assemble_variable_decl; extern void reg_alloc (void); static void general_init (const char *); static void do_compile (void); static void process_options (void); static void backend_init (void); static int lang_dependent_init (const char *); static void init_asm_output (const char *); static void finalize (void); static void crash_signal (int) ATTRIBUTE_NORETURN; static void setup_core_dumping (void); static void compile_file (void); static int print_single_switch (FILE *, int, int, const char *, const char *, const char *, const char *, const char *); static void print_switch_values (FILE *, int, int, const char *, const char *, const char *); /* Rest of compilation helper functions. */ static bool rest_of_handle_inlining (tree); static void rest_of_handle_cse (tree, rtx); static void rest_of_handle_cse2 (tree, rtx); static void rest_of_handle_gcse (tree, rtx); static void rest_of_handle_life (tree, rtx); static void rest_of_handle_loop_optimize (tree, rtx); static void rest_of_handle_loop2 (tree, rtx); static void rest_of_handle_jump_bypass (tree, rtx); static void rest_of_handle_sibling_calls (rtx); static void rest_of_handle_null_pointer (tree, rtx); static void rest_of_handle_addressof (tree, rtx); static void rest_of_handle_cfg (tree, rtx); static void rest_of_handle_branch_prob (tree, rtx); static void rest_of_handle_value_profile_transformations (tree, rtx); static void rest_of_handle_if_conversion (tree, rtx); static void rest_of_handle_if_after_combine (tree, rtx); static void rest_of_handle_tracer (tree, rtx); static void rest_of_handle_combine (tree, rtx); static void rest_of_handle_regmove (tree, rtx); #ifdef INSN_SCHEDULING static void rest_of_handle_sched (tree, rtx); static void rest_of_handle_sched2 (tree, rtx); #endif static bool rest_of_handle_new_regalloc (tree, rtx); static bool rest_of_handle_old_regalloc (tree, rtx); static void rest_of_handle_regrename (tree, rtx); static void rest_of_handle_reorder_blocks (tree, rtx); #ifdef STACK_REGS static void rest_of_handle_stack_regs (tree, rtx); #endif static void rest_of_handle_machine_reorg (tree, rtx); #ifdef DELAY_SLOTS static void rest_of_handle_delay_slots (tree, rtx); #endif static void rest_of_handle_final (tree, rtx); /* Nonzero to dump debug info whilst parsing (-dy option). */ static int set_yydebug; /* True if we don't need a backend (e.g. preprocessing only). */ static bool no_backend; /* Length of line when printing switch values. */ #define MAX_LINE 75 /* Name of program invoked, sans directories. */ const char *progname; /* Copy of argument vector to toplev_main. */ static const char **save_argv; /* Name of top-level original source file (what was input to cpp). This comes from the #-command at the beginning of the actual input. If there isn't any there, then this is the cc1 input file name. */ const char *main_input_filename; /* Current position in real source file. */ location_t input_location; /* Nonzero if it is unsafe to create any new pseudo registers. */ int no_new_pseudos; /* Stack of currently pending input files. */ struct file_stack *input_file_stack; /* Incremented on each change to input_file_stack. */ int input_file_stack_tick; /* Name to use as base of names for dump output files. */ const char *dump_base_name; /* Name to use as a base for auxiliary output files. */ const char *aux_base_name; /* Format to use to print dumpfile index value */ #ifndef DUMPFILE_FORMAT #define DUMPFILE_FORMAT ".%02d." #endif /* Bit flags that specify the machine subtype we are compiling for. Bits are tested using macros TARGET_... defined in the tm.h file and set by `-m...' switches. Must be defined in rtlanal.c. */ extern int target_flags; /* A mask of target_flags that includes bit X if X was set or cleared on the command line. */ int target_flags_explicit; /* Debug hooks - dependent upon command line options. */ const struct gcc_debug_hooks *debug_hooks; /* Describes a dump file. */ struct dump_file_info { /* The unique extension to apply, e.g. ".jump". */ const char *const extension; /* The -d character that enables this dump file. */ char const debug_switch; /* True if there is a corresponding graph dump file. */ char const graph_dump_p; /* True if the user selected this dump. */ char enabled; /* True if the files have been initialized (ie truncated). */ char initialized; }; /* Enumerate the extant dump files. */ enum dump_file_index { DFI_cgraph, DFI_rtl, DFI_sibling, DFI_eh, DFI_jump, DFI_null, DFI_cse, DFI_addressof, DFI_gcse, DFI_loop, DFI_bypass, DFI_cfg, DFI_bp, DFI_vpt, DFI_ce1, DFI_tracer, DFI_loop2, DFI_web, DFI_cse2, DFI_life, DFI_combine, DFI_ce2, DFI_regmove, DFI_sched, DFI_lreg, DFI_greg, DFI_postreload, DFI_flow2, DFI_peephole2, DFI_ce3, DFI_rnreg, DFI_bbro, DFI_branch_target_load, DFI_sched2, DFI_stack, DFI_mach, DFI_dbr, DFI_MAX }; /* Describes all the dump files. Should be kept in order of the pass and in sync with dump_file_index above. Remaining -d letters: " e m q " " JK O Q WXY " */ static struct dump_file_info dump_file[DFI_MAX] = { { "cgraph", 'U', 0, 0, 0 }, { "rtl", 'r', 0, 0, 0 }, { "sibling", 'i', 0, 0, 0 }, { "eh", 'h', 0, 0, 0 }, { "jump", 'j', 0, 0, 0 }, { "null", 'u', 0, 0, 0 }, { "cse", 's', 0, 0, 0 }, { "addressof", 'F', 0, 0, 0 }, { "gcse", 'G', 1, 0, 0 }, { "loop", 'L', 1, 0, 0 }, { "bypass", 'G', 1, 0, 0 }, /* Yes, duplicate enable switch. */ { "cfg", 'f', 1, 0, 0 }, { "bp", 'b', 1, 0, 0 }, { "vpt", 'V', 1, 0, 0 }, { "ce1", 'C', 1, 0, 0 }, { "tracer", 'T', 1, 0, 0 }, { "loop2", 'L', 1, 0, 0 }, { "web", 'Z', 0, 0, 0 }, { "cse2", 't', 1, 0, 0 }, { "life", 'f', 1, 0, 0 }, /* Yes, duplicate enable switch. */ { "combine", 'c', 1, 0, 0 }, { "ce2", 'C', 1, 0, 0 }, { "regmove", 'N', 1, 0, 0 }, { "sched", 'S', 1, 0, 0 }, { "lreg", 'l', 1, 0, 0 }, { "greg", 'g', 1, 0, 0 }, { "postreload", 'o', 1, 0, 0 }, { "flow2", 'w', 1, 0, 0 }, { "peephole2", 'z', 1, 0, 0 }, { "ce3", 'E', 1, 0, 0 }, { "rnreg", 'n', 1, 0, 0 }, { "bbro", 'B', 1, 0, 0 }, { "btl", 'd', 1, 0, 0 }, /* Yes, duplicate enable switch. */ { "sched2", 'R', 1, 0, 0 }, { "stack", 'k', 1, 0, 0 }, { "mach", 'M', 1, 0, 0 }, { "dbr", 'd', 0, 0, 0 }, }; static int open_dump_file (enum dump_file_index, tree); static void close_dump_file (enum dump_file_index, void (*) (FILE *, rtx), rtx); /* Other flags saying which kinds of debugging dump have been requested. */ int rtl_dump_and_exit; int flag_print_asm_name; enum graph_dump_types graph_dump_format; /* Name for output file of assembly code, specified with -o. */ const char *asm_file_name; /* Nonzero means do optimizations. -O. Particular numeric values stand for particular amounts of optimization; thus, -O2 stores 2 here. However, the optimizations beyond the basic ones are not controlled directly by this variable. Instead, they are controlled by individual `flag_...' variables that are defaulted based on this variable. */ int optimize = 0; /* Nonzero means optimize for size. -Os. The only valid values are zero and nonzero. When optimize_size is nonzero, optimize defaults to 2, but certain individual code bloating optimizations are disabled. */ int optimize_size = 0; /* The FUNCTION_DECL for the function currently being compiled, or 0 if between functions. */ tree current_function_decl; /* Set to the FUNC_BEGIN label of the current function, or NULL_TREE if none. */ tree current_function_func_begin_label; /* Nonzero if doing dwarf2 duplicate elimination. */ int flag_eliminate_dwarf2_dups = 0; /* Nonzero if doing unused type elimination. */ int flag_eliminate_unused_debug_types = 1; /* Nonzero means emit debugging information only for symbols which are used. */ int flag_debug_only_used_symbols = 0; /* Nonzero if generating code to do profiling. */ int profile_flag = 0; /* Nonzero if generating code to profile program flow graph arcs. */ int profile_arc_flag = 0; /* Nonzero if value histograms should be measured. */ int flag_profile_values = 0; /* Nonzero if value histograms should be used to optimize code. */ int flag_value_profile_transformations = 0; /* Nonzero if generating info for gcov to calculate line test coverage. */ int flag_test_coverage = 0; /* Nonzero indicates that branch taken probabilities should be calculated. */ int flag_branch_probabilities = 0; /* Nonzero if basic blocks should be reordered. */ int flag_reorder_blocks = 0; /* Nonzero if functions should be reordered. */ int flag_reorder_functions = 0; /* Nonzero if registers should be renamed. */ int flag_rename_registers = 0; int flag_cprop_registers = 0; /* Nonzero for -pedantic switch: warn about anything that standard spec forbids. */ int pedantic = 0; /* Temporarily suppress certain warnings. This is set while reading code from a system header file. */ int in_system_header = 0; /* Don't print functions as they are compiled. -quiet. */ int quiet_flag = 0; /* Print times taken by the various passes. -ftime-report. */ int time_report = 0; /* Print memory still in use at end of compilation (which may have little to do with peak memory consumption). -fmem-report. */ int mem_report = 0; /* Nonzero means to collect statistics which might be expensive and to print them when we are done. */ int flag_detailed_statistics = 0; /* A random sequence of characters, unless overridden by user. */ const char *flag_random_seed; /* A local time stamp derived from the time of compilation. It will be zero if the system cannot provide a time. It will be -1u, if the user has specified a particular random seed. */ unsigned local_tick; /* -f flags. */ /* Nonzero means `char' should be signed. */ int flag_signed_char; /* Nonzero means give an enum type only as many bytes as it needs. */ int flag_short_enums; /* Nonzero for -fcaller-saves: allocate values in regs that need to be saved across function calls, if that produces overall better code. Optional now, so people can test it. */ int flag_caller_saves = 0; /* Nonzero if structures and unions should be returned in memory. This should only be defined if compatibility with another compiler or with an ABI is needed, because it results in slower code. */ #ifndef DEFAULT_PCC_STRUCT_RETURN #define DEFAULT_PCC_STRUCT_RETURN 1 #endif /* Nonzero for -fpcc-struct-return: return values the same way PCC does. */ int flag_pcc_struct_return = DEFAULT_PCC_STRUCT_RETURN; /* Nonzero for -fforce-mem: load memory value into a register before arithmetic on it. This makes better cse but slower compilation. */ int flag_force_mem = 0; /* Nonzero for -fforce-addr: load memory address into a register before reference to memory. This makes better cse but slower compilation. */ int flag_force_addr = 0; /* Nonzero for -fdefer-pop: don't pop args after each function call; instead save them up to pop many calls' args with one insns. */ int flag_defer_pop = 0; /* Nonzero for -ffloat-store: don't allocate floats and doubles in extended-precision registers. */ int flag_float_store = 0; /* Nonzero for -fcse-follow-jumps: have cse follow jumps to do a more extensive job. */ int flag_cse_follow_jumps; /* Nonzero for -fcse-skip-blocks: have cse follow a branch around a block. */ int flag_cse_skip_blocks; /* Nonzero for -fexpensive-optimizations: perform miscellaneous relatively-expensive optimizations. */ int flag_expensive_optimizations; /* Nonzero for -fthread-jumps: have jump optimize output of loop. */ int flag_thread_jumps; /* Nonzero enables strength-reduction in loop.c. */ int flag_strength_reduce = 0; /* Nonzero enables loop unrolling in unroll.c. Only loops for which the number of iterations can be calculated at compile-time (UNROLL_COMPLETELY, UNROLL_MODULO) or at run-time (preconditioned to be UNROLL_MODULO) are unrolled. */ int flag_old_unroll_loops; /* Nonzero enables loop unrolling in unroll.c. All loops are unrolled. This is generally not a win. */ int flag_old_unroll_all_loops; /* Enables unrolling of simple loops in loop-unroll.c. */ int flag_unroll_loops; /* Enables unrolling of all loops in loop-unroll.c. */ int flag_unroll_all_loops; /* Nonzero enables loop peeling. */ int flag_peel_loops; /* Nonzero enables loop unswitching. */ int flag_unswitch_loops; /* Nonzero enables prefetch optimizations for arrays in loops. */ int flag_prefetch_loop_arrays; /* Nonzero forces all invariant computations in loops to be moved outside the loop. */ int flag_move_all_movables = 0; /* Nonzero forces all general induction variables in loops to be strength reduced. */ int flag_reduce_all_givs = 0; /* Nonzero to perform full register move optimization passes. This is the default for -O2. */ int flag_regmove = 0; /* Nonzero for -fwritable-strings: store string constants in data segment and don't uniquize them. */ int flag_writable_strings = 0; /* Nonzero means don't put addresses of constant functions in registers. Used for compiling the Unix kernel, where strange substitutions are done on the assembly output. */ int flag_no_function_cse = 0; /* Nonzero for -fomit-frame-pointer: don't make a frame pointer in simple functions that don't require one. */ int flag_omit_frame_pointer = 0; /* Nonzero means place each function into its own section on those platforms which support arbitrary section names and unlimited numbers of sections. */ int flag_function_sections = 0; /* ... and similar for data. */ int flag_data_sections = 0; /* Nonzero to inhibit use of define_optimization peephole opts. */ int flag_no_peephole = 0; /* Nonzero allows GCC to optimize sibling and tail recursive calls. */ int flag_optimize_sibling_calls = 0; /* Nonzero means the front end generally wants `errno' maintained by math operations, like built-in SQRT. */ int flag_errno_math = 1; /* Nonzero means that unsafe floating-point math optimizations are allowed for the sake of speed. IEEE compliance is not guaranteed, and operations are allowed to assume that their arguments and results are "normal" (e.g., nonnegative for SQRT). */ int flag_unsafe_math_optimizations = 0; /* Nonzero means that no NaNs or +-Infs are expected. */ int flag_finite_math_only = 0; /* Zero means that floating-point math operations cannot generate a (user-visible) trap. This is the case, for example, in nonstop IEEE 754 arithmetic. Trapping conditions include division by zero, overflow, underflow, invalid and inexact, but does not include operations on signaling NaNs (see below). */ int flag_trapping_math = 1; /* Nonzero means disable transformations that assume default floating point rounding behavior. */ int flag_rounding_math = 0; /* Nonzero means disable transformations observable by signaling NaNs. This option implies that any operation on an IEEE signaling NaN can generate a (user-visible) trap. */ int flag_signaling_nans = 0; /* 0 means straightforward implementation of complex divide acceptable. 1 means wide ranges of inputs must work for complex divide. 2 means C99-like requirements for complex divide (not yet implemented). */ int flag_complex_divide_method = 0; /* Nonzero means just do syntax checking; don't output anything. */ int flag_syntax_only = 0; /* Nonzero means performs web construction pass. */ int flag_web; /* Nonzero means perform loop optimizer. */ int flag_loop_optimize; /* Nonzero means perform crossjumping. */ int flag_crossjumping; /* Nonzero means perform if conversion. */ int flag_if_conversion; /* Nonzero means perform if conversion after reload. */ int flag_if_conversion2; /* Nonzero means to use global dataflow analysis to eliminate useless null pointer tests. */ int flag_delete_null_pointer_checks; /* Nonzero means perform global CSE. */ int flag_gcse = 0; /* Nonzero means to do the enhanced load motion during gcse, which trys to hoist loads by not killing them when a store to the same location is seen. */ int flag_gcse_lm = 1; /* Nonzero means to perform store motion after gcse, which will try to move stores closer to the exit block. Its not very effective without flag_gcse_lm. */ int flag_gcse_sm = 1; /* Nonzero if we want to perfrom redundant load after store elimination in gcse. */ int flag_gcse_las = 1; /* Perform target register optimization before prologue / epilogue threading. */ int flag_branch_target_load_optimize = 0; /* Perform target register optimization after prologue / epilogue threading and jump2. */ int flag_branch_target_load_optimize2 = 0; /* Nonzero means to rerun cse after loop optimization. This increases compilation time about 20% and picks up a few more common expressions. */ int flag_rerun_cse_after_loop; /* Nonzero means to run loop optimizations twice. */ int flag_rerun_loop_opt; /* Nonzero for -finline-functions: ok to inline functions that look like good inline candidates. */ int flag_inline_functions; /* Nonzero for -fkeep-inline-functions: even if we make a function go inline everywhere, keep its definition around for debugging purposes. */ int flag_keep_inline_functions; /* Nonzero means that functions will not be inlined. */ int flag_no_inline = 2; /* Nonzero means that we don't want inlining by virtue of -fno-inline, not just because the tree inliner turned us off. */ int flag_really_no_inline = 2; /* Nonzero means that we should emit static const variables regardless of whether or not optimization is turned on. */ int flag_keep_static_consts = 1; /* Nonzero means we should be saving declaration info into a .X file. */ int flag_gen_aux_info = 0; /* Specified name of aux-info file. */ const char *aux_info_file_name; /* Nonzero means make the text shared if supported. */ int flag_shared_data; /* Nonzero means schedule into delayed branch slots if supported. */ int flag_delayed_branch; /* Nonzero if we are compiling pure (sharable) code. Value is 1 if we are doing "small" pic; value is 2 if we're doing "large" pic. */ int flag_pic; /* Nonzero if we are compiling position independent code for executable. The value is 1 if we are doing "small" pic; value is 2 if we're doing "large" pic. */ int flag_pie; /* Nonzero if we are compiling code for a shared library, zero for executable. */ int flag_shlib; /* Set to the default thread-local storage (tls) model to use. */ enum tls_model flag_tls_default = TLS_MODEL_GLOBAL_DYNAMIC; /* Nonzero means generate extra code for exception handling and enable exception handling. */ int flag_exceptions; /* Nonzero means generate frame unwind info table when supported. */ int flag_unwind_tables = 0; /* Nonzero means generate frame unwind info table exact at each insn boundary. */ int flag_asynchronous_unwind_tables = 0; /* Nonzero means don't place uninitialized global data in common storage by default. */ int flag_no_common; /* Nonzero means change certain warnings into errors. Usually these are warnings about failure to conform to some standard. */ int flag_pedantic_errors = 0; /* flag_schedule_insns means schedule insns within basic blocks (before local_alloc). flag_schedule_insns_after_reload means schedule insns after global_alloc. */ int flag_schedule_insns = 0; int flag_schedule_insns_after_reload = 0; /* When flag_schedule_insns_after_reload is set, use EBB scheduler. */ int flag_sched2_use_superblocks = 0; /* When flag_schedule_insns_after_reload is set, construct traces and EBB scheduler. */ int flag_sched2_use_traces = 0; /* The following flags have effect only for scheduling before register allocation: flag_schedule_interblock means schedule insns across basic blocks. flag_schedule_speculative means allow speculative motion of non-load insns. flag_schedule_speculative_load means allow speculative motion of some load insns. flag_schedule_speculative_load_dangerous allows speculative motion of more load insns. */ int flag_schedule_interblock = 1; int flag_schedule_speculative = 1; int flag_schedule_speculative_load = 0; int flag_schedule_speculative_load_dangerous = 0; /* The following flags have an effect during scheduling after register allocation: flag_sched_stalled_insns means that insns can be moved prematurely from the queue of stalled insns into the ready list. flag_sched_stalled_insns_dep controls how many insn groups will be examined for a dependency on a stalled insn that is candidate for premature removal from the queue of stalled insns into the ready list (has an effect only if the flag 'sched_stalled_insns' is set). */ int flag_sched_stalled_insns = 0; int flag_sched_stalled_insns_dep = 1; int flag_single_precision_constant; /* flag_branch_on_count_reg means try to replace add-1,compare,branch tupple by a cheaper branch on a count register. */ int flag_branch_on_count_reg = 1; /* -finhibit-size-directive inhibits output of .size for ELF. This is used only for compiling crtstuff.c, and it may be extended to other effects needed for crtstuff.c on other systems. */ int flag_inhibit_size_directive = 0; /* -fverbose-asm causes extra commentary information to be produced in the generated assembly code (to make it more readable). This option is generally only of use to those who actually need to read the generated assembly code (perhaps while debugging the compiler itself). -fno-verbose-asm, the default, causes the extra information to be omitted and is useful when comparing two assembler files. */ int flag_verbose_asm = 0; /* -dA causes debug commentary information to be produced in the generated assembly code (to make it more readable). This option is generally only of use to those who actually need to read the generated assembly code (perhaps while debugging the compiler itself). Currently, this switch is only used by dwarfout.c; however, it is intended to be a catchall for printing debug information in the assembler file. */ int flag_debug_asm = 0; /* -dP causes the rtl to be emitted as a comment in assembly. */ int flag_dump_rtl_in_asm = 0; /* Nonzero means put zero initialized data in the bss section. */ int flag_zero_initialized_in_bss = 1; /* Tag all structures with __attribute__(packed). */ int flag_pack_struct = 0; /* Nonzero means that -Wformat accepts certain system-dependent formats. */ int flag_format_extensions = 0; /* Emit code to check for stack overflow; also may cause large objects to be allocated dynamically. */ int flag_stack_check; /* When non-NULL, indicates that whenever space is allocated on the stack, the resulting stack pointer must not pass this address---that is, for stacks that grow downward, the stack pointer must always be greater than or equal to this address; for stacks that grow upward, the stack pointer must be less than this address. At present, the rtx may be either a REG or a SYMBOL_REF, although the support provided depends on the backend. */ rtx stack_limit_rtx; /* 0 if pointer arguments may alias each other. True in C. 1 if pointer arguments may not alias each other but may alias global variables. 2 if pointer arguments may not alias each other and may not alias global variables. True in Fortran. This defaults to 0 for C. */ int flag_argument_noalias = 0; /* Nonzero if we should do (language-dependent) alias analysis. Typically, this analysis will assume that expressions of certain types do not alias expressions of certain other types. Only used if alias analysis (in general) is enabled. */ int flag_strict_aliasing = 0; /* Instrument functions with calls at entry and exit, for profiling. */ int flag_instrument_function_entry_exit = 0; /* Nonzero means ignore `#ident' directives. 0 means handle them. On SVR4 targets, it also controls whether or not to emit a string identifying the compiler. */ int flag_no_ident = 0; /* This will perform a peephole pass before sched2. */ int flag_peephole2 = 0; /* This will try to guess branch probabilities. */ int flag_guess_branch_prob = 0; /* -fcheck-bounds causes gcc to generate array bounds checks. For C, C++, ObjC: defaults to off. For Java: defaults to on. For Fortran: defaults to off. */ int flag_bounds_check = 0; /* This will attempt to merge constant section constants, if 1 only string constants and constants from constant pool, if 2 also constant variables. */ int flag_merge_constants = 1; /* If one, renumber instruction UIDs to reduce the number of unused UIDs if there are a lot of instructions. If greater than one, unconditionally renumber instruction UIDs. */ int flag_renumber_insns = 1; /* If nonzero, use the graph coloring register allocator. */ int flag_new_regalloc = 0; /* Nonzero if we perform superblock formation. */ int flag_tracer = 0; /* Nonzero if we perform whole unit at a time compilation. */ int flag_unit_at_a_time = 0; /* Values of the -falign-* flags: how much to align labels in code. 0 means `use default', 1 means `don't align'. For each variable, there is an _log variant which is the power of two not less than the variable, for .align output. */ int align_loops; int align_loops_log; int align_loops_max_skip; int align_jumps; int align_jumps_log; int align_jumps_max_skip; int align_labels; int align_labels_log; int align_labels_max_skip; int align_functions; int align_functions_log; /* Like align_functions_log above, but used by front-ends to force the minimum function alignment. Zero means no alignment is forced. */ int force_align_functions_log; typedef struct { const char *const string; int *const variable; const int on_value; } lang_independent_options; /* Nonzero if signed arithmetic overflow should trap. */ int flag_trapv = 0; /* Nonzero if signed arithmetic overflow should wrap around. */ int flag_wrapv = 0; /* Nonzero if subexpressions must be evaluated from left-to-right. */ int flag_evaluation_order = 0; /* Add or remove a leading underscore from user symbols. */ int flag_leading_underscore = -1; /* The version of the C++ ABI in use. The following values are allowed: 0: The version of the ABI believed most conformant with the C++ ABI specification. This ABI may change as bugs are discovered and fixed. Therefore, 0 will not necessarily indicate the same ABI in different versions of G++. 1: The version of the ABI first used in G++ 3.2. 2: The version of the ABI first used in G++ 3.4. Additional positive integers will be assigned as new versions of the ABI become the default version of the ABI. */ int flag_abi_version = 2; /* The user symbol prefix after having resolved same. */ const char *user_label_prefix; static const param_info lang_independent_params[] = { #define DEFPARAM(ENUM, OPTION, HELP, DEFAULT) \ { OPTION, DEFAULT, HELP }, #include "params.def" #undef DEFPARAM { NULL, 0, NULL } }; /* Table of language-independent -f options. STRING is the option name. VARIABLE is the address of the variable. ON_VALUE is the value to store in VARIABLE if `-fSTRING' is seen as an option. (If `-fno-STRING' is seen as an option, the opposite value is stored.) */ static const lang_independent_options f_options[] = { {"format-extensions", &flag_format_extensions, 1}, {"eliminate-dwarf2-dups", &flag_eliminate_dwarf2_dups, 1 }, {"eliminate-unused-debug-symbols", &flag_debug_only_used_symbols, 1 }, {"eliminate-unused-debug-types", &flag_eliminate_unused_debug_types, 1 }, {"float-store", &flag_float_store, 1 }, {"defer-pop", &flag_defer_pop, 1 }, {"omit-frame-pointer", &flag_omit_frame_pointer, 1 }, {"optimize-sibling-calls", &flag_optimize_sibling_calls, 1 }, {"tracer", &flag_tracer, 1 }, {"unit-at-a-time", &flag_unit_at_a_time, 1 }, {"cse-follow-jumps", &flag_cse_follow_jumps, 1 }, {"cse-skip-blocks", &flag_cse_skip_blocks, 1 }, {"expensive-optimizations", &flag_expensive_optimizations, 1 }, {"thread-jumps", &flag_thread_jumps, 1 }, {"strength-reduce", &flag_strength_reduce, 1 }, {"unroll-loops", &flag_unroll_loops, 1 }, {"unroll-all-loops", &flag_unroll_all_loops, 1 }, {"old-unroll-loops", &flag_old_unroll_loops, 1 }, {"old-unroll-all-loops", &flag_old_unroll_all_loops, 1 }, {"peel-loops", &flag_peel_loops, 1 }, {"unswitch-loops", &flag_unswitch_loops, 1 }, {"prefetch-loop-arrays", &flag_prefetch_loop_arrays, 1 }, {"move-all-movables", &flag_move_all_movables, 1 }, {"reduce-all-givs", &flag_reduce_all_givs, 1 }, {"writable-strings", &flag_writable_strings, 1 }, {"peephole", &flag_no_peephole, 0 }, {"force-mem", &flag_force_mem, 1 }, {"force-addr", &flag_force_addr, 1 }, {"function-cse", &flag_no_function_cse, 0 }, {"inline-functions", &flag_inline_functions, 1 }, {"keep-inline-functions", &flag_keep_inline_functions, 1 }, {"inline", &flag_no_inline, 0 }, {"keep-static-consts", &flag_keep_static_consts, 1 }, {"syntax-only", &flag_syntax_only, 1 }, {"shared-data", &flag_shared_data, 1 }, {"caller-saves", &flag_caller_saves, 1 }, {"pcc-struct-return", &flag_pcc_struct_return, 1 }, {"reg-struct-return", &flag_pcc_struct_return, 0 }, {"delayed-branch", &flag_delayed_branch, 1 }, {"web", &flag_web, 1}, {"gcse", &flag_gcse, 1 }, {"gcse-lm", &flag_gcse_lm, 1 }, {"gcse-sm", &flag_gcse_sm, 1 }, {"gcse-las", &flag_gcse_las, 1 }, {"branch-target-load-optimize", &flag_branch_target_load_optimize, 1 }, {"branch-target-load-optimize2", &flag_branch_target_load_optimize2, 1 }, {"loop-optimize", &flag_loop_optimize, 1 }, {"crossjumping", &flag_crossjumping, 1 }, {"if-conversion", &flag_if_conversion, 1 }, {"if-conversion2", &flag_if_conversion2, 1 }, {"rerun-cse-after-loop", &flag_rerun_cse_after_loop, 1 }, {"rerun-loop-opt", &flag_rerun_loop_opt, 1 }, {"delete-null-pointer-checks", &flag_delete_null_pointer_checks, 1 }, {"schedule-insns", &flag_schedule_insns, 1 }, {"schedule-insns2", &flag_schedule_insns_after_reload, 1 }, {"sched-interblock",&flag_schedule_interblock, 1 }, {"sched-spec",&flag_schedule_speculative, 1 }, {"sched-spec-load",&flag_schedule_speculative_load, 1 }, {"sched-spec-load-dangerous",&flag_schedule_speculative_load_dangerous, 1 }, {"sched-stalled-insns", &flag_sched_stalled_insns, 0 }, {"sched-stalled-insns-dep", &flag_sched_stalled_insns_dep, 1 }, {"sched2-use-superblocks", &flag_sched2_use_superblocks, 1 }, {"sched2-use-traces", &flag_sched2_use_traces, 1 }, {"branch-count-reg",&flag_branch_on_count_reg, 1 }, {"pic", &flag_pic, 1 }, {"PIC", &flag_pic, 2 }, {"pie", &flag_pie, 1 }, {"PIE", &flag_pie, 2 }, {"exceptions", &flag_exceptions, 1 }, {"unwind-tables", &flag_unwind_tables, 1 }, {"asynchronous-unwind-tables", &flag_asynchronous_unwind_tables, 1 }, {"non-call-exceptions", &flag_non_call_exceptions, 1 }, {"profile-arcs", &profile_arc_flag, 1 }, {"profile-values", &flag_profile_values, 1 }, {"vpt", &flag_value_profile_transformations, 1 }, {"test-coverage", &flag_test_coverage, 1 }, {"branch-probabilities", &flag_branch_probabilities, 1 }, {"profile", &profile_flag, 1 }, {"reorder-blocks", &flag_reorder_blocks, 1 }, {"reorder-functions", &flag_reorder_functions, 1 }, {"rename-registers", &flag_rename_registers, 1 }, {"cprop-registers", &flag_cprop_registers, 1 }, {"common", &flag_no_common, 0 }, {"inhibit-size-directive", &flag_inhibit_size_directive, 1 }, {"function-sections", &flag_function_sections, 1 }, {"data-sections", &flag_data_sections, 1 }, {"verbose-asm", &flag_verbose_asm, 1 }, {"regmove", &flag_regmove, 1 }, {"optimize-register-move", &flag_regmove, 1 }, {"pack-struct", &flag_pack_struct, 1 }, {"stack-check", &flag_stack_check, 1 }, {"argument-alias", &flag_argument_noalias, 0 }, {"argument-noalias", &flag_argument_noalias, 1 }, {"argument-noalias-global", &flag_argument_noalias, 2 }, {"strict-aliasing", &flag_strict_aliasing, 1 }, {"align-loops", &align_loops, 0 }, {"align-jumps", &align_jumps, 0 }, {"align-labels", &align_labels, 0 }, {"align-functions", &align_functions, 0 }, {"merge-constants", &flag_merge_constants, 1 }, {"merge-all-constants", &flag_merge_constants, 2 }, {"dump-unnumbered", &flag_dump_unnumbered, 1 }, {"instrument-functions", &flag_instrument_function_entry_exit, 1 }, {"zero-initialized-in-bss", &flag_zero_initialized_in_bss, 1 }, {"leading-underscore", &flag_leading_underscore, 1 }, {"ident", &flag_no_ident, 0 }, { "peephole2", &flag_peephole2, 1 }, {"finite-math-only", &flag_finite_math_only, 1 }, { "guess-branch-probability", &flag_guess_branch_prob, 1 }, {"math-errno", &flag_errno_math, 1 }, {"trapping-math", &flag_trapping_math, 1 }, {"rounding-math", &flag_rounding_math, 1 }, {"unsafe-math-optimizations", &flag_unsafe_math_optimizations, 1 }, {"signaling-nans", &flag_signaling_nans, 1 }, {"bounds-check", &flag_bounds_check, 1 }, {"single-precision-constant", &flag_single_precision_constant, 1 }, {"time-report", &time_report, 1 }, {"mem-report", &mem_report, 1 }, { "trapv", &flag_trapv, 1 }, { "wrapv", &flag_wrapv, 1 }, { "new-ra", &flag_new_regalloc, 1 } }; /* Here is a table, controlled by the tm.h file, listing each -m switch and which bits in `target_switches' it should set or clear. If VALUE is positive, it is bits to set. If VALUE is negative, -VALUE is bits to clear. (The sign bit is not used so there is no confusion.) */ static const struct { const char *const name; const int value; const char *const description; } target_switches[] = TARGET_SWITCHES; /* This table is similar, but allows the switch to have a value. */ #ifdef TARGET_OPTIONS static const struct { const char *const prefix; const char **const variable; const char *const description; const char *const value; } target_options[] = TARGET_OPTIONS; #endif /* Nonzero means warn about function definitions that default the return type or that use a null return and have a return-type other than void. */ int warn_return_type; /* Output files for assembler code (real compiler output) and debugging dumps. */ FILE *asm_out_file; FILE *aux_info_file; FILE *rtl_dump_file = NULL; FILE *cgraph_dump_file = NULL; /* The current working directory of a translation. It's generally the directory from which compilation was initiated, but a preprocessed file may specify the original directory in which it was created. */ static const char *src_pwd; /* Initialize src_pwd with the given string, and return true. If it was already initialized, return false. As a special case, it may be called with a NULL argument to test whether src_pwd has NOT been initialized yet. */ bool set_src_pwd (const char *pwd) { if (src_pwd) return false; src_pwd = xstrdup (pwd); return true; } /* Return the directory from which the translation unit was initiated, in case set_src_pwd() was not called before to assign it a different value. */ const char * get_src_pwd (void) { if (! src_pwd) src_pwd = getpwd (); return src_pwd; } /* Called when the start of a function definition is parsed, this function prints on stderr the name of the function. */ void announce_function (tree decl) { if (!quiet_flag) { if (rtl_dump_and_exit) verbatim ("%s ", IDENTIFIER_POINTER (DECL_NAME (decl))); else verbatim (" %s", (*lang_hooks.decl_printable_name) (decl, 2)); fflush (stderr); pp_needs_newline (global_dc->printer) = true; diagnostic_set_last_function (global_dc); } } /* Set up a default flag_random_seed and local_tick, unless the user already specified one. */ static void randomize (void) { if (!flag_random_seed) { unsigned HOST_WIDE_INT value; static char random_seed[HOST_BITS_PER_WIDE_INT / 4 + 3]; /* Get some more or less random data. */ #ifdef HAVE_GETTIMEOFDAY { struct timeval tv; gettimeofday (&tv, NULL); local_tick = tv.tv_sec * 1000 + tv.tv_usec / 1000; } #else { time_t now = time (NULL); if (now != (time_t)-1) local_tick = (unsigned) now; } #endif value = local_tick ^ getpid (); sprintf (random_seed, HOST_WIDE_INT_PRINT_HEX, value); flag_random_seed = random_seed; } else if (!local_tick) local_tick = -1; } /* Decode the string P as an integral parameter. If the string is indeed an integer return its numeric value else issue an Invalid Option error for the option PNAME and return DEFVAL. If PNAME is zero just return DEFVAL, do not call error. */ int read_integral_parameter (const char *p, const char *pname, const int defval) { const char *endp = p; while (*endp) { if (ISDIGIT (*endp)) endp++; else break; } if (*endp != 0) { if (pname != 0) error ("invalid option argument `%s'", pname); return defval; } return atoi (p); } /* Return the logarithm of X, base 2, considering X unsigned, if X is a power of 2. Otherwise, returns -1. This should be used via the `exact_log2' macro. */ int exact_log2_wide (unsigned HOST_WIDE_INT x) { int log = 0; /* Test for 0 or a power of 2. */ if (x == 0 || x != (x & -x)) return -1; while ((x >>= 1) != 0) log++; return log; } /* Given X, an unsigned number, return the largest int Y such that 2**Y <= X. If X is 0, return -1. This should be used via the floor_log2 macro. */ int floor_log2_wide (unsigned HOST_WIDE_INT x) { int log = -1; while (x != 0) log++, x >>= 1; return log; } /* Handler for fatal signals, such as SIGSEGV. These are transformed into ICE messages, which is much more user friendly. In case the error printer crashes, reset the signal to prevent infinite recursion. */ static void crash_signal (int signo) { signal (signo, SIG_DFL); internal_error ("%s", strsignal (signo)); } /* Arrange to dump core on error. (The regular error message is still printed first, except in the case of abort().) */ static void setup_core_dumping (void) { #ifdef SIGABRT signal (SIGABRT, SIG_DFL); #endif #if defined(HAVE_SETRLIMIT) { struct rlimit rlim; if (getrlimit (RLIMIT_CORE, &rlim) != 0) fatal_error ("getting core file size maximum limit: %m"); rlim.rlim_cur = rlim.rlim_max; if (setrlimit (RLIMIT_CORE, &rlim) != 0) fatal_error ("setting core file size limit to maximum: %m"); } #endif diagnostic_abort_on_error (global_dc); } /* Strip off a legitimate source ending from the input string NAME of length LEN. Rather than having to know the names used by all of our front ends, we strip off an ending of a period followed by up to five characters. (Java uses ".class".) */ void strip_off_ending (char *name, int len) { int i; for (i = 2; i < 6 && len > i; i++) { if (name[len - i] == '.') { name[len - i] = '\0'; break; } } } /* Output a quoted string. */ void output_quoted_string (FILE *asm_file, const char *string) { #ifdef OUTPUT_QUOTED_STRING OUTPUT_QUOTED_STRING (asm_file, string); #else char c; putc ('\"', asm_file); while ((c = *string++) != 0) { if (ISPRINT (c)) { if (c == '\"' || c == '\\') putc ('\\', asm_file); putc (c, asm_file); } else fprintf (asm_file, "\\%03o", (unsigned char) c); } putc ('\"', asm_file); #endif } /* Output a file name in the form wanted by System V. */ void output_file_directive (FILE *asm_file, const char *input_name) { int len; const char *na; if (input_name == NULL) input_name = ""; len = strlen (input_name); na = input_name + len; /* NA gets INPUT_NAME sans directory names. */ while (na > input_name) { if (IS_DIR_SEPARATOR (na[-1])) break; na--; } #ifdef ASM_OUTPUT_SOURCE_FILENAME ASM_OUTPUT_SOURCE_FILENAME (asm_file, na); #else fprintf (asm_file, "\t.file\t"); output_quoted_string (asm_file, na); fputc ('\n', asm_file); #endif } /* Routine to open a dump file. Return true if the dump file is enabled. */ static int open_dump_file (enum dump_file_index index, tree decl) { char *dump_name; const char *open_arg; char seq[16]; if (! dump_file[index].enabled) return 0; timevar_push (TV_DUMP); if (rtl_dump_file != NULL) fclose (rtl_dump_file); sprintf (seq, DUMPFILE_FORMAT, index); if (! dump_file[index].initialized) { /* If we've not initialized the files, do so now. */ if (graph_dump_format != no_graph && dump_file[index].graph_dump_p) { dump_name = concat (seq, dump_file[index].extension, NULL); clean_graph_dump_file (dump_base_name, dump_name); free (dump_name); } dump_file[index].initialized = 1; open_arg = "w"; } else open_arg = "a"; dump_name = concat (dump_base_name, seq, dump_file[index].extension, NULL); rtl_dump_file = fopen (dump_name, open_arg); if (rtl_dump_file == NULL) fatal_error ("can't open %s: %m", dump_name); free (dump_name); if (decl) fprintf (rtl_dump_file, "\n;; Function %s%s\n\n", (*lang_hooks.decl_printable_name) (decl, 2), cfun->function_frequency == FUNCTION_FREQUENCY_HOT ? " (hot)" : cfun->function_frequency == FUNCTION_FREQUENCY_UNLIKELY_EXECUTED ? " (unlikely executed)" : ""); timevar_pop (TV_DUMP); return 1; } /* Routine to close a dump file. */ static void close_dump_file (enum dump_file_index index, void (*func) (FILE *, rtx), rtx insns) { if (! rtl_dump_file) return; timevar_push (TV_DUMP); if (insns && graph_dump_format != no_graph && dump_file[index].graph_dump_p) { char seq[16]; char *suffix; sprintf (seq, DUMPFILE_FORMAT, index); suffix = concat (seq, dump_file[index].extension, NULL); print_rtl_graph_with_bb (dump_base_name, suffix, insns); free (suffix); } if (func && insns) func (rtl_dump_file, insns); fflush (rtl_dump_file); fclose (rtl_dump_file); rtl_dump_file = NULL; timevar_pop (TV_DUMP); } /* Do any final processing required for the declarations in VEC, of which there are LEN. We write out inline functions and variables that have been deferred until this point, but which are required. Returns nonzero if anything was put out. */ int wrapup_global_declarations (tree *vec, int len) { tree decl; int i; int reconsider; int output_something = 0; for (i = 0; i < len; i++) { decl = vec[i]; /* We're not deferring this any longer. Assignment is conditional to avoid needlessly dirtying PCH pages. */ if (DECL_DEFER_OUTPUT (decl) != 0) DECL_DEFER_OUTPUT (decl) = 0; if (TREE_CODE (decl) == VAR_DECL && DECL_SIZE (decl) == 0) (*lang_hooks.finish_incomplete_decl) (decl); } /* Now emit any global variables or functions that we have been putting off. We need to loop in case one of the things emitted here references another one which comes earlier in the list. */ do { reconsider = 0; for (i = 0; i < len; i++) { decl = vec[i]; if (TREE_ASM_WRITTEN (decl) || DECL_EXTERNAL (decl)) continue; /* Don't write out static consts, unless we still need them. We also keep static consts if not optimizing (for debugging), unless the user specified -fno-keep-static-consts. ??? They might be better written into the debug information. This is possible when using DWARF. A language processor that wants static constants to be always written out (even if it is not used) is responsible for calling rest_of_decl_compilation itself. E.g. the C front-end calls rest_of_decl_compilation from finish_decl. One motivation for this is that is conventional in some environments to write things like: static const char rcsid[] = "... version string ..."; intending to force the string to be in the executable. A language processor that would prefer to have unneeded static constants "optimized away" would just defer writing them out until here. E.g. C++ does this, because static constants are often defined in header files. ??? A tempting alternative (for both C and C++) would be to force a constant to be written if and only if it is defined in a main file, as opposed to an include file. */ if (TREE_CODE (decl) == VAR_DECL && TREE_STATIC (decl)) { bool needed = 1; if (flag_unit_at_a_time && cgraph_varpool_node (decl)->finalized) needed = 0; else if ((flag_unit_at_a_time && !cgraph_global_info_ready) && (TREE_USED (decl) || TREE_USED (DECL_ASSEMBLER_NAME (decl)))) /* needed */; else if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))) /* needed */; else if (DECL_COMDAT (decl)) needed = 0; else if (TREE_READONLY (decl) && !TREE_PUBLIC (decl) && (optimize || !flag_keep_static_consts || DECL_ARTIFICIAL (decl))) needed = 0; if (needed) { reconsider = 1; rest_of_decl_compilation (decl, NULL, 1, 1); } } if (TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl) != 0 && DECL_SAVED_INSNS (decl) != 0 && DECL_SAVED_INSNS (decl)->saved_for_inline && (flag_keep_inline_functions || (TREE_PUBLIC (decl) && !DECL_COMDAT (decl)) || TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)))) { reconsider = 1; output_inline_function (decl); } } if (reconsider) output_something = 1; } while (reconsider); return output_something; } /* Issue appropriate warnings for the global declarations in VEC (of which there are LEN). Output debugging information for them. */ void check_global_declarations (tree *vec, int len) { tree decl; int i; for (i = 0; i < len; i++) { decl = vec[i]; if (TREE_CODE (decl) == VAR_DECL && TREE_STATIC (decl) && ! TREE_ASM_WRITTEN (decl)) /* Cancel the RTL for this decl so that, if debugging info output for global variables is still to come, this one will be omitted. */ SET_DECL_RTL (decl, NULL_RTX); /* Warn about any function declared static but not defined. We don't warn about variables, because many programs have static variables that exist only to get some text into the object file. */ if (TREE_CODE (decl) == FUNCTION_DECL && (warn_unused_function || TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))) && DECL_INITIAL (decl) == 0 && DECL_EXTERNAL (decl) && ! DECL_ARTIFICIAL (decl) && ! TREE_PUBLIC (decl)) { if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))) pedwarn ("%J'%F' used but never defined", decl, decl); else warning ("%J'%F' declared `static' but never defined", decl, decl); /* This symbol is effectively an "extern" declaration now. */ TREE_PUBLIC (decl) = 1; assemble_external (decl); } /* Warn about static fns or vars defined but not used. */ if (((warn_unused_function && TREE_CODE (decl) == FUNCTION_DECL) /* We don't warn about "static const" variables because the "rcs_id" idiom uses that construction. */ || (warn_unused_variable && TREE_CODE (decl) == VAR_DECL && ! TREE_READONLY (decl))) && ! DECL_IN_SYSTEM_HEADER (decl) && ! TREE_USED (decl) /* The TREE_USED bit for file-scope decls is kept in the identifier, to handle multiple external decls in different scopes. */ && ! TREE_USED (DECL_NAME (decl)) && ! DECL_EXTERNAL (decl) && ! TREE_PUBLIC (decl) /* A volatile variable might be used in some non-obvious way. */ && ! TREE_THIS_VOLATILE (decl) /* Global register variables must be declared to reserve them. */ && ! (TREE_CODE (decl) == VAR_DECL && DECL_REGISTER (decl)) /* Otherwise, ask the language. */ && (*lang_hooks.decls.warn_unused_global) (decl)) warning ("%J'%D' defined but not used", decl, decl); /* Avoid confusing the debug information machinery when there are errors. */ if (errorcount == 0 && sorrycount == 0) { timevar_push (TV_SYMOUT); (*debug_hooks->global_decl) (decl); timevar_pop (TV_SYMOUT); } } } /* Warn about a use of an identifier which was marked deprecated. */ void warn_deprecated_use (tree node) { if (node == 0 || !warn_deprecated_decl) return; if (DECL_P (node)) warning ("`%s' is deprecated (declared at %s:%d)", IDENTIFIER_POINTER (DECL_NAME (node)), DECL_SOURCE_FILE (node), DECL_SOURCE_LINE (node)); else if (TYPE_P (node)) { const char *what = NULL; tree decl = TYPE_STUB_DECL (node); if (TREE_CODE (TYPE_NAME (node)) == IDENTIFIER_NODE) what = IDENTIFIER_POINTER (TYPE_NAME (node)); else if (TREE_CODE (TYPE_NAME (node)) == TYPE_DECL && DECL_NAME (TYPE_NAME (node))) what = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (node))); if (what) { if (decl) warning ("`%s' is deprecated (declared at %s:%d)", what, DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl)); else warning ("`%s' is deprecated", what); } else if (decl) warning ("type is deprecated (declared at %s:%d)", DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl)); else warning ("type is deprecated"); } } /* Save the current INPUT_LOCATION on the top entry in the INPUT_FILE_STACK. Push a new entry for FILE and LINE, and set the INPUT_LOCATION accordingly. */ void push_srcloc (const char *file, int line) { struct file_stack *fs; fs = xmalloc (sizeof (struct file_stack)); fs->location = input_location; fs->next = input_file_stack; input_filename = file; input_line = line; input_file_stack = fs; input_file_stack_tick++; } /* Pop the top entry off the stack of presently open source files. Restore the INPUT_LOCATION from the new topmost entry on the stack. */ void pop_srcloc (void) { struct file_stack *fs; fs = input_file_stack; input_location = fs->location; input_file_stack = fs->next; free (fs); input_file_stack_tick++; } /* Compile an entire translation unit. Write a file of assembly output and various debugging dumps. */ static void compile_file (void) { /* Initialize yet another pass. */ init_final (main_input_filename); coverage_init (aux_base_name); timevar_push (TV_PARSE); /* Call the parser, which parses the entire file (calling rest_of_compilation for each function). */ (*lang_hooks.parse_file) (set_yydebug); /* In case there were missing block closers, get us back to the global binding level. */ (*lang_hooks.clear_binding_stack) (); /* Compilation is now finished except for writing what's left of the symbol table output. */ timevar_pop (TV_PARSE); if (flag_syntax_only) return; (*lang_hooks.decls.final_write_globals)(); cgraph_varpool_assemble_pending_decls (); /* This must occur after the loop to output deferred functions. Else the coverage initializer would not be emitted if all the functions in this compilation unit were deferred. */ coverage_finish (); /* Write out any pending weak symbol declarations. */ weak_finish (); /* Do dbx symbols. */ timevar_push (TV_SYMOUT); #ifdef DWARF2_UNWIND_INFO if (dwarf2out_do_frame ()) dwarf2out_frame_finish (); #endif (*debug_hooks->finish) (main_input_filename); timevar_pop (TV_SYMOUT); /* Output some stuff at end of file if nec. */ dw2_output_indirect_constants (); /* Flush any pending equate directives. */ process_pending_assemble_output_defs (); if (profile_arc_flag || flag_test_coverage || flag_branch_probabilities) { timevar_push (TV_DUMP); open_dump_file (DFI_bp, NULL); end_branch_prob (); close_dump_file (DFI_bp, NULL, NULL_RTX); timevar_pop (TV_DUMP); } targetm.asm_out.file_end (); /* Attach a special .ident directive to the end of the file to identify the version of GCC which compiled this code. The format of the .ident string is patterned after the ones produced by native SVR4 compilers. */ #ifdef IDENT_ASM_OP if (!flag_no_ident) fprintf (asm_out_file, "%s\"GCC: (GNU) %s\"\n", IDENT_ASM_OP, version_string); #endif if (optimize > 0 && open_dump_file (DFI_combine, NULL)) { timevar_push (TV_DUMP); dump_combine_total_stats (rtl_dump_file); close_dump_file (DFI_combine, NULL, NULL_RTX); timevar_pop (TV_DUMP); } } /* This is called from various places for FUNCTION_DECL, VAR_DECL, and TYPE_DECL nodes. This does nothing for local (non-static) variables, unless the variable is a register variable with an ASMSPEC. In that case, or if the variable is not an automatic, it sets up the RTL and outputs any assembler code (label definition, storage allocation and initialization). DECL is the declaration. If ASMSPEC is nonzero, it specifies the assembler symbol name to be used. TOP_LEVEL is nonzero if this declaration is not within a function. */ void rest_of_decl_compilation (tree decl, const char *asmspec, int top_level, int at_end) { /* We deferred calling assemble_alias so that we could collect other attributes such as visibility. Emit the alias now. */ { tree alias; alias = lookup_attribute ("alias", DECL_ATTRIBUTES (decl)); if (alias) { alias = TREE_VALUE (TREE_VALUE (alias)); alias = get_identifier (TREE_STRING_POINTER (alias)); assemble_alias (decl, alias); } } /* Forward declarations for nested functions are not "external", but we need to treat them as if they were. */ if (TREE_STATIC (decl) || DECL_EXTERNAL (decl) || TREE_CODE (decl) == FUNCTION_DECL) { timevar_push (TV_VARCONST); if (asmspec) make_decl_rtl (decl, asmspec); /* Don't output anything when a tentative file-scope definition is seen. But at end of compilation, do output code for them. We do output all variables when unit-at-a-time is active and rely on callgraph code to defer them except for forward declarations (see gcc.c-torture/compile/920624-1.c) */ if ((at_end || !DECL_DEFER_OUTPUT (decl) || (flag_unit_at_a_time && DECL_INITIAL (decl))) && !DECL_EXTERNAL (decl)) { if (flag_unit_at_a_time && !cgraph_global_info_ready && TREE_CODE (decl) != FUNCTION_DECL && top_level) cgraph_varpool_finalize_decl (decl); else assemble_variable (decl, top_level, at_end, 0); } #ifdef ASM_FINISH_DECLARE_OBJECT if (decl == last_assemble_variable_decl) { ASM_FINISH_DECLARE_OBJECT (asm_out_file, decl, top_level, at_end); } #endif timevar_pop (TV_VARCONST); } else if (DECL_REGISTER (decl) && asmspec != 0) { if (decode_reg_name (asmspec) >= 0) { SET_DECL_RTL (decl, NULL_RTX); make_decl_rtl (decl, asmspec); } else { error ("invalid register name `%s' for register variable", asmspec); DECL_REGISTER (decl) = 0; if (!top_level) expand_decl (decl); } } #if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) else if ((write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG) && TREE_CODE (decl) == TYPE_DECL) { timevar_push (TV_SYMOUT); dbxout_symbol (decl, 0); timevar_pop (TV_SYMOUT); } #endif #ifdef SDB_DEBUGGING_INFO else if (write_symbols == SDB_DEBUG && top_level && TREE_CODE (decl) == TYPE_DECL) { timevar_push (TV_SYMOUT); sdbout_symbol (decl, 0); timevar_pop (TV_SYMOUT); } #endif #ifdef DWARF2_DEBUGGING_INFO else if ((write_symbols == DWARF2_DEBUG || write_symbols == VMS_AND_DWARF2_DEBUG) && top_level && TREE_CODE (decl) == TYPE_DECL) { timevar_push (TV_SYMOUT); dwarf2out_decl (decl); timevar_pop (TV_SYMOUT); } #endif } /* Called after finishing a record, union or enumeral type. */ void rest_of_type_compilation ( #if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) \ || defined (SDB_DEBUGGING_INFO) || defined (DWARF2_DEBUGGING_INFO) tree type, int toplev #else tree type ATTRIBUTE_UNUSED, int toplev ATTRIBUTE_UNUSED #endif ) { /* Avoid confusing the debug information machinery when there are errors. */ if (errorcount != 0 || sorrycount != 0) return; timevar_push (TV_SYMOUT); #if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG) dbxout_symbol (TYPE_STUB_DECL (type), !toplev); #endif #ifdef SDB_DEBUGGING_INFO if (write_symbols == SDB_DEBUG) sdbout_symbol (TYPE_STUB_DECL (type), !toplev); #endif #ifdef DWARF2_DEBUGGING_INFO if ((write_symbols == DWARF2_DEBUG || write_symbols == VMS_AND_DWARF2_DEBUG) && toplev) dwarf2out_decl (TYPE_STUB_DECL (type)); #endif timevar_pop (TV_SYMOUT); } /* Turn the RTL into assembly. */ static void rest_of_handle_final (tree decl, rtx insns) { timevar_push (TV_FINAL); { rtx x; const char *fnname; /* Get the function's name, as described by its RTL. This may be different from the DECL_NAME name used in the source file. */ x = DECL_RTL (decl); if (GET_CODE (x) != MEM) abort (); x = XEXP (x, 0); if (GET_CODE (x) != SYMBOL_REF) abort (); fnname = XSTR (x, 0); assemble_start_function (decl, fnname); final_start_function (insns, asm_out_file, optimize); final (insns, asm_out_file, optimize, 0); final_end_function (); #ifdef IA64_UNWIND_INFO /* ??? The IA-64 ".handlerdata" directive must be issued before the ".endp" directive that closes the procedure descriptor. */ output_function_exception_table (); #endif assemble_end_function (decl, fnname); #ifndef IA64_UNWIND_INFO /* Otherwise, it feels unclean to switch sections in the middle. */ output_function_exception_table (); #endif if (! quiet_flag) fflush (asm_out_file); /* Release all memory allocated by flow. */ free_basic_block_vars (0); /* Release all memory held by regsets now. */ regset_release_memory (); } timevar_pop (TV_FINAL); ggc_collect (); } #ifdef DELAY_SLOTS /* Run delay slot optimization. */ static void rest_of_handle_delay_slots (tree decl, rtx insns) { timevar_push (TV_DBR_SCHED); open_dump_file (DFI_dbr, decl); dbr_schedule (insns, rtl_dump_file); close_dump_file (DFI_dbr, print_rtl, insns); timevar_pop (TV_DBR_SCHED); ggc_collect (); } #endif #ifdef STACK_REGS /* Convert register usage from flat register file usage to a stack register file. */ static void rest_of_handle_stack_regs (tree decl, rtx insns) { #if defined (HAVE_ATTR_length) /* If flow2 creates new instructions which need splitting and scheduling after reload is not done, they might not be split until final which doesn't allow splitting if HAVE_ATTR_length. */ #ifdef INSN_SCHEDULING if (optimize && !flag_schedule_insns_after_reload) #else if (optimize) #endif { timevar_push (TV_SHORTEN_BRANCH); split_all_insns (1); timevar_pop (TV_SHORTEN_BRANCH); } #endif timevar_push (TV_REG_STACK); open_dump_file (DFI_stack, decl); if (reg_to_stack (insns, rtl_dump_file) && optimize) { if (cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_POST_REGSTACK | (flag_crossjumping ? CLEANUP_CROSSJUMP : 0)) && flag_reorder_blocks) { reorder_basic_blocks (0); cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_POST_REGSTACK); } } close_dump_file (DFI_stack, print_rtl_with_bb, insns); timevar_pop (TV_REG_STACK); ggc_collect (); } #endif /* Machine independent reorg pass. */ static void rest_of_handle_machine_reorg (tree decl, rtx insns) { timevar_push (TV_MACH_DEP); open_dump_file (DFI_mach, decl); (*targetm.machine_dependent_reorg) (); close_dump_file (DFI_mach, print_rtl, insns); timevar_pop (TV_MACH_DEP); ggc_collect (); } /* Run new register allocator. Return TRUE if we must exit rest_of_compilation upon return. */ static bool rest_of_handle_new_regalloc (tree decl, rtx insns) { int failure; delete_trivially_dead_insns (insns, max_reg_num ()); reg_alloc (); timevar_pop (TV_LOCAL_ALLOC); if (dump_file[DFI_lreg].enabled) { timevar_push (TV_DUMP); close_dump_file (DFI_lreg, NULL, NULL); timevar_pop (TV_DUMP); } /* XXX clean up the whole mess to bring live info in shape again. */ timevar_push (TV_GLOBAL_ALLOC); open_dump_file (DFI_greg, decl); build_insn_chain (insns); failure = reload (insns, 0); timevar_pop (TV_GLOBAL_ALLOC); if (dump_file[DFI_greg].enabled) { timevar_push (TV_DUMP); dump_global_regs (rtl_dump_file); close_dump_file (DFI_greg, print_rtl_with_bb, insns); timevar_pop (TV_DUMP); } if (failure) return true; reload_completed = 1; return false; } /* Run old register allocator. Return TRUE if we must exit rest_of_compilation upon return. */ static bool rest_of_handle_old_regalloc (tree decl, rtx insns) { int failure; int rebuild_notes; /* Allocate the reg_renumber array. */ allocate_reg_info (max_regno, FALSE, TRUE); /* And the reg_equiv_memory_loc array. */ reg_equiv_memory_loc = xcalloc (max_regno, sizeof (rtx)); allocate_initial_values (reg_equiv_memory_loc); regclass (insns, max_reg_num (), rtl_dump_file); rebuild_notes = local_alloc (); timevar_pop (TV_LOCAL_ALLOC); /* Local allocation may have turned an indirect jump into a direct jump. If so, we must rebuild the JUMP_LABEL fields of jumping instructions. */ if (rebuild_notes) { timevar_push (TV_JUMP); rebuild_jump_labels (insns); purge_all_dead_edges (0); timevar_pop (TV_JUMP); } if (dump_file[DFI_lreg].enabled) { timevar_push (TV_DUMP); dump_flow_info (rtl_dump_file); dump_local_alloc (rtl_dump_file); close_dump_file (DFI_lreg, print_rtl_with_bb, insns); timevar_pop (TV_DUMP); } ggc_collect (); timevar_push (TV_GLOBAL_ALLOC); open_dump_file (DFI_greg, decl); /* If optimizing, allocate remaining pseudo-regs. Do the reload pass fixing up any insns that are invalid. */ if (optimize) failure = global_alloc (rtl_dump_file); else { build_insn_chain (insns); failure = reload (insns, 0); } timevar_pop (TV_GLOBAL_ALLOC); if (dump_file[DFI_greg].enabled) { timevar_push (TV_DUMP); dump_global_regs (rtl_dump_file); close_dump_file (DFI_greg, print_rtl_with_bb, insns); timevar_pop (TV_DUMP); } return failure; } /* Run the regrename and cprop passes. */ static void rest_of_handle_regrename (tree decl, rtx insns) { timevar_push (TV_RENAME_REGISTERS); open_dump_file (DFI_rnreg, decl); if (flag_rename_registers) regrename_optimize (); if (flag_cprop_registers) copyprop_hardreg_forward (); close_dump_file (DFI_rnreg, print_rtl_with_bb, insns); timevar_pop (TV_RENAME_REGISTERS); } /* Reorder basic blocks. */ static void rest_of_handle_reorder_blocks (tree decl, rtx insns) { bool changed; unsigned int liveness_flags; open_dump_file (DFI_bbro, decl); /* Last attempt to optimize CFG, as scheduling, peepholing and insn splitting possibly introduced more crossjumping opportunities. */ liveness_flags = (!HAVE_conditional_execution ? CLEANUP_UPDATE_LIFE : 0); changed = cleanup_cfg (CLEANUP_EXPENSIVE | liveness_flags); if (flag_sched2_use_traces && flag_schedule_insns_after_reload) tracer (liveness_flags); if (flag_reorder_blocks) reorder_basic_blocks (liveness_flags); if (flag_reorder_blocks || (flag_sched2_use_traces && flag_schedule_insns_after_reload)) changed |= cleanup_cfg (CLEANUP_EXPENSIVE | liveness_flags); /* On conditional execution targets we can not update the life cheaply, so we deffer the updating to after both cleanups. This may lose some cases but should not be terribly bad. */ if (changed && HAVE_conditional_execution) update_life_info (NULL, UPDATE_LIFE_GLOBAL_RM_NOTES, PROP_DEATH_NOTES | PROP_REG_INFO); close_dump_file (DFI_bbro, print_rtl_with_bb, insns); } #ifdef INSN_SCHEDULING /* Run instruction scheduler. */ static void rest_of_handle_sched (tree decl, rtx insns) { timevar_push (TV_SCHED); /* Print function header into sched dump now because doing the sched analysis makes some of the dump. */ if (optimize > 0 && flag_schedule_insns) { open_dump_file (DFI_sched, decl); /* Do control and data sched analysis, and write some of the results to dump file. */ schedule_insns (rtl_dump_file); close_dump_file (DFI_sched, print_rtl_with_bb, insns); } timevar_pop (TV_SCHED); ggc_collect (); } /* Run second scheduling pass after reload. */ static void rest_of_handle_sched2 (tree decl, rtx insns) { timevar_push (TV_SCHED2); open_dump_file (DFI_sched2, decl); /* Do control and data sched analysis again, and write some more of the results to dump file. */ split_all_insns (1); if (flag_sched2_use_superblocks || flag_sched2_use_traces) { schedule_ebbs (rtl_dump_file); /* No liveness updating code yet, but it should be easy to do. reg-stack recompute the liveness when needed for now. */ count_or_remove_death_notes (NULL, 1); cleanup_cfg (CLEANUP_EXPENSIVE); } else schedule_insns (rtl_dump_file); close_dump_file (DFI_sched2, print_rtl_with_bb, insns); timevar_pop (TV_SCHED2); ggc_collect (); } #endif /* Register allocation pre-pass, to reduce number of moves necessary for two-address machines. */ static void rest_of_handle_regmove (tree decl, rtx insns) { timevar_push (TV_REGMOVE); open_dump_file (DFI_regmove, decl); regmove_optimize (insns, max_reg_num (), rtl_dump_file); cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_UPDATE_LIFE); close_dump_file (DFI_regmove, print_rtl_with_bb, insns); timevar_pop (TV_REGMOVE); ggc_collect (); } /* Run tracer. */ static void rest_of_handle_tracer (tree decl, rtx insns) { open_dump_file (DFI_tracer, decl); if (rtl_dump_file) dump_flow_info (rtl_dump_file); tracer (0); cleanup_cfg (CLEANUP_EXPENSIVE); reg_scan (insns, max_reg_num (), 0); close_dump_file (DFI_tracer, print_rtl_with_bb, get_insns ()); } /* If-conversion and CFG cleanup. */ static void rest_of_handle_if_conversion (tree decl, rtx insns) { open_dump_file (DFI_ce1, decl); if (flag_if_conversion) { timevar_push (TV_IFCVT); if (rtl_dump_file) dump_flow_info (rtl_dump_file); cleanup_cfg (CLEANUP_EXPENSIVE); reg_scan (insns, max_reg_num (), 0); if_convert (0); timevar_pop (TV_IFCVT); } timevar_push (TV_JUMP); cleanup_cfg (CLEANUP_EXPENSIVE); reg_scan (insns, max_reg_num (), 0); timevar_pop (TV_JUMP); close_dump_file (DFI_ce1, print_rtl_with_bb, get_insns ()); } /* Rerun if-conversion, as combine may have simplified things enough to now meet sequence length restrictions. */ static void rest_of_handle_if_after_combine (tree decl, rtx insns) { timevar_push (TV_IFCVT); open_dump_file (DFI_ce2, decl); no_new_pseudos = 0; if_convert (1); no_new_pseudos = 1; close_dump_file (DFI_ce2, print_rtl_with_bb, insns); timevar_pop (TV_IFCVT); } static void rest_of_handle_web (tree decl, rtx insns) { open_dump_file (DFI_web, decl); timevar_push (TV_WEB); web_main (); delete_trivially_dead_insns (insns, max_reg_num ()); cleanup_cfg (CLEANUP_EXPENSIVE); timevar_pop (TV_WEB); close_dump_file (DFI_web, print_rtl_with_bb, insns); reg_scan (get_insns (), max_reg_num (), 0); } /* Do branch profiling and static profile estimation passes. */ static void rest_of_handle_branch_prob (tree decl, rtx insns) { struct loops loops; timevar_push (TV_BRANCH_PROB); open_dump_file (DFI_bp, decl); if (profile_arc_flag || flag_test_coverage || flag_branch_probabilities) branch_prob (); /* Discover and record the loop depth at the head of each basic block. The loop infrastructure does the real job for us. */ flow_loops_find (&loops, LOOP_TREE); if (rtl_dump_file) flow_loops_dump (&loops, rtl_dump_file, NULL, 0); /* Estimate using heuristics if no profiling info is available. */ if (flag_guess_branch_prob) estimate_probability (&loops); flow_loops_free (&loops); free_dominance_info (CDI_DOMINATORS); close_dump_file (DFI_bp, print_rtl_with_bb, insns); timevar_pop (TV_BRANCH_PROB); } /* Do optimizations based on expression value profiles. */ static void rest_of_handle_value_profile_transformations (tree decl, rtx insns) { open_dump_file (DFI_vpt, decl); timevar_push (TV_VPT); if (value_profile_transformations ()) cleanup_cfg (CLEANUP_EXPENSIVE); timevar_pop (TV_VPT); close_dump_file (DFI_vpt, print_rtl_with_bb, insns); } /* Do control and data flow analysis; write some of the results to the dump file. */ static void rest_of_handle_cfg (tree decl, rtx insns) { open_dump_file (DFI_cfg, decl); if (rtl_dump_file) dump_flow_info (rtl_dump_file); if (optimize) cleanup_cfg (CLEANUP_EXPENSIVE | (flag_thread_jumps ? CLEANUP_THREADING : 0)); /* It may make more sense to mark constant functions after dead code is eliminated by life_analysis, but we need to do it early, as -fprofile-arcs may insert code making function non-constant, but we still must consider it as constant, otherwise -fbranch-probabilities will not read data back. life_analysis rarely eliminates modification of external memory. */ if (optimize) { /* Alias analysis depends on this information and mark_constant_function depends on alias analysis. */ reg_scan (insns, max_reg_num (), 1); mark_constant_function (); } close_dump_file (DFI_cfg, print_rtl_with_bb, insns); } /* Purge addressofs. */ static void rest_of_handle_addressof (tree decl, rtx insns) { open_dump_file (DFI_addressof, decl); purge_addressof (insns); if (optimize && purge_all_dead_edges (0)) delete_unreachable_blocks (); reg_scan (insns, max_reg_num (), 1); close_dump_file (DFI_addressof, print_rtl, insns); } /* We may have potential sibling or tail recursion sites. Select one (of possibly multiple) methods of performing the call. */ static void rest_of_handle_sibling_calls (rtx insns) { rtx insn; optimize_sibling_and_tail_recursive_calls (); /* Recompute the CFG as sibling optimization clobbers it randomly. */ free_bb_for_insn (); find_exception_handler_labels (); rebuild_jump_labels (insns); find_basic_blocks (insns, max_reg_num (), rtl_dump_file); /* There is pass ordering problem - we must lower NOTE_INSN_PREDICTION notes before simplifying cfg and we must do lowering after sibcall that unhides parts of RTL chain and cleans up the CFG. Until sibcall is replaced by tree-level optimizer, lets just sweep away the NOTE_INSN_PREDICTION notes that leaked out. */ for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) == NOTE_INSN_PREDICTION) delete_insn (insn); close_dump_file (DFI_sibling, print_rtl, get_insns ()); } /* Perform jump bypassing and control flow optimizations. */ static void rest_of_handle_jump_bypass (tree decl, rtx insns) { timevar_push (TV_BYPASS); open_dump_file (DFI_bypass, decl); cleanup_cfg (CLEANUP_EXPENSIVE); reg_scan (insns, max_reg_num (), 1); if (bypass_jumps (rtl_dump_file)) { rebuild_jump_labels (insns); cleanup_cfg (CLEANUP_EXPENSIVE); delete_trivially_dead_insns (insns, max_reg_num ()); } close_dump_file (DFI_bypass, print_rtl_with_bb, insns); timevar_pop (TV_BYPASS); ggc_collect (); #ifdef ENABLE_CHECKING verify_flow_info (); #endif } /* Handle inlining of functions in rest_of_compilation. Return TRUE if we must exit rest_of_compilation upon return. */ static bool rest_of_handle_inlining (tree decl) { rtx insns; int inlinable = 0; tree parent; const char *lose; /* If we are reconsidering an inline function at the end of compilation, skip the stuff for making it inline. */ if (cfun->rtl_inline_init) return 0; cfun->rtl_inline_init = 1; /* If this is nested inside an inlined external function, pretend it was only declared. Since we cannot inline such functions, generating code for this one is not only not necessary but will confuse some debugging output writers. */ for (parent = DECL_CONTEXT (current_function_decl); parent != NULL_TREE; parent = get_containing_scope (parent)) if (TREE_CODE (parent) == FUNCTION_DECL && DECL_INLINE (parent) && DECL_EXTERNAL (parent)) { DECL_INITIAL (decl) = 0; return true; } else if (TYPE_P (parent)) /* A function in a local class should be treated normally. */ break; /* If requested, consider whether to make this function inline. */ if ((DECL_INLINE (decl) && !flag_no_inline) || flag_inline_functions) { timevar_push (TV_INTEGRATION); lose = function_cannot_inline_p (decl); timevar_pop (TV_INTEGRATION); if (lose || ! optimize) { if (warn_inline && lose && DECL_INLINE (decl)) { char *msg = concat ("%J", lose, NULL); warning (msg, decl); free (msg); } DECL_ABSTRACT_ORIGIN (decl) = 0; /* Don't really compile an extern inline function. If we can't make it inline, pretend it was only declared. */ if (DECL_EXTERNAL (decl)) { DECL_INITIAL (decl) = 0; return true; } } else inlinable = DECL_INLINE (decl) = 1; } insns = get_insns (); /* Dump the rtl code if we are dumping rtl. */ if (open_dump_file (DFI_rtl, decl)) { if (DECL_SAVED_INSNS (decl) && DECL_SAVED_INSNS (decl)->saved_for_inline) fprintf (rtl_dump_file, ";; (integrable)\n\n"); close_dump_file (DFI_rtl, print_rtl, insns); } /* Convert from NOTE_INSN_EH_REGION style notes, and do other sorts of eh initialization. Delay this until after the initial rtl dump so that we can see the original nesting. */ convert_from_eh_region_ranges (); /* If function is inline, and we don't yet know whether to compile it by itself, defer decision till end of compilation. wrapup_global_declarations will (indirectly) call rest_of_compilation again for those functions that need to be output. Also defer those functions that we are supposed to defer. */ if (inlinable || (DECL_INLINE (decl) /* Egad. This RTL deferral test conflicts with Fortran assumptions for unreferenced symbols. See g77.f-torture/execute/980520-1.f. But removing this line from the check breaks all languages that use the call graph to output symbols. This hard-coded check is the least invasive work-around. Nested functions need to be deferred too. */ && (flag_inline_functions || strcmp (lang_hooks.name, "GNU F77") == 0 || (cgraph_n_nodes > 0 && cgraph_node (decl)->origin)) && ((! TREE_PUBLIC (decl) && ! TREE_ADDRESSABLE (decl) && ! TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)) && ! flag_keep_inline_functions) || DECL_EXTERNAL (decl)))) DECL_DEFER_OUTPUT (decl) = 1; if (DECL_INLINE (decl)) /* DWARF wants separate debugging info for abstract and concrete instances of all inline functions, including those declared inline but not inlined, and those inlined even though they weren't declared inline. Conveniently, that's what DECL_INLINE means at this point. */ (*debug_hooks->deferred_inline_function) (decl); if (DECL_DEFER_OUTPUT (decl)) { /* If -Wreturn-type, we have to do a bit of compilation. We just want to call cleanup the cfg to figure out whether or not we can fall off the end of the function; we do the minimum amount of work necessary to make that safe. */ if (warn_return_type) { int saved_optimize = optimize; optimize = 0; rebuild_jump_labels (insns); find_exception_handler_labels (); find_basic_blocks (insns, max_reg_num (), rtl_dump_file); cleanup_cfg (CLEANUP_PRE_SIBCALL | CLEANUP_PRE_LOOP); optimize = saved_optimize; /* CFG is no longer maintained up-to-date. */ free_bb_for_insn (); } set_nothrow_function_flags (); if (current_function_nothrow) /* Now we know that this can't throw; set the flag for the benefit of other functions later in this translation unit. */ TREE_NOTHROW (current_function_decl) = 1; timevar_push (TV_INTEGRATION); save_for_inline (decl); timevar_pop (TV_INTEGRATION); DECL_SAVED_INSNS (decl)->inlinable = inlinable; return true; } /* If specified extern inline but we aren't inlining it, we are done. This goes for anything that gets here with DECL_EXTERNAL set, not just things with DECL_INLINE. */ return (bool) DECL_EXTERNAL (decl); } /* Try to identify useless null pointer tests and delete them. */ static void rest_of_handle_null_pointer (tree decl, rtx insns) { open_dump_file (DFI_null, decl); if (rtl_dump_file) dump_flow_info (rtl_dump_file); if (delete_null_pointer_checks (insns)) cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP); close_dump_file (DFI_null, print_rtl_with_bb, insns); } /* Try combining insns through substitution. */ static void rest_of_handle_combine (tree decl, rtx insns) { int rebuild_jump_labels_after_combine = 0; timevar_push (TV_COMBINE); open_dump_file (DFI_combine, decl); rebuild_jump_labels_after_combine = combine_instructions (insns, max_reg_num ()); /* Combining insns may have turned an indirect jump into a direct jump. Rebuild the JUMP_LABEL fields of jumping instructions. */ if (rebuild_jump_labels_after_combine) { timevar_push (TV_JUMP); rebuild_jump_labels (insns); timevar_pop (TV_JUMP); cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_UPDATE_LIFE); } close_dump_file (DFI_combine, print_rtl_with_bb, insns); timevar_pop (TV_COMBINE); ggc_collect (); } /* Perform life analysis. */ static void rest_of_handle_life (tree decl, rtx insns) { open_dump_file (DFI_life, decl); regclass_init (); #ifdef ENABLE_CHECKING verify_flow_info (); #endif life_analysis (insns, rtl_dump_file, PROP_FINAL); if (optimize) cleanup_cfg ((optimize ? CLEANUP_EXPENSIVE : 0) | CLEANUP_UPDATE_LIFE | CLEANUP_LOG_LINKS | (flag_thread_jumps ? CLEANUP_THREADING : 0)); timevar_pop (TV_FLOW); if (warn_uninitialized) { uninitialized_vars_warning (DECL_INITIAL (decl)); if (extra_warnings) setjmp_args_warning (); } if (optimize) { if (!flag_new_regalloc && initialize_uninitialized_subregs ()) { /* Insns were inserted, and possibly pseudos created, so things might look a bit different. */ insns = get_insns (); allocate_reg_life_data (); update_life_info (NULL, UPDATE_LIFE_GLOBAL_RM_NOTES, PROP_LOG_LINKS | PROP_REG_INFO | PROP_DEATH_NOTES); } } no_new_pseudos = 1; close_dump_file (DFI_life, print_rtl_with_bb, insns); ggc_collect (); } /* Perform common subexpression elimination. Nonzero value from `cse_main' means that jumps were simplified and some code may now be unreachable, so do jump optimization again. */ static void rest_of_handle_cse (tree decl, rtx insns) { int tem; open_dump_file (DFI_cse, decl); if (rtl_dump_file) dump_flow_info (rtl_dump_file); timevar_push (TV_CSE); reg_scan (insns, max_reg_num (), 1); tem = cse_main (insns, max_reg_num (), 0, rtl_dump_file); if (tem) rebuild_jump_labels (insns); if (purge_all_dead_edges (0)) delete_unreachable_blocks (); delete_trivially_dead_insns (insns, max_reg_num ()); /* If we are not running more CSE passes, then we are no longer expecting CSE to be run. But always rerun it in a cheap mode. */ cse_not_expected = !flag_rerun_cse_after_loop && !flag_gcse; if (tem || optimize > 1) cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP); /* Try to identify useless null pointer tests and delete them. */ if (flag_delete_null_pointer_checks) { timevar_push (TV_JUMP); if (delete_null_pointer_checks (insns)) cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP); timevar_pop (TV_JUMP); } /* The second pass of jump optimization is likely to have removed a bunch more instructions. */ renumber_insns (rtl_dump_file); timevar_pop (TV_CSE); close_dump_file (DFI_cse, print_rtl_with_bb, insns); } /* Run second CSE pass after loop optimizations. */ static void rest_of_handle_cse2 (tree decl, rtx insns) { int tem; timevar_push (TV_CSE2); open_dump_file (DFI_cse2, decl); if (rtl_dump_file) dump_flow_info (rtl_dump_file); /* CFG is no longer maintained up-to-date. */ tem = cse_main (insns, max_reg_num (), 1, rtl_dump_file); /* Run a pass to eliminate duplicated assignments to condition code registers. We have to run this after bypass_jumps, because it makes it harder for that pass to determine whether a jump can be bypassed safely. */ cse_condition_code_reg (); purge_all_dead_edges (0); delete_trivially_dead_insns (insns, max_reg_num ()); if (tem) { timevar_push (TV_JUMP); rebuild_jump_labels (insns); cleanup_cfg (CLEANUP_EXPENSIVE); timevar_pop (TV_JUMP); } reg_scan (insns, max_reg_num (), 0); close_dump_file (DFI_cse2, print_rtl_with_bb, insns); ggc_collect (); timevar_pop (TV_CSE2); } /* Perform global cse. */ static void rest_of_handle_gcse (tree decl, rtx insns) { int save_csb, save_cfj; int tem2 = 0, tem; timevar_push (TV_GCSE); open_dump_file (DFI_gcse, decl); tem = gcse_main (insns, rtl_dump_file); rebuild_jump_labels (insns); delete_trivially_dead_insns (insns, max_reg_num ()); save_csb = flag_cse_skip_blocks; save_cfj = flag_cse_follow_jumps; flag_cse_skip_blocks = flag_cse_follow_jumps = 0; /* Instantiate any remaining CONSTANT_P_RTX nodes. */ if (current_function_calls_constant_p) purge_builtin_constant_p (); /* If -fexpensive-optimizations, re-run CSE to clean up things done by gcse. */ if (flag_expensive_optimizations) { timevar_push (TV_CSE); reg_scan (insns, max_reg_num (), 1); tem2 = cse_main (insns, max_reg_num (), 0, rtl_dump_file); purge_all_dead_edges (0); delete_trivially_dead_insns (insns, max_reg_num ()); timevar_pop (TV_CSE); cse_not_expected = !flag_rerun_cse_after_loop; } /* If gcse or cse altered any jumps, rerun jump optimizations to clean things up. Then possibly re-run CSE again. */ while (tem || tem2) { tem = tem2 = 0; timevar_push (TV_JUMP); rebuild_jump_labels (insns); cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP); timevar_pop (TV_JUMP); if (flag_expensive_optimizations) { timevar_push (TV_CSE); reg_scan (insns, max_reg_num (), 1); tem2 = cse_main (insns, max_reg_num (), 0, rtl_dump_file); purge_all_dead_edges (0); delete_trivially_dead_insns (insns, max_reg_num ()); timevar_pop (TV_CSE); } } close_dump_file (DFI_gcse, print_rtl_with_bb, insns); timevar_pop (TV_GCSE); ggc_collect (); flag_cse_skip_blocks = save_csb; flag_cse_follow_jumps = save_cfj; #ifdef ENABLE_CHECKING verify_flow_info (); #endif } /* Move constant computations out of loops. */ static void rest_of_handle_loop_optimize (tree decl, rtx insns) { int do_unroll, do_prefetch; timevar_push (TV_LOOP); delete_dead_jumptables (); cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP); open_dump_file (DFI_loop, decl); /* CFG is no longer maintained up-to-date. */ free_bb_for_insn (); if (flag_unroll_loops) do_unroll = LOOP_AUTO_UNROLL; /* Having two unrollers is useless. */ else do_unroll = flag_old_unroll_loops ? LOOP_UNROLL : LOOP_AUTO_UNROLL; do_prefetch = flag_prefetch_loop_arrays ? LOOP_PREFETCH : 0; if (flag_rerun_loop_opt) { cleanup_barriers (); /* We only want to perform unrolling once. */ loop_optimize (insns, rtl_dump_file, do_unroll); do_unroll = 0; /* The first call to loop_optimize makes some instructions trivially dead. We delete those instructions now in the hope that doing so will make the heuristics in loop work better and possibly speed up compilation. */ delete_trivially_dead_insns (insns, max_reg_num ()); /* The regscan pass is currently necessary as the alias analysis code depends on this information. */ reg_scan (insns, max_reg_num (), 1); } cleanup_barriers (); loop_optimize (insns, rtl_dump_file, do_unroll | LOOP_BCT | do_prefetch); /* Loop can create trivially dead instructions. */ delete_trivially_dead_insns (insns, max_reg_num ()); close_dump_file (DFI_loop, print_rtl, insns); timevar_pop (TV_LOOP); find_basic_blocks (insns, max_reg_num (), rtl_dump_file); ggc_collect (); } /* Perform loop optimizations. It might be better to do them a bit sooner, but we want the profile feedback to work more efficiently. */ static void rest_of_handle_loop2 (tree decl, rtx insns) { struct loops *loops; timevar_push (TV_LOOP); open_dump_file (DFI_loop2, decl); if (rtl_dump_file) dump_flow_info (rtl_dump_file); loops = loop_optimizer_init (rtl_dump_file); if (loops) { /* The optimizations: */ if (flag_unswitch_loops) unswitch_loops (loops); if (flag_peel_loops || flag_unroll_loops) unroll_and_peel_loops (loops, (flag_peel_loops ? UAP_PEEL : 0) | (flag_unroll_loops ? UAP_UNROLL : 0) | (flag_unroll_all_loops ? UAP_UNROLL_ALL : 0)); loop_optimizer_finalize (loops, rtl_dump_file); } cleanup_cfg (CLEANUP_EXPENSIVE); delete_trivially_dead_insns (insns, max_reg_num ()); reg_scan (insns, max_reg_num (), 0); if (rtl_dump_file) dump_flow_info (rtl_dump_file); close_dump_file (DFI_loop2, print_rtl_with_bb, get_insns ()); timevar_pop (TV_LOOP); ggc_collect (); } /* This is called from finish_function (within langhooks.parse_file) after each top-level definition is parsed. It is supposed to compile that function or variable and output the assembler code for it. After we return, the tree storage is freed. */ void rest_of_compilation (tree decl) { rtx insns; timevar_push (TV_REST_OF_COMPILATION); /* Register rtl specific functions for cfg. */ rtl_register_cfg_hooks (); /* Now that we're out of the frontend, we shouldn't have any more CONCATs anywhere. */ generating_concat_p = 0; /* When processing delayed functions, prepare_function_start() won't have been run to re-initialize it. */ cse_not_expected = ! optimize; /* First, make sure that NOTE_BLOCK is set correctly for each NOTE_INSN_BLOCK_BEG/NOTE_INSN_BLOCK_END note. */ if (!cfun->x_whole_function_mode_p) identify_blocks (); /* In function-at-a-time mode, we do not attempt to keep the BLOCK tree in sensible shape. So, we just recalculate it here. */ if (cfun->x_whole_function_mode_p) reorder_blocks (); init_flow (); if (rest_of_handle_inlining (decl)) goto exit_rest_of_compilation; /* If we're emitting a nested function, make sure its parent gets emitted as well. Doing otherwise confuses debug info. */ { tree parent; for (parent = DECL_CONTEXT (current_function_decl); parent != NULL_TREE; parent = get_containing_scope (parent)) if (TREE_CODE (parent) == FUNCTION_DECL) TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (parent)) = 1; } /* We are now committed to emitting code for this function. Do any preparation, such as emitting abstract debug info for the inline before it gets mangled by optimization. */ if (cgraph_function_possibly_inlined_p (decl)) (*debug_hooks->outlining_inline_function) (decl); /* Remove any notes we don't need. That will make iterating over the instruction sequence faster, and allow the garbage collector to reclaim the memory used by the notes. */ remove_unnecessary_notes (); reorder_blocks (); ggc_collect (); /* Initialize some variables used by the optimizers. */ init_function_for_compilation (); if (! DECL_DEFER_OUTPUT (decl)) TREE_ASM_WRITTEN (decl) = 1; /* Now that integrate will no longer see our rtl, we need not distinguish between the return value of this function and the return value of called functions. Also, we can remove all SETs of subregs of hard registers; they are only here because of integrate. Also, we can now initialize pseudos intended to carry magic hard reg data throughout the function. */ rtx_equal_function_value_matters = 0; purge_hard_subreg_sets (get_insns ()); /* Early return if there were errors. We can run afoul of our consistency checks, and there's not really much point in fixing them. Don't return yet if -Wreturn-type; we need to do cleanup_cfg. */ if (((rtl_dump_and_exit || flag_syntax_only) && !warn_return_type) || errorcount || sorrycount) goto exit_rest_of_compilation; timevar_push (TV_JUMP); open_dump_file (DFI_sibling, decl); insns = get_insns (); rebuild_jump_labels (insns); find_exception_handler_labels (); find_basic_blocks (insns, max_reg_num (), rtl_dump_file); delete_unreachable_blocks (); /* Turn NOTE_INSN_PREDICTIONs into branch predictions. */ if (flag_guess_branch_prob) { timevar_push (TV_BRANCH_PROB); note_prediction_to_br_prob (); timevar_pop (TV_BRANCH_PROB); } if (flag_optimize_sibling_calls) rest_of_handle_sibling_calls (insns); /* We have to issue these warnings now already, because CFG cleanups further down may destroy the required information. However, this must be done after the sibcall optimization pass because the barrier emitted for noreturn calls that are candidate for the optimization is folded into the CALL_PLACEHOLDER until after this pass, so the CFG is inaccurate. */ check_function_return_warnings (); timevar_pop (TV_JUMP); insn_locators_initialize (); /* Complete generation of exception handling code. */ if (doing_eh (0)) { timevar_push (TV_JUMP); open_dump_file (DFI_eh, decl); finish_eh_generation (); close_dump_file (DFI_eh, print_rtl, get_insns ()); timevar_pop (TV_JUMP); } /* Delay emitting hard_reg_initial_value sets until after EH landing pad generation, which might create new sets. */ emit_initial_value_sets (); #ifdef FINALIZE_PIC /* If we are doing position-independent code generation, now is the time to output special prologues and epilogues. We do not want to do this earlier, because it just clutters up inline functions with meaningless insns. */ if (flag_pic) FINALIZE_PIC; #endif insns = get_insns (); /* Copy any shared structure that should not be shared. */ unshare_all_rtl (current_function_decl, insns); #ifdef SETJMP_VIA_SAVE_AREA /* This must be performed before virtual register instantiation. Please be aware the everything in the compiler that can look at the RTL up to this point must understand that REG_SAVE_AREA is just like a use of the REG contained inside. */ if (current_function_calls_alloca) optimize_save_area_alloca (insns); #endif /* Instantiate all virtual registers. */ instantiate_virtual_regs (current_function_decl, insns); open_dump_file (DFI_jump, decl); /* Always do one jump optimization pass to ensure that JUMP_LABEL fields are initialized and to compute whether control can drop off the end of the function. */ timevar_push (TV_JUMP); /* Turn NOTE_INSN_EXPECTED_VALUE into REG_BR_PROB. Do this before jump optimization switches branch directions. */ if (flag_guess_branch_prob) expected_value_to_br_prob (); reg_scan (insns, max_reg_num (), 0); rebuild_jump_labels (insns); find_basic_blocks (insns, max_reg_num (), rtl_dump_file); delete_trivially_dead_insns (insns, max_reg_num ()); if (rtl_dump_file) dump_flow_info (rtl_dump_file); cleanup_cfg ((optimize ? CLEANUP_EXPENSIVE : 0) | CLEANUP_PRE_LOOP | (flag_thread_jumps ? CLEANUP_THREADING : 0)); if (optimize) { free_bb_for_insn (); copy_loop_headers (insns); find_basic_blocks (insns, max_reg_num (), rtl_dump_file); } purge_line_number_notes (insns); timevar_pop (TV_JUMP); close_dump_file (DFI_jump, print_rtl, insns); /* Now is when we stop if -fsyntax-only and -Wreturn-type. */ if (rtl_dump_and_exit || flag_syntax_only || DECL_DEFER_OUTPUT (decl)) goto exit_rest_of_compilation; timevar_push (TV_JUMP); if (optimize) cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP); if (flag_delete_null_pointer_checks) rest_of_handle_null_pointer (decl, insns); /* Jump optimization, and the removal of NULL pointer checks, may have reduced the number of instructions substantially. CSE, and future passes, allocate arrays whose dimensions involve the maximum instruction UID, so if we can reduce the maximum UID we'll save big on memory. */ renumber_insns (rtl_dump_file); timevar_pop (TV_JUMP); close_dump_file (DFI_jump, print_rtl_with_bb, insns); ggc_collect (); if (optimize > 0) rest_of_handle_cse (decl, insns); rest_of_handle_addressof (decl, insns); ggc_collect (); if (optimize > 0) { if (flag_gcse) rest_of_handle_gcse (decl, insns); if (flag_loop_optimize) rest_of_handle_loop_optimize (decl, insns); if (flag_gcse) rest_of_handle_jump_bypass (decl, insns); } timevar_push (TV_FLOW); rest_of_handle_cfg (decl, insns); if (optimize > 0 || profile_arc_flag || flag_test_coverage || flag_branch_probabilities) { rest_of_handle_branch_prob (decl, insns); if (flag_branch_probabilities && flag_profile_values && flag_value_profile_transformations) rest_of_handle_value_profile_transformations (decl, insns); /* Remove the death notes created for vpt. */ if (flag_profile_values) count_or_remove_death_notes (NULL, 1); } if (optimize > 0) rest_of_handle_if_conversion (decl, insns); if (flag_tracer) rest_of_handle_tracer (decl, insns); if (optimize > 0 && (flag_unswitch_loops || flag_peel_loops || flag_unroll_loops)) rest_of_handle_loop2 (decl, insns); if (flag_web) rest_of_handle_web (decl, insns); if (flag_rerun_cse_after_loop) rest_of_handle_cse2 (decl, insns); cse_not_expected = 1; rest_of_handle_life (decl, insns); if (optimize > 0) rest_of_handle_combine (decl, insns); if (flag_if_conversion) rest_of_handle_if_after_combine (decl, insns); if (optimize > 0 && (flag_regmove || flag_expensive_optimizations)) rest_of_handle_regmove (decl, insns); /* Do unconditional splitting before register allocation to allow machine description to add extra information not needed previously. */ split_all_insns (1); #ifdef OPTIMIZE_MODE_SWITCHING timevar_push (TV_MODE_SWITCH); no_new_pseudos = 0; optimize_mode_switching (NULL); no_new_pseudos = 1; timevar_pop (TV_MODE_SWITCH); #endif /* Any of the several passes since flow1 will have munged register lifetime data a bit. We need it to be up to date for scheduling (see handling of reg_known_equiv in init_alias_analysis). */ recompute_reg_usage (insns, !optimize_size); #ifdef INSN_SCHEDULING rest_of_handle_sched (decl, insns); #endif /* Determine if the current function is a leaf before running reload since this can impact optimizations done by the prologue and epilogue thus changing register elimination offsets. */ current_function_is_leaf = leaf_function_p (); timevar_push (TV_LOCAL_ALLOC); open_dump_file (DFI_lreg, decl); if (flag_new_regalloc) { if (rest_of_handle_new_regalloc (decl, insns)) goto exit_rest_of_compilation; } else { if (rest_of_handle_old_regalloc (decl, insns)) goto exit_rest_of_compilation; } ggc_collect (); open_dump_file (DFI_postreload, decl); /* Do a very simple CSE pass over just the hard registers. */ if (optimize > 0) { timevar_push (TV_RELOAD_CSE_REGS); reload_cse_regs (insns); /* reload_cse_regs can eliminate potentially-trapping MEMs. Remove any EH edges associated with them. */ if (flag_non_call_exceptions) purge_all_dead_edges (0); timevar_pop (TV_RELOAD_CSE_REGS); } close_dump_file (DFI_postreload, print_rtl_with_bb, insns); /* Re-create the death notes which were deleted during reload. */ timevar_push (TV_FLOW2); open_dump_file (DFI_flow2, decl); #ifdef ENABLE_CHECKING verify_flow_info (); #endif /* If optimizing, then go ahead and split insns now. */ #ifndef STACK_REGS if (optimize > 0) #endif split_all_insns (0); if (flag_branch_target_load_optimize) { open_dump_file (DFI_branch_target_load, decl); branch_target_load_optimize (insns, false); close_dump_file (DFI_branch_target_load, print_rtl_with_bb, insns); ggc_collect (); } if (optimize) cleanup_cfg (CLEANUP_EXPENSIVE); /* On some machines, the prologue and epilogue code, or parts thereof, can be represented as RTL. Doing so lets us schedule insns between it and the rest of the code and also allows delayed branch scheduling to operate in the epilogue. */ thread_prologue_and_epilogue_insns (insns); epilogue_completed = 1; if (optimize) { life_analysis (insns, rtl_dump_file, PROP_POSTRELOAD); cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_UPDATE_LIFE | (flag_crossjumping ? CLEANUP_CROSSJUMP : 0)); /* This is kind of a heuristic. We need to run combine_stack_adjustments even for machines with possibly nonzero RETURN_POPS_ARGS and ACCUMULATE_OUTGOING_ARGS. We expect that only ports having push instructions will have popping returns. */ #ifndef PUSH_ROUNDING if (!ACCUMULATE_OUTGOING_ARGS) #endif combine_stack_adjustments (); ggc_collect (); } flow2_completed = 1; close_dump_file (DFI_flow2, print_rtl_with_bb, insns); timevar_pop (TV_FLOW2); #ifdef HAVE_peephole2 if (optimize > 0 && flag_peephole2) { timevar_push (TV_PEEPHOLE2); open_dump_file (DFI_peephole2, decl); peephole2_optimize (rtl_dump_file); close_dump_file (DFI_peephole2, print_rtl_with_bb, insns); timevar_pop (TV_PEEPHOLE2); } #endif open_dump_file (DFI_ce3, decl); if (optimize) /* Last attempt to optimize CFG, as scheduling, peepholing and insn splitting possibly introduced more crossjumping opportunities. */ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_UPDATE_LIFE | (flag_crossjumping ? CLEANUP_CROSSJUMP : 0)); if (flag_if_conversion2) { timevar_push (TV_IFCVT2); if_convert (1); timevar_pop (TV_IFCVT2); } close_dump_file (DFI_ce3, print_rtl_with_bb, insns); if (optimize > 0) { if (flag_rename_registers || flag_cprop_registers) rest_of_handle_regrename (decl, insns); rest_of_handle_reorder_blocks (decl, insns); } if (flag_branch_target_load_optimize2) { /* Leave this a warning for now so that it is possible to experiment with running this pass twice. In 3.6, we should either make this an error, or use separate dump files. */ if (flag_branch_target_load_optimize) warning ("branch target register load optimization is not intended " "to be run twice"); open_dump_file (DFI_branch_target_load, decl); branch_target_load_optimize (insns, true); close_dump_file (DFI_branch_target_load, print_rtl_with_bb, insns); ggc_collect (); } #ifdef INSN_SCHEDULING if (optimize > 0 && flag_schedule_insns_after_reload) rest_of_handle_sched2 (decl, insns); #endif #ifdef LEAF_REGISTERS current_function_uses_only_leaf_regs = optimize > 0 && only_leaf_regs_used () && leaf_function_p (); #endif #ifdef STACK_REGS rest_of_handle_stack_regs (decl, insns); #endif compute_alignments (); /* CFG is no longer maintained up-to-date. */ free_bb_for_insn (); if (targetm.machine_dependent_reorg != 0) rest_of_handle_machine_reorg (decl, insns); purge_line_number_notes (insns); cleanup_barriers (); #ifdef DELAY_SLOTS if (optimize > 0 && flag_delayed_branch) rest_of_handle_delay_slots (decl, insns); #endif #if defined (HAVE_ATTR_length) && !defined (STACK_REGS) timevar_push (TV_SHORTEN_BRANCH); split_all_insns_noflow (); timevar_pop (TV_SHORTEN_BRANCH); #endif convert_to_eh_region_ranges (); /* Shorten branches. */ timevar_push (TV_SHORTEN_BRANCH); shorten_branches (get_insns ()); timevar_pop (TV_SHORTEN_BRANCH); set_nothrow_function_flags (); if (current_function_nothrow) /* Now we know that this can't throw; set the flag for the benefit of other functions later in this translation unit. */ TREE_NOTHROW (current_function_decl) = 1; rest_of_handle_final (decl, insns); /* Write DBX symbols if requested. */ /* Note that for those inline functions where we don't initially know for certain that we will be generating an out-of-line copy, the first invocation of this routine (rest_of_compilation) will skip over this code by doing a `goto exit_rest_of_compilation;'. Later on, wrapup_global_declarations will (indirectly) call rest_of_compilation again for those inline functions that need to have out-of-line copies generated. During that call, we *will* be routed past here. */ timevar_push (TV_SYMOUT); (*debug_hooks->function_decl) (decl); timevar_pop (TV_SYMOUT); exit_rest_of_compilation: coverage_end_function (); /* In case the function was not output, don't leave any temporary anonymous types queued up for sdb output. */ #ifdef SDB_DEBUGGING_INFO if (write_symbols == SDB_DEBUG) sdbout_types (NULL_TREE); #endif reload_completed = 0; epilogue_completed = 0; flow2_completed = 0; no_new_pseudos = 0; timevar_push (TV_FINAL); /* Clear out the insn_length contents now that they are no longer valid. */ init_insn_lengths (); /* Show no temporary slots allocated. */ init_temp_slots (); free_basic_block_vars (0); free_bb_for_insn (); timevar_pop (TV_FINAL); if ((*targetm.binds_local_p) (current_function_decl)) { int pref = cfun->preferred_stack_boundary; if (cfun->stack_alignment_needed > cfun->preferred_stack_boundary) pref = cfun->stack_alignment_needed; cgraph_rtl_info (current_function_decl)->preferred_incoming_stack_boundary = pref; } /* Make sure volatile mem refs aren't considered valid operands for arithmetic insns. We must call this here if this is a nested inline function, since the above code leaves us in the init_recog state (from final.c), and the function context push/pop code does not save/restore volatile_ok. ??? Maybe it isn't necessary for expand_start_function to call this anymore if we do it here? */ init_recog_no_volatile (); /* We're done with this function. Free up memory if we can. */ free_after_parsing (cfun); if (! DECL_DEFER_OUTPUT (decl)) { free_after_compilation (cfun); DECL_SAVED_INSNS (decl) = 0; } cfun = 0; ggc_collect (); timevar_pop (TV_REST_OF_COMPILATION); } /* Display help for target options. */ void display_target_options (void) { int undoc, i; static bool displayed = false; /* Avoid double printing for --help --target-help. */ if (displayed) return; displayed = true; if (ARRAY_SIZE (target_switches) > 1 #ifdef TARGET_OPTIONS || ARRAY_SIZE (target_options) > 1 #endif ) { int doc = 0; undoc = 0; printf (_("\nTarget specific options:\n")); for (i = ARRAY_SIZE (target_switches); i--;) { const char *option = target_switches[i].name; const char *description = target_switches[i].description; if (option == NULL || *option == 0) continue; else if (description == NULL) { undoc = 1; if (extra_warnings) printf (_(" -m%-23s [undocumented]\n"), option); } else if (*description != 0) doc += printf (" -m%-23s %s\n", option, _(description)); } #ifdef TARGET_OPTIONS for (i = ARRAY_SIZE (target_options); i--;) { const char *option = target_options[i].prefix; const char *description = target_options[i].description; if (option == NULL || *option == 0) continue; else if (description == NULL) { undoc = 1; if (extra_warnings) printf (_(" -m%-23s [undocumented]\n"), option); } else if (*description != 0) doc += printf (" -m%-23s %s\n", option, _(description)); } #endif if (undoc) { if (doc) printf (_("\nThere are undocumented target specific options as well.\n")); else printf (_(" They exist, but they are not documented.\n")); } } } /* Parse a -d... command line switch. */ void decode_d_option (const char *arg) { int i, c, matched; while (*arg) switch (c = *arg++) { case 'a': for (i = 0; i < (int) DFI_MAX; ++i) dump_file[i].enabled = 1; break; case 'A': flag_debug_asm = 1; break; case 'p': flag_print_asm_name = 1; break; case 'P': flag_dump_rtl_in_asm = 1; flag_print_asm_name = 1; break; case 'v': graph_dump_format = vcg; break; case 'x': rtl_dump_and_exit = 1; break; case 'y': set_yydebug = 1; break; case 'D': /* These are handled by the preprocessor. */ case 'I': break; case 'H': setup_core_dumping(); break; default: matched = 0; for (i = 0; i < (int) DFI_MAX; ++i) if (c == dump_file[i].debug_switch) { dump_file[i].enabled = 1; matched = 1; } if (! matched) warning ("unrecognized gcc debugging option: %c", c); break; } } /* Indexed by enum debug_info_type. */ const char *const debug_type_names[] = { "none", "stabs", "coff", "dwarf-1", "dwarf-2", "xcoff", "vms" }; /* Decode -m switches. */ /* Decode the switch -mNAME. */ void set_target_switch (const char *name) { size_t j; int valid_target_option = 0; for (j = 0; j < ARRAY_SIZE (target_switches); j++) if (!strcmp (target_switches[j].name, name)) { if (target_switches[j].value < 0) target_flags &= ~-target_switches[j].value; else target_flags |= target_switches[j].value; if (name[0] != 0) { if (target_switches[j].value < 0) target_flags_explicit |= -target_switches[j].value; else target_flags_explicit |= target_switches[j].value; } valid_target_option = 1; } #ifdef TARGET_OPTIONS if (!valid_target_option) for (j = 0; j < ARRAY_SIZE (target_options); j++) { int len = strlen (target_options[j].prefix); if (target_options[j].value) { if (!strcmp (target_options[j].prefix, name)) { *target_options[j].variable = target_options[j].value; valid_target_option = 1; } } else { if (!strncmp (target_options[j].prefix, name, len)) { *target_options[j].variable = name + len; valid_target_option = 1; } } } #endif if (!valid_target_option) error ("invalid option `%s'", name); } /* Print version information to FILE. Each line begins with INDENT (for the case where FILE is the assembler output file). */ void print_version (FILE *file, const char *indent) { #ifndef __VERSION__ #define __VERSION__ "[?]" #endif fnotice (file, #ifdef __GNUC__ "%s%s%s version %s (%s)\n%s\tcompiled by GNU C version %s.\n" #else "%s%s%s version %s (%s) compiled by CC.\n" #endif , indent, *indent != 0 ? " " : "", lang_hooks.name, version_string, TARGET_NAME, indent, __VERSION__); fnotice (file, "%s%sGGC heuristics: --param ggc-min-expand=%d --param ggc-min-heapsize=%d\n", indent, *indent != 0 ? " " : "", PARAM_VALUE (GGC_MIN_EXPAND), PARAM_VALUE (GGC_MIN_HEAPSIZE)); } /* Print an option value and return the adjusted position in the line. ??? We don't handle error returns from fprintf (disk full); presumably other code will catch a disk full though. */ static int print_single_switch (FILE *file, int pos, int max, const char *indent, const char *sep, const char *term, const char *type, const char *name) { /* The ultrix fprintf returns 0 on success, so compute the result we want here since we need it for the following test. */ int len = strlen (sep) + strlen (type) + strlen (name); if (pos != 0 && pos + len > max) { fprintf (file, "%s", term); pos = 0; } if (pos == 0) { fprintf (file, "%s", indent); pos = strlen (indent); } fprintf (file, "%s%s%s", sep, type, name); pos += len; return pos; } /* Print active target switches to FILE. POS is the current cursor position and MAX is the size of a "line". Each line begins with INDENT and ends with TERM. Each switch is separated from the next by SEP. */ static void print_switch_values (FILE *file, int pos, int max, const char *indent, const char *sep, const char *term) { size_t j; const char **p; /* Fill in the -frandom-seed option, if the user didn't pass it, so that it can be printed below. This helps reproducibility. */ randomize (); /* Print the options as passed. */ pos = print_single_switch (file, pos, max, indent, *indent ? " " : "", term, _("options passed: "), ""); for (p = &save_argv[1]; *p != NULL; p++) if (**p == '-') { /* Ignore these. */ if (strcmp (*p, "-o") == 0) { if (p[1] != NULL) p++; continue; } if (strcmp (*p, "-quiet") == 0) continue; if (strcmp (*p, "-version") == 0) continue; if ((*p)[1] == 'd') continue; pos = print_single_switch (file, pos, max, indent, sep, term, *p, ""); } if (pos > 0) fprintf (file, "%s", term); /* Print the -f and -m options that have been enabled. We don't handle language specific options but printing argv should suffice. */ pos = print_single_switch (file, 0, max, indent, *indent ? " " : "", term, _("options enabled: "), ""); for (j = 0; j < ARRAY_SIZE (f_options); j++) if (*f_options[j].variable == f_options[j].on_value) pos = print_single_switch (file, pos, max, indent, sep, term, "-f", f_options[j].string); /* Print target specific options. */ for (j = 0; j < ARRAY_SIZE (target_switches); j++) if (target_switches[j].name[0] != '\0' && target_switches[j].value > 0 && ((target_switches[j].value & target_flags) == target_switches[j].value)) { pos = print_single_switch (file, pos, max, indent, sep, term, "-m", target_switches[j].name); } #ifdef TARGET_OPTIONS for (j = 0; j < ARRAY_SIZE (target_options); j++) if (*target_options[j].variable != NULL) { char prefix[256]; sprintf (prefix, "-m%s", target_options[j].prefix); pos = print_single_switch (file, pos, max, indent, sep, term, prefix, *target_options[j].variable); } #endif fprintf (file, "%s", term); } /* Open assembly code output file. Do this even if -fsyntax-only is on, because then the driver will have provided the name of a temporary file or bit bucket for us. NAME is the file specified on the command line, possibly NULL. */ static void init_asm_output (const char *name) { if (name == NULL && asm_file_name == 0) asm_out_file = stdout; else { if (asm_file_name == 0) { int len = strlen (dump_base_name); char *dumpname = xmalloc (len + 6); memcpy (dumpname, dump_base_name, len + 1); strip_off_ending (dumpname, len); strcat (dumpname, ".s"); asm_file_name = dumpname; } if (!strcmp (asm_file_name, "-")) asm_out_file = stdout; else asm_out_file = fopen (asm_file_name, "w+"); if (asm_out_file == 0) fatal_error ("can't open %s for writing: %m", asm_file_name); } #ifdef IO_BUFFER_SIZE setvbuf (asm_out_file, xmalloc (IO_BUFFER_SIZE), _IOFBF, IO_BUFFER_SIZE); #endif if (!flag_syntax_only) { targetm.asm_out.file_start (); #ifdef ASM_COMMENT_START if (flag_verbose_asm) { /* Print the list of options in effect. */ print_version (asm_out_file, ASM_COMMENT_START); print_switch_values (asm_out_file, 0, MAX_LINE, ASM_COMMENT_START, " ", "\n"); /* Add a blank line here so it appears in assembler output but not screen output. */ fprintf (asm_out_file, "\n"); } #endif } } /* Default version of get_pch_validity. By default, every flag difference is fatal; that will be mostly right for most targets, but completely right for very few. */ void * default_get_pch_validity (size_t *len) { #ifdef TARGET_OPTIONS size_t i; #endif char *result, *r; *len = sizeof (target_flags) + 2; #ifdef TARGET_OPTIONS for (i = 0; i < ARRAY_SIZE (target_options); i++) { *len += 1; if (*target_options[i].variable) *len += strlen (*target_options[i].variable); } #endif result = r = xmalloc (*len); r[0] = flag_pic; r[1] = flag_pie; r += 2; memcpy (r, &target_flags, sizeof (target_flags)); r += sizeof (target_flags); #ifdef TARGET_OPTIONS for (i = 0; i < ARRAY_SIZE (target_options); i++) { const char *str = *target_options[i].variable; size_t l; if (! str) str = ""; l = strlen (str) + 1; memcpy (r, str, l); r += l; } #endif return result; } /* Default version of pch_valid_p. */ const char * default_pch_valid_p (const void *data_p, size_t len) { const char *data = (const char *)data_p; const char *flag_that_differs = NULL; size_t i; /* -fpic and -fpie also usually make a PCH invalid. */ if (data[0] != flag_pic) return _("created and used with different settings of -fpic"); if (data[1] != flag_pie) return _("created and used with different settings of -fpie"); data += 2; /* Check target_flags. */ if (memcmp (data, &target_flags, sizeof (target_flags)) != 0) { for (i = 0; i < ARRAY_SIZE (target_switches); i++) { int bits; int tf; memcpy (&tf, data, sizeof (target_flags)); bits = target_switches[i].value; if (bits < 0) bits = -bits; if ((target_flags & bits) != (tf & bits)) { flag_that_differs = target_switches[i].name; goto make_message; } } abort (); } data += sizeof (target_flags); len -= sizeof (target_flags); /* Check string options. */ #ifdef TARGET_OPTIONS for (i = 0; i < ARRAY_SIZE (target_options); i++) { const char *str = *target_options[i].variable; size_t l; if (! str) str = ""; l = strlen (str) + 1; if (len < l || memcmp (data, str, l) != 0) { flag_that_differs = target_options[i].prefix; goto make_message; } data += l; len -= l; } #endif return NULL; make_message: { char *r; asprintf (&r, _("created and used with differing settings of `-m%s'"), flag_that_differs); if (r == NULL) return _("out of memory"); return r; } } /* Default tree printer. Handles declarations only. */ static bool default_tree_printer (pretty_printer * pp, text_info *text) { switch (*text->format_spec) { case 'D': case 'F': case 'T': { tree t = va_arg (*text->args_ptr, tree); const char *n = DECL_NAME (t) ? (*lang_hooks.decl_printable_name) (t, 2) : ""; pp_string (pp, n); } return true; default: return false; } } /* Initialization of the front end environment, before command line options are parsed. Signal handlers, internationalization etc. ARGV0 is main's argv[0]. */ static void general_init (const char *argv0) { const char *p; p = argv0 + strlen (argv0); while (p != argv0 && !IS_DIR_SEPARATOR (p[-1])) --p; progname = p; xmalloc_set_program_name (progname); hex_init (); gcc_init_libintl (); /* Initialize the diagnostics reporting machinery, so option parsing can give warnings and errors. */ diagnostic_initialize (global_dc); /* Set a default printer. Language specific initializations will override it later. */ pp_format_decoder (global_dc->printer) = &default_tree_printer; /* Trap fatal signals, e.g. SIGSEGV, and convert them to ICE messages. */ #ifdef SIGSEGV signal (SIGSEGV, crash_signal); #endif #ifdef SIGILL signal (SIGILL, crash_signal); #endif #ifdef SIGBUS signal (SIGBUS, crash_signal); #endif #ifdef SIGABRT signal (SIGABRT, crash_signal); #endif #if defined SIGIOT && (!defined SIGABRT || SIGABRT != SIGIOT) signal (SIGIOT, crash_signal); #endif #ifdef SIGFPE signal (SIGFPE, crash_signal); #endif /* Other host-specific signal setup. */ (*host_hooks.extra_signals)(); /* Initialize the garbage-collector, string pools and tree type hash table. */ init_ggc (); init_stringpool (); init_ttree (); /* Initialize register usage now so switches may override. */ init_reg_sets (); /* Register the language-independent parameters. */ add_params (lang_independent_params, LAST_PARAM); /* This must be done after add_params but before argument processing. */ init_ggc_heuristics(); } /* Process the options that have been parsed. */ static void process_options (void) { + /* Just in case lang_hooks.post_options ends up calling a debug_hook. + This can happen with incorrect pre-processed input. */ + debug_hooks = &do_nothing_debug_hooks; + /* Allow the front end to perform consistency checks and do further initialization based on the command line options. This hook also sets the original filename if appropriate (e.g. foo.i -> foo.c) so we can correctly initialize debug output. */ no_backend = (*lang_hooks.post_options) (&main_input_filename); input_filename = main_input_filename; #ifdef OVERRIDE_OPTIONS /* Some machines may reject certain combinations of options. */ OVERRIDE_OPTIONS; #endif /* Set aux_base_name if not already set. */ if (aux_base_name) ; else if (main_input_filename) { char *name = xstrdup (lbasename (main_input_filename)); strip_off_ending (name, strlen (name)); aux_base_name = name; } else aux_base_name = "gccaux"; /* Set up the align_*_log variables, defaulting them to 1 if they were still unset. */ if (align_loops <= 0) align_loops = 1; if (align_loops_max_skip > align_loops || !align_loops) align_loops_max_skip = align_loops - 1; align_loops_log = floor_log2 (align_loops * 2 - 1); if (align_jumps <= 0) align_jumps = 1; if (align_jumps_max_skip > align_jumps || !align_jumps) align_jumps_max_skip = align_jumps - 1; align_jumps_log = floor_log2 (align_jumps * 2 - 1); if (align_labels <= 0) align_labels = 1; align_labels_log = floor_log2 (align_labels * 2 - 1); if (align_labels_max_skip > align_labels || !align_labels) align_labels_max_skip = align_labels - 1; if (align_functions <= 0) align_functions = 1; align_functions_log = floor_log2 (align_functions * 2 - 1); /* Unrolling all loops implies that standard loop unrolling must also be done. */ if (flag_unroll_all_loops) flag_unroll_loops = 1; if (flag_unroll_loops) { flag_old_unroll_loops = 0; flag_old_unroll_all_loops = 0; } if (flag_old_unroll_all_loops) flag_old_unroll_loops = 1; /* Old loop unrolling requires that strength_reduction be on also. Silently turn on strength reduction here if it isn't already on. Also, the loop unrolling code assumes that cse will be run after loop, so that must be turned on also. */ if (flag_old_unroll_loops) { flag_strength_reduce = 1; flag_rerun_cse_after_loop = 1; } if (flag_unroll_loops || flag_peel_loops) flag_rerun_cse_after_loop = 1; if (flag_non_call_exceptions) flag_asynchronous_unwind_tables = 1; if (flag_asynchronous_unwind_tables) flag_unwind_tables = 1; /* Disable unit-at-a-time mode for frontends not supporting callgraph interface. */ if (flag_unit_at_a_time && ! lang_hooks.callgraph.expand_function) flag_unit_at_a_time = 0; if (flag_value_profile_transformations) flag_profile_values = 1; /* Warn about options that are not supported on this machine. */ #ifndef INSN_SCHEDULING if (flag_schedule_insns || flag_schedule_insns_after_reload) warning ("instruction scheduling not supported on this target machine"); #endif #ifndef DELAY_SLOTS if (flag_delayed_branch) warning ("this target machine does not have delayed branches"); #endif user_label_prefix = USER_LABEL_PREFIX; if (flag_leading_underscore != -1) { /* If the default prefix is more complicated than "" or "_", issue a warning and ignore this option. */ if (user_label_prefix[0] == 0 || (user_label_prefix[0] == '_' && user_label_prefix[1] == 0)) { user_label_prefix = flag_leading_underscore ? "_" : ""; } else warning ("-f%sleading-underscore not supported on this target machine", flag_leading_underscore ? "" : "no-"); } /* If we are in verbose mode, write out the version and maybe all the option flags in use. */ if (version_flag) { print_version (stderr, ""); if (! quiet_flag) print_switch_values (stderr, 0, MAX_LINE, "", " ", "\n"); } if (flag_syntax_only) { write_symbols = NO_DEBUG; profile_flag = 0; } /* A lot of code assumes write_symbols == NO_DEBUG if the debugging level is 0. */ if (debug_info_level == DINFO_LEVEL_NONE) write_symbols = NO_DEBUG; /* Now we know write_symbols, set up the debug hooks based on it. By default we do nothing for debug output. */ if (write_symbols == NO_DEBUG) - debug_hooks = &do_nothing_debug_hooks; + ; #if defined(DBX_DEBUGGING_INFO) else if (write_symbols == DBX_DEBUG) debug_hooks = &dbx_debug_hooks; #endif #if defined(XCOFF_DEBUGGING_INFO) else if (write_symbols == XCOFF_DEBUG) debug_hooks = &xcoff_debug_hooks; #endif #ifdef SDB_DEBUGGING_INFO else if (write_symbols == SDB_DEBUG) debug_hooks = &sdb_debug_hooks; #endif #ifdef DWARF2_DEBUGGING_INFO else if (write_symbols == DWARF2_DEBUG) debug_hooks = &dwarf2_debug_hooks; #endif #ifdef VMS_DEBUGGING_INFO else if (write_symbols == VMS_DEBUG || write_symbols == VMS_AND_DWARF2_DEBUG) debug_hooks = &vmsdbg_debug_hooks; #endif else error ("target system does not support the \"%s\" debug format", debug_type_names[write_symbols]); /* If auxiliary info generation is desired, open the output file. This goes in the same directory as the source file--unlike all the other output files. */ if (flag_gen_aux_info) { aux_info_file = fopen (aux_info_file_name, "w"); if (aux_info_file == 0) fatal_error ("can't open %s: %m", aux_info_file_name); } if (! targetm.have_named_sections) { if (flag_function_sections) { warning ("-ffunction-sections not supported for this target"); flag_function_sections = 0; } if (flag_data_sections) { warning ("-fdata-sections not supported for this target"); flag_data_sections = 0; } } if (flag_function_sections && profile_flag) { warning ("-ffunction-sections disabled; it makes profiling impossible"); flag_function_sections = 0; } #ifndef HAVE_prefetch if (flag_prefetch_loop_arrays) { warning ("-fprefetch-loop-arrays not supported for this target"); flag_prefetch_loop_arrays = 0; } #else if (flag_prefetch_loop_arrays && !HAVE_prefetch) { warning ("-fprefetch-loop-arrays not supported for this target (try -march switches)"); flag_prefetch_loop_arrays = 0; } #endif /* This combination of options isn't handled for i386 targets and doesn't make much sense anyway, so don't allow it. */ if (flag_prefetch_loop_arrays && optimize_size) { warning ("-fprefetch-loop-arrays is not supported with -Os"); flag_prefetch_loop_arrays = 0; } #ifndef OBJECT_FORMAT_ELF if (flag_function_sections && write_symbols != NO_DEBUG) warning ("-ffunction-sections may affect debugging on some targets"); #endif /* The presence of IEEE signaling NaNs, implies all math can trap. */ if (flag_signaling_nans) flag_trapping_math = 1; } /* Initialize the compiler back end. */ static void backend_init (void) { init_emit_once (debug_info_level == DINFO_LEVEL_NORMAL || debug_info_level == DINFO_LEVEL_VERBOSE #ifdef VMS_DEBUGGING_INFO /* Enable line number info for traceback. */ || debug_info_level > DINFO_LEVEL_NONE #endif || flag_test_coverage || warn_notreached); init_regs (); init_fake_stack_mems (); init_alias_once (); init_loop (); init_reload (); init_function_once (); init_varasm_once (); /* The following initialization functions need to generate rtl, so provide a dummy function context for them. */ init_dummy_function_start (); init_expmed (); if (flag_caller_saves) init_caller_save (); expand_dummy_function_end (); } /* Language-dependent initialization. Returns nonzero on success. */ static int lang_dependent_init (const char *name) { if (dump_base_name == 0) dump_base_name = name ? name : "gccdump"; /* Other front-end initialization. */ if ((*lang_hooks.init) () == 0) return 0; init_asm_output (name); /* These create various _DECL nodes, so need to be called after the front end is initialized. */ init_eh (); init_optabs (); /* The following initialization functions need to generate rtl, so provide a dummy function context for them. */ init_dummy_function_start (); init_expr_once (); expand_dummy_function_end (); /* If dbx symbol table desired, initialize writing it and output the predefined types. */ timevar_push (TV_SYMOUT); #ifdef DWARF2_UNWIND_INFO if (dwarf2out_do_frame ()) dwarf2out_frame_init (); #endif /* Now we have the correct original filename, we can initialize debug output. */ (*debug_hooks->init) (name); timevar_pop (TV_SYMOUT); return 1; } /* Clean up: close opened files, etc. */ static void finalize (void) { /* Close the dump files. */ if (flag_gen_aux_info) { fclose (aux_info_file); if (errorcount) unlink (aux_info_file_name); } /* Close non-debugging input and output files. Take special care to note whether fclose returns an error, since the pages might still be on the buffer chain while the file is open. */ if (asm_out_file) { if (ferror (asm_out_file) != 0) fatal_error ("error writing to %s: %m", asm_file_name); if (fclose (asm_out_file) != 0) fatal_error ("error closing %s: %m", asm_file_name); } /* Do whatever is necessary to finish printing the graphs. */ if (graph_dump_format != no_graph) { int i; for (i = 0; i < (int) DFI_MAX; ++i) if (dump_file[i].initialized && dump_file[i].graph_dump_p) { char seq[16]; char *suffix; sprintf (seq, DUMPFILE_FORMAT, i); suffix = concat (seq, dump_file[i].extension, NULL); finish_graph_dump_file (dump_base_name, suffix); free (suffix); } } if (mem_report) { ggc_print_statistics (); stringpool_statistics (); dump_tree_statistics (); dump_rtx_statistics (); dump_varray_statistics (); dump_alloc_pool_statistics (); } /* Free up memory for the benefit of leak detectors. */ free_reg_info (); /* Language-specific end of compilation actions. */ (*lang_hooks.finish) (); } /* Initialize the compiler, and compile the input file. */ static void do_compile (void) { /* Initialize timing first. The C front ends read the main file in the post_options hook, and C++ does file timings. */ if (time_report || !quiet_flag || flag_detailed_statistics) timevar_init (); timevar_start (TV_TOTAL); process_options (); /* Don't do any more if an error has already occurred. */ if (!errorcount) { /* This must be run always, because it is needed to compute the FP predefined macros, such as __LDBL_MAX__, for targets using non default FP formats. */ init_adjust_machine_modes (); /* Set up the back-end if requested. */ if (!no_backend) backend_init (); /* Language-dependent initialization. Returns true on success. */ if (lang_dependent_init (main_input_filename)) { if (flag_unit_at_a_time) { open_dump_file (DFI_cgraph, NULL); cgraph_dump_file = rtl_dump_file; rtl_dump_file = NULL; } compile_file (); if (flag_unit_at_a_time) { rtl_dump_file = cgraph_dump_file; cgraph_dump_file = NULL; close_dump_file (DFI_cgraph, NULL, NULL_RTX); } } finalize (); } /* Stop timing and print the times. */ timevar_stop (TV_TOTAL); timevar_print (stderr); } /* Entry point of cc1, cc1plus, jc1, f771, etc. Exit code is FATAL_EXIT_CODE if can't open files or if there were any errors, or SUCCESS_EXIT_CODE if compilation succeeded. It is not safe to call this function more than once. */ int toplev_main (unsigned int argc, const char **argv) { save_argv = argv; /* Initialization of GCC's environment, and diagnostics. */ general_init (argv[0]); /* Parse the options and do minimal processing; basically just enough to default flags appropriately. */ decode_options (argc, argv); randomize (); /* Exit early if we can (e.g. -help). */ if (!exit_after_options) do_compile (); if (errorcount || sorrycount) return (FATAL_EXIT_CODE); return (SUCCESS_EXIT_CODE); } diff --git a/contrib/gcc/version.c b/contrib/gcc/version.c index 00f84e6429b8..229cf6270e20 100644 --- a/contrib/gcc/version.c +++ b/contrib/gcc/version.c @@ -1,18 +1,18 @@ /* $FreeBSD$ */ #include "version.h" /* This is the string reported as the version number by all components of the compiler. If you distribute a modified version of GCC, please modify this string to indicate that, e.g. by putting your organization's name in parentheses at the end of the string. */ -const char version_string[] = "3.4.4 [FreeBSD] 20050518"; +const char version_string[] = "3.4.6 [FreeBSD] 20060825"; /* This is the location of the online document giving instructions for reporting bugs. If you distribute a modified version of GCC, please change this to refer to a document giving instructions for reporting bugs to you, not us. (You are of course welcome to forward us bugs reported to you, if you determine that they are not bugs in your modifications.) */ const char bug_report_url[] = "";