aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog15
-rw-r--r--gcc/Makefile.in8
-rw-r--r--gcc/builtins.c2198
-rw-r--r--gcc/expr.c1949
-rw-r--r--gcc/expr.h21
5 files changed, 2239 insertions, 1952 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 2da77c5..81580cb 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,18 @@
+Thu Jul 22 11:25:20 1999 Bernd Schmidt <bernds@cygnus.co.uk>
+
+ * builtins.c: New file.
+ * expr.c (saveregs_value, apply_args_value): Delete definition,
+ moved into builtins.c.
+ (string_constant): No longer static.
+ (get_pointer_alignment, c_strlen, get_memory_rtx, expand_builtin,
+ apply_args_size, apply_result_size, result_vector,
+ expand_builtin_apply_args, expand_builtin_apply,
+ expand_builtin_return): Delete functions, moved into builtins.c.
+ (INCOMING_REGNO, OUTGOING_REGNO): Delete unused macros.
+ * expr.h (saveregs_value, apply_args_value): Declare variables.
+ (expand_builtin, string_constant): Declare functions.
+ * Makefile.in: Update to build builtin.o.
+
Wed Jul 21 12:37:34 IDT 1999 Vladik Goytin (goytin@yahoo.com)
* config/arm/aout.h (ASM_OUTPUT_SECTION_NAME): New macro: Support
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 521e694..141e472 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -673,8 +673,8 @@ SCHED_CFLAGS = @sched_cflags@
# Language-independent object files.
OBJS = toplev.o version.o tree.o print-tree.o stor-layout.o fold-const.o \
- function.o stmt.o except.o expr.o calls.o expmed.o explow.o optabs.o \
- intl.o varasm.o rtl.o print-rtl.o rtlanal.o emit-rtl.o genrtl.o real.o \
+ function.o stmt.o except.o expr.o calls.o expmed.o explow.o optabs.o real.o \
+ builtins.o intl.o varasm.o rtl.o print-rtl.o rtlanal.o emit-rtl.o genrtl.o \
dbxout.o sdbout.o dwarfout.o dwarf2out.o xcoffout.o bitmap.o alias.o gcse.o \
integrate.o jump.o cse.o loop.o unroll.o flow.o stupid.o combine.o varray.o \
regclass.o regmove.o local-alloc.o global.o reload.o reload1.o caller-save.o \
@@ -1484,6 +1484,10 @@ except.o : except.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h \
expr.o : expr.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h function.h \
$(REGS_H) insn-flags.h insn-codes.h $(EXPR_H) insn-config.h $(RECOG_H) \
output.h typeclass.h hard-reg-set.h toplev.h hard-reg-set.h except.h
+builtins.o : builtins.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h \
+ function.h $(REGS_H) insn-flags.h insn-codes.h $(EXPR_H) insn-config.h \
+ $(RECOG_H) output.h typeclass.h hard-reg-set.h toplev.h hard-reg-set.h \
+ except.h
calls.o : calls.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h $(EXPR_H) \
insn-flags.h $(REGS_H) toplev.h output.h
expmed.o : expmed.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h \
diff --git a/gcc/builtins.c b/gcc/builtins.c
new file mode 100644
index 0000000..0a833fc
--- /dev/null
+++ b/gcc/builtins.c
@@ -0,0 +1,2198 @@
+/* Expand builtin functions.
+ Copyright (C) 1988, 92-98, 1999 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC 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.
+
+GNU CC 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 GNU CC; 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 "machmode.h"
+#include "rtl.h"
+#include "tree.h"
+#include "obstack.h"
+#include "flags.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "except.h"
+#include "function.h"
+#include "insn-flags.h"
+#include "insn-codes.h"
+#include "insn-config.h"
+#include "expr.h"
+#include "recog.h"
+#include "output.h"
+#include "typeclass.h"
+#include "defaults.h"
+#include "toplev.h"
+
+#define CALLED_AS_BUILT_IN(NODE) \
+ (!strncmp (IDENTIFIER_POINTER (DECL_NAME (NODE)), "__builtin_", 10))
+
+#define CEIL(x,y) (((x) + (y) - 1) / (y))
+
+/* 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
+
+/* Nonzero means __builtin_saveregs has already been done in this function.
+ The value is the pseudoreg containing the value __builtin_saveregs
+ returned. */
+rtx saveregs_value;
+
+/* Similarly for __builtin_apply_args. */
+rtx apply_args_value;
+
+static int get_pointer_alignment PROTO((tree, unsigned));
+static tree c_strlen PROTO((tree));
+static rtx get_memory_rtx PROTO((tree));
+static int apply_args_size PROTO((void));
+static int apply_result_size PROTO((void));
+static rtx result_vector PROTO((int, rtx));
+static rtx expand_builtin_apply_args PROTO((void));
+static rtx expand_builtin_apply_args_1 PROTO((void));
+static rtx expand_builtin_apply PROTO((rtx, rtx, rtx));
+static void expand_builtin_return PROTO((rtx));
+static rtx expand_builtin_classify_type PROTO((tree));
+static rtx expand_builtin_mathfn PROTO((tree, rtx, rtx));
+static rtx expand_builtin_constant_p PROTO((tree));
+static rtx expand_builtin_saveregs PROTO((tree, rtx, int));
+static rtx expand_builtin_args_info PROTO((tree));
+static rtx expand_builtin_next_arg PROTO((tree));
+static rtx expand_builtin_memcmp PROTO((tree, tree, rtx));
+static rtx expand_builtin_strcmp PROTO((tree, rtx));
+static rtx expand_builtin_memcpy PROTO((tree));
+static rtx expand_builtin_strcpy PROTO((tree));
+static rtx expand_builtin_memset PROTO((tree));
+static rtx expand_builtin_strlen PROTO((tree, rtx, enum machine_mode));
+static rtx expand_builtin_alloca PROTO((tree, rtx));
+static rtx expand_builtin_ffs PROTO((tree, rtx, rtx));
+static rtx expand_builtin_frame_address PROTO((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 (exp, max_align)
+ tree exp;
+ unsigned max_align;
+{
+ unsigned 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 (TREE_CODE (TREE_OPERAND (exp, 1)) != INTEGER_CST)
+ return align;
+
+ while (((TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)) * BITS_PER_UNIT)
+ & (max_align - 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 (TREE_CODE_CLASS (TREE_CODE (exp)) == 'd')
+ 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.
+
+ 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 (src)
+ tree src;
+{
+ tree offset_node;
+ int offset, max;
+ char *ptr;
+
+ src = string_constant (src, &offset_node);
+ if (src == 0)
+ return 0;
+ max = TREE_STRING_LENGTH (src);
+ 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_binop (MINUS_EXPR, size_int (max), offset_node);
+ }
+
+ /* We have a known offset into the string. Start searching there for
+ a null character. */
+ if (offset_node == 0)
+ offset = 0;
+ else
+ {
+ /* Did we get a long long offset? If so, punt. */
+ if (TREE_INT_CST_HIGH (offset_node) != 0)
+ return 0;
+ offset = TREE_INT_CST_LOW (offset_node);
+ }
+ /* 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 size_int (strlen (ptr + offset));
+}
+
+/* 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 (fndecl_code, count, tem)
+ 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 = copy_to_reg (gen_rtx_MEM (Pmode, 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);
+#endif
+ return tem;
+}
+
+/* __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. */
+
+rtx
+expand_builtin_setjmp (buf_addr, target, first_label, next_label)
+ rtx buf_addr;
+ rtx target;
+ rtx first_label, next_label;
+{
+ rtx lab1 = gen_label_rtx ();
+ enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL);
+ enum machine_mode value_mode;
+ rtx stack_save;
+
+ value_mode = TYPE_MODE (integer_type_node);
+
+#ifdef POINTERS_EXTEND_UNSIGNED
+ buf_addr = convert_memory_address (Pmode, buf_addr);
+#endif
+
+ buf_addr = force_reg (Pmode, buf_addr);
+
+ if (target == 0 || GET_CODE (target) != REG
+ || REGNO (target) < FIRST_PSEUDO_REGISTER)
+ target = gen_reg_rtx (value_mode);
+
+ emit_queue ();
+
+ /* We store the frame pointer and the address of lab1 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
+
+ emit_move_insn (gen_rtx_MEM (Pmode, buf_addr),
+ BUILTIN_SETJMP_FRAME_VALUE);
+ emit_move_insn (validize_mem
+ (gen_rtx_MEM (Pmode,
+ plus_constant (buf_addr,
+ GET_MODE_SIZE (Pmode)))),
+ force_reg (Pmode, gen_rtx_LABEL_REF (Pmode, lab1)));
+
+ stack_save = gen_rtx_MEM (sa_mode,
+ plus_constant (buf_addr,
+ 2 * GET_MODE_SIZE (Pmode)));
+ 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
+
+ /* Set TARGET to zero and branch to the first-time-through label. */
+ emit_move_insn (target, const0_rtx);
+ emit_jump_insn (gen_jump (first_label));
+ emit_barrier ();
+ emit_label (lab1);
+
+ /* Tell flow about the strange goings on. Putting `lab1' 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, lab1, nonlocal_goto_handler_labels);
+
+ /* 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 struct elims {int from, to;} elim_regs[] = ELIMINABLE_REGS;
+
+ for (i = 0; i < sizeof elim_regs / sizeof elim_regs[0]; i++)
+ if (elim_regs[i].from == ARG_POINTER_REGNUM
+ && elim_regs[i].to == HARD_FRAME_POINTER_REGNUM)
+ break;
+
+ if (i == sizeof elim_regs / sizeof elim_regs [0])
+#endif
+ {
+ /* Now restore our arg pointer from the address at which it
+ was saved in our stack frame.
+ If there hasn't be space allocated for it yet, make
+ some now. */
+ if (arg_pointer_save_area == 0)
+ arg_pointer_save_area
+ = assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0);
+ emit_move_insn (virtual_incoming_args_rtx,
+ copy_to_reg (arg_pointer_save_area));
+ }
+ }
+#endif
+
+#ifdef HAVE_builtin_setjmp_receiver
+ if (HAVE_builtin_setjmp_receiver)
+ emit_insn (gen_builtin_setjmp_receiver (lab1));
+ else
+#endif
+#ifdef HAVE_nonlocal_goto_receiver
+ if (HAVE_nonlocal_goto_receiver)
+ emit_insn (gen_nonlocal_goto_receiver ());
+ else
+#endif
+ {
+ ; /* Nothing */
+ }
+
+ /* Set TARGET, and branch to the next-time-through label. */
+ emit_move_insn (target, const1_rtx);
+ emit_jump_insn (gen_jump (next_label));
+ emit_barrier ();
+
+ 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 (buf_addr, value)
+ rtx buf_addr, value;
+{
+ rtx fp, lab, stack;
+ enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL);
+
+#ifdef POINTERS_EXTEND_UNSIGNED
+ buf_addr = convert_memory_address (Pmode, buf_addr);
+#endif
+ 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 ();
+
+#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)));
+
+ /* 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, fp, stack, lab));
+ else
+#endif
+ {
+ lab = copy_to_reg (lab);
+
+ 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);
+ }
+ }
+}
+
+/* Get a MEM rtx for expression EXP which can be used in a string instruction
+ (cmpstrsi, movstrsi, ..). */
+static rtx
+get_memory_rtx (exp)
+ tree exp;
+{
+ rtx mem;
+ int is_aggregate;
+
+ mem = gen_rtx_MEM (BLKmode,
+ memory_address (BLKmode,
+ expand_expr (exp, NULL_RTX,
+ ptr_mode, EXPAND_SUM)));
+
+ RTX_UNCHANGING_P (mem) = TREE_READONLY (exp);
+
+ /* Figure out the type of the object pointed to. Set MEM_IN_STRUCT_P
+ if the value is the address of a structure or if the expression is
+ cast to a pointer to structure type. */
+ is_aggregate = 0;
+
+ while (TREE_CODE (exp) == NOP_EXPR)
+ {
+ tree cast_type = TREE_TYPE (exp);
+ if (TREE_CODE (cast_type) == POINTER_TYPE
+ && AGGREGATE_TYPE_P (TREE_TYPE (cast_type)))
+ {
+ is_aggregate = 1;
+ break;
+ }
+ exp = TREE_OPERAND (exp, 0);
+ }
+
+ if (is_aggregate == 0)
+ {
+ tree type;
+
+ if (TREE_CODE (exp) == ADDR_EXPR)
+ /* If this is the address of an object, check whether the
+ object is an array. */
+ type = TREE_TYPE (TREE_OPERAND (exp, 0));
+ else
+ type = TREE_TYPE (TREE_TYPE (exp));
+ is_aggregate = AGGREGATE_TYPE_P (type);
+ }
+
+ MEM_SET_IN_STRUCT_P (mem, is_aggregate);
+ 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 (regno)
+ 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 ()
+{
+ static int size = -1;
+ int align, 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 (struct_value_rtx)
+ 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)
+ && (mov_optab->handlers[(int) mode].insn_code
+ != CODE_FOR_nothing))
+ 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 ()
+{
+ 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)
+ && (mov_optab->handlers[(int) mode].insn_code
+ != CODE_FOR_nothing))
+ 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 (savep, result)
+ int savep;
+ rtx result;
+{
+ int regno, size, align, nelts;
+ enum machine_mode mode;
+ rtx reg, mem;
+ rtx *savevec = (rtx *) 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 = change_address (result, mode,
+ plus_constant (XEXP (result, 0), 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 ()
+{
+ rtx registers;
+ int size, align, regno;
+ enum machine_mode mode;
+
+ /* 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 (struct_value_rtx)
+ 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)
+ {
+ rtx tem;
+
+ 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));
+
+#ifdef STACK_REGS
+ /* For reg-stack.c's stack register household.
+ Compare with a similar piece of code in function.c. */
+
+ emit_insn (gen_rtx_USE (mode, tem));
+#endif
+
+ emit_move_insn (change_address (registers, mode,
+ plus_constant (XEXP (registers, 0),
+ size)),
+ tem);
+ size += GET_MODE_SIZE (mode);
+ }
+
+ /* Save the arg pointer to the block. */
+ emit_move_insn (change_address (registers, Pmode, XEXP (registers, 0)),
+ copy_to_reg (virtual_incoming_args_rtx));
+ size = GET_MODE_SIZE (Pmode);
+
+ /* Save the structure value address unless this is passed as an
+ "invisible" first argument. */
+ if (struct_value_incoming_rtx)
+ {
+ emit_move_insn (change_address (registers, Pmode,
+ plus_constant (XEXP (registers, 0),
+ size)),
+ copy_to_reg (struct_value_incoming_rtx));
+ 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 ()
+{
+ /* 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 sequence after the NOTE that starts the function.
+ If this is inside a SEQUENCE, make the outer-level insn
+ chain current, so the code is placed at the start of the
+ function. */
+ push_topmost_sequence ();
+ emit_insns_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 (function, arguments, argsize)
+ rtx function, arguments, argsize;
+{
+ int size, align, regno;
+ enum machine_mode mode;
+ rtx incoming_args, result, reg, dest, call_insn;
+ rtx old_stack_level = 0;
+ rtx call_fusage = 0;
+
+ /* Create a block where the return registers can be saved. */
+ result = assign_stack_local (BLKmode, apply_result_size (), -1);
+
+ /* ??? The argsize value should be adjusted here. */
+
+ /* 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_binop (Pmode, sub_optab, 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_pending_stack_adjust ();
+
+ /* 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);
+
+ /* Push a block of memory onto the stack to store the memory arguments.
+ Save the address in a register, and copy the memory arguments. ??? I
+ haven't figured out how the calling convention macros effect this,
+ but it's likely that the source and/or destination addresses in
+ the block copy will need updating in machine specific ways. */
+ dest = allocate_dynamic_stack_space (argsize, 0, 0);
+ emit_block_move (gen_rtx_MEM (BLKmode, dest),
+ gen_rtx_MEM (BLKmode, incoming_args),
+ argsize,
+ PARM_BOUNDARY / BITS_PER_UNIT);
+
+ /* Refer to the argument block. */
+ apply_args_size ();
+ arguments = gen_rtx_MEM (BLKmode, arguments);
+
+ /* Walk past the arg-pointer and structure value address. */
+ size = GET_MODE_SIZE (Pmode);
+ if (struct_value_rtx)
+ 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,
+ change_address (arguments, mode,
+ plus_constant (XEXP (arguments, 0),
+ 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)
+ {
+ rtx value = gen_reg_rtx (Pmode);
+ emit_move_insn (value,
+ change_address (arguments, Pmode,
+ plus_constant (XEXP (arguments, 0),
+ size)));
+ emit_move_insn (struct_value_rtx, value);
+ if (GET_CODE (struct_value_rtx) == REG)
+ use_reg (&call_fusage, struct_value_rtx);
+ 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);
+
+ /* 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 (change_address (result, GET_MODE (valreg),
+ XEXP (result, 0)),
+ valreg);
+ }
+ else
+#endif
+ abort ();
+
+ /* Find the CALL insn we just emitted. */
+ for (call_insn = get_last_insn ();
+ call_insn && GET_CODE (call_insn) != CALL_INSN;
+ call_insn = PREV_INSN (call_insn))
+ ;
+
+ if (! call_insn)
+ abort ();
+
+ /* Put the register usage information on the CALL. If there is already
+ some usage information, put ours at the end. */
+ if (CALL_INSN_FUNCTION_USAGE (call_insn))
+ {
+ rtx link;
+
+ for (link = CALL_INSN_FUNCTION_USAGE (call_insn); XEXP (link, 1) != 0;
+ link = XEXP (link, 1))
+ ;
+
+ XEXP (link, 1) = call_fusage;
+ }
+ else
+ CALL_INSN_FUNCTION_USAGE (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);
+
+ /* Return the address of the result block. */
+ return copy_addr_to_reg (XEXP (result, 0));
+}
+
+/* Perform an untyped return. */
+
+static void
+expand_builtin_return (result)
+ rtx result;
+{
+ int size, align, regno;
+ enum machine_mode mode;
+ rtx reg;
+ rtx call_fusage = 0;
+
+ 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,
+ change_address (result, mode,
+ plus_constant (XEXP (result, 0),
+ 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_insns (call_fusage);
+
+ /* Return whatever values was restored by jumping directly to the end
+ of the function. */
+ expand_null_return ();
+}
+
+/* Expand a call to __builtin_classify_type with arguments found in
+ ARGLIST. */
+static rtx
+expand_builtin_classify_type (arglist)
+ tree arglist;
+{
+ if (arglist != 0)
+ {
+ tree type = TREE_TYPE (TREE_VALUE (arglist));
+ enum tree_code code = TREE_CODE (type);
+ if (code == VOID_TYPE)
+ return GEN_INT (void_type_class);
+ if (code == INTEGER_TYPE)
+ return GEN_INT (integer_type_class);
+ if (code == CHAR_TYPE)
+ return GEN_INT (char_type_class);
+ if (code == ENUMERAL_TYPE)
+ return GEN_INT (enumeral_type_class);
+ if (code == BOOLEAN_TYPE)
+ return GEN_INT (boolean_type_class);
+ if (code == POINTER_TYPE)
+ return GEN_INT (pointer_type_class);
+ if (code == REFERENCE_TYPE)
+ return GEN_INT (reference_type_class);
+ if (code == OFFSET_TYPE)
+ return GEN_INT (offset_type_class);
+ if (code == REAL_TYPE)
+ return GEN_INT (real_type_class);
+ if (code == COMPLEX_TYPE)
+ return GEN_INT (complex_type_class);
+ if (code == FUNCTION_TYPE)
+ return GEN_INT (function_type_class);
+ if (code == METHOD_TYPE)
+ return GEN_INT (method_type_class);
+ if (code == RECORD_TYPE)
+ return GEN_INT (record_type_class);
+ if (code == UNION_TYPE || code == QUAL_UNION_TYPE)
+ return GEN_INT (union_type_class);
+ if (code == ARRAY_TYPE)
+ {
+ if (TYPE_STRING_FLAG (type))
+ return GEN_INT (string_type_class);
+ else
+ return GEN_INT (array_type_class);
+ }
+ if (code == SET_TYPE)
+ return GEN_INT (set_type_class);
+ if (code == FILE_TYPE)
+ return GEN_INT (file_type_class);
+ if (code == LANG_TYPE)
+ return GEN_INT (lang_type_class);
+ }
+ return GEN_INT (no_type_class);
+}
+
+/* Expand expression EXP, which is a call to __builtin_constant_p. */
+static rtx
+expand_builtin_constant_p (exp)
+ tree exp;
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+ enum machine_mode value_mode = TYPE_MODE (TREE_TYPE (exp));
+
+ if (arglist == 0)
+ return const0_rtx;
+ else
+ {
+ tree arg = TREE_VALUE (arglist);
+ rtx tmp;
+
+ /* 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 (arg);
+
+ /* If we know this is a constant, emit the constant of one. */
+ if (TREE_CODE_CLASS (TREE_CODE (arg)) == 'c'
+ || (TREE_CODE (arg) == CONSTRUCTOR
+ && TREE_CONSTANT (arg))
+ || (TREE_CODE (arg) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (arg, 0)) == STRING_CST))
+ return const1_rtx;
+
+ /* If we aren't going to be running CSE or 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. */
+ if (TREE_SIDE_EFFECTS (arg) || cse_not_expected
+ || AGGREGATE_TYPE_P (TREE_TYPE (arg))
+ || POINTER_TYPE_P (TREE_TYPE (arg)))
+ return const0_rtx;
+
+ /* Otherwise, emit (constant_p_rtx (ARG)) and let CSE get a
+ chance to see if it can deduce whether ARG is constant. */
+
+ tmp = expand_expr (arg, NULL_RTX, VOIDmode, 0);
+ tmp = gen_rtx_CONSTANT_P_RTX (value_mode, tmp);
+ return tmp;
+ }
+}
+
+/* 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 (exp, target, subtarget)
+ tree exp;
+ rtx target, subtarget;
+{
+ optab builtin_optab;
+ rtx op0, insns;
+ tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ tree arglist = TREE_OPERAND (exp, 1);
+
+ if (arglist == 0
+ /* Arg could be wrong type if user redeclared this fcn wrong. */
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != REAL_TYPE)
+ return 0;
+
+ /* Stabilize and compute the argument. */
+ if (TREE_CODE (TREE_VALUE (arglist)) != VAR_DECL
+ && TREE_CODE (TREE_VALUE (arglist)) != PARM_DECL)
+ {
+ exp = copy_node (exp);
+ arglist = copy_node (arglist);
+ TREE_OPERAND (exp, 1) = arglist;
+ TREE_VALUE (arglist) = save_expr (TREE_VALUE (arglist));
+ }
+ op0 = expand_expr (TREE_VALUE (arglist), subtarget, VOIDmode, 0);
+
+ /* Make a suitable register to place result in. */
+ target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
+
+ emit_queue ();
+ start_sequence ();
+
+ switch (DECL_FUNCTION_CODE (fndecl))
+ {
+ case BUILT_IN_SIN:
+ builtin_optab = sin_optab; break;
+ case BUILT_IN_COS:
+ builtin_optab = cos_optab; break;
+ case BUILT_IN_FSQRT:
+ builtin_optab = sqrt_optab; break;
+ default:
+ abort ();
+ }
+
+ /* Compute into TARGET.
+ Set TARGET to wherever the result comes back. */
+ target = expand_unop (TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist))),
+ builtin_optab, op0, target, 0);
+
+ /* If we were unable to expand via the builtin, stop the
+ sequence (without outputting the insns) and return 0, causing
+ a call to the library function. */
+ if (target == 0)
+ {
+ end_sequence ();
+ return 0;
+ }
+
+ /* Check the results by default. But if flag_fast_math is turned on,
+ then assume sqrt will always be called with valid arguments. */
+
+ if (flag_errno_math && ! flag_fast_math)
+ {
+ rtx lab1;
+
+ /* Don't define the builtin FP instructions
+ if your machine is not IEEE. */
+ if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT)
+ abort ();
+
+ lab1 = 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, 0, lab1);
+
+#ifdef TARGET_EDOM
+ {
+#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));
+ }
+#else
+ /* 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;
+#endif
+
+ emit_label (lab1);
+ }
+
+ /* Output the entire sequence. */
+ insns = get_insns ();
+ end_sequence ();
+ emit_insns (insns);
+
+ return target;
+}
+
+/* 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 (and in mode MODE if that's
+ convenient). */
+static rtx
+expand_builtin_strlen (exp, target, mode)
+ tree exp;
+ rtx target;
+ enum machine_mode mode;
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+ enum machine_mode value_mode = TYPE_MODE (TREE_TYPE (exp));
+
+ if (arglist == 0
+ /* Arg could be non-pointer if user redeclared this fcn wrong. */
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE)
+ return 0;
+ else
+ {
+ tree src = TREE_VALUE (arglist);
+ tree len = c_strlen (src);
+
+ int align
+ = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
+
+ rtx result, src_rtx, char_rtx;
+ enum machine_mode insn_mode = value_mode, char_mode;
+ enum insn_code icode;
+
+ /* If the length is known, just return it. */
+ if (len != 0)
+ return expand_expr (len, target, mode, EXPAND_MEMORY_USE_BAD);
+
+ /* If SRC is not a pointer type, don't do this operation inline. */
+ if (align == 0)
+ return 0;
+
+ /* Call a function 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)
+ return 0;
+
+ 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 sure the operands are acceptable to the predicates. */
+
+ if (! (*insn_operand_predicate[(int)icode][0]) (result, insn_mode))
+ result = gen_reg_rtx (insn_mode);
+ src_rtx = memory_address (BLKmode,
+ expand_expr (src, NULL_RTX, ptr_mode,
+ EXPAND_NORMAL));
+
+ if (! (*insn_operand_predicate[(int)icode][1]) (src_rtx, Pmode))
+ src_rtx = copy_to_mode_reg (Pmode, src_rtx);
+
+ /* Check the string is readable and has an end. */
+ if (current_function_check_memory_usage)
+ emit_library_call (chkr_check_str_libfunc, 1, VOIDmode, 2,
+ src_rtx, Pmode,
+ GEN_INT (MEMORY_USE_RO),
+ TYPE_MODE (integer_type_node));
+
+ char_rtx = const0_rtx;
+ char_mode = insn_operand_mode[(int)icode][2];
+ if (! (*insn_operand_predicate[(int)icode][2]) (char_rtx, char_mode))
+ char_rtx = copy_to_mode_reg (char_mode, char_rtx);
+
+ emit_insn (GEN_FCN (icode) (result,
+ gen_rtx_MEM (BLKmode, src_rtx),
+ char_rtx, GEN_INT (align)));
+
+ /* Return the value in the proper mode for this function. */
+ if (GET_MODE (result) == value_mode)
+ return result;
+ else if (target != 0)
+ {
+ convert_move (target, result, 0);
+ return target;
+ }
+ else
+ return convert_to_mode (value_mode, result, 0);
+ }
+}
+
+/* Expand a call to the memcpy builtin, with arguments in ARGLIST. */
+static rtx
+expand_builtin_memcpy (arglist)
+ tree arglist;
+{
+ if (arglist == 0
+ /* Arg could be non-pointer if user redeclared this fcn wrong. */
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+ || TREE_CHAIN (arglist) == 0
+ || (TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist))))
+ != POINTER_TYPE)
+ || TREE_CHAIN (TREE_CHAIN (arglist)) == 0
+ || (TREE_CODE (TREE_TYPE (TREE_VALUE
+ (TREE_CHAIN (TREE_CHAIN (arglist)))))
+ != INTEGER_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)));
+
+ int src_align
+ = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
+ int dest_align
+ = get_pointer_alignment (dest, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
+ rtx dest_mem, src_mem, dest_addr, len_rtx;
+
+ /* If either SRC or DEST is not a pointer type, don't do
+ this operation in-line. */
+ if (src_align == 0 || dest_align == 0)
+ return 0;
+
+ dest_mem = get_memory_rtx (dest);
+ src_mem = get_memory_rtx (src);
+ len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
+
+ /* Just copy the rights of SRC to the rights of DEST. */
+ if (current_function_check_memory_usage)
+ emit_library_call (chkr_copy_bitmap_libfunc, 1, VOIDmode, 3,
+ XEXP (dest_mem, 0), Pmode,
+ XEXP (src_mem, 0), Pmode,
+ len_rtx, TYPE_MODE (sizetype));
+
+ /* Copy word part most expediently. */
+ dest_addr
+ = emit_block_move (dest_mem, src_mem, len_rtx,
+ MIN (src_align, dest_align));
+
+ if (dest_addr == 0)
+ dest_addr = force_operand (XEXP (dest_mem, 0), NULL_RTX);
+
+ return dest_addr;
+ }
+}
+
+/* Expand expression EXP, which is a call to the strcpy builtin. Return 0
+ if we failed the caller should emit a normal call. */
+static rtx
+expand_builtin_strcpy (exp)
+ tree exp;
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+ rtx result;
+
+ if (arglist == 0
+ /* Arg could be non-pointer if user redeclared this fcn wrong. */
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+ || TREE_CHAIN (arglist) == 0
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE)
+ return 0;
+ else
+ {
+ tree len = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)));
+
+ if (len == 0)
+ return 0;
+
+ len = size_binop (PLUS_EXPR, len, integer_one_node);
+
+ chainon (arglist, build_tree_list (NULL_TREE, len));
+ }
+ result = expand_builtin_memcpy (arglist);
+ if (! result)
+ TREE_CHAIN (TREE_CHAIN (arglist)) = 0;
+ return result;
+}
+
+/* Expand expression EXP, which is a call to the memset builtin. Return 0
+ if we failed the caller should emit a normal call. */
+static rtx
+expand_builtin_memset (exp)
+ tree exp;
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+
+ if (arglist == 0
+ /* Arg could be non-pointer if user redeclared this fcn wrong. */
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+ || TREE_CHAIN (arglist) == 0
+ || (TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist))))
+ != INTEGER_TYPE)
+ || TREE_CHAIN (TREE_CHAIN (arglist)) == 0
+ || (INTEGER_TYPE
+ != (TREE_CODE (TREE_TYPE
+ (TREE_VALUE
+ (TREE_CHAIN (TREE_CHAIN (arglist))))))))
+ return 0;
+ else
+ {
+ tree dest = TREE_VALUE (arglist);
+ tree val = TREE_VALUE (TREE_CHAIN (arglist));
+ tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+ int dest_align
+ = get_pointer_alignment (dest, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
+ 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 arguments have side-effects, then we can only evaluate
+ them at most once. The following code evaluates them twice if
+ they are not constants because we break out to expand_call
+ in that case. They can't be constants if they have side-effects
+ so we can check for that first. Alternatively, we could call
+ save_expr to make multiple evaluation safe. */
+ if (TREE_SIDE_EFFECTS (val) || TREE_SIDE_EFFECTS (len))
+ return 0;
+
+ /* If VAL is not 0, don't do this operation in-line. */
+ if (expand_expr (val, NULL_RTX, VOIDmode, 0) != const0_rtx)
+ return 0;
+
+ /* If LEN does not expand to a constant, don't do this
+ operation in-line. */
+ len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
+ if (GET_CODE (len_rtx) != CONST_INT)
+ return 0;
+
+ dest_mem = get_memory_rtx (dest);
+
+ /* Just check DST is writable and mark it as readable. */
+ if (current_function_check_memory_usage)
+ emit_library_call (chkr_check_addr_libfunc, 1, VOIDmode, 3,
+ XEXP (dest_mem, 0), Pmode,
+ len_rtx, TYPE_MODE (sizetype),
+ GEN_INT (MEMORY_USE_WO),
+ TYPE_MODE (integer_type_node));
+
+
+ dest_addr = clear_storage (dest_mem, len_rtx, dest_align);
+
+ if (dest_addr == 0)
+ dest_addr = force_operand (XEXP (dest_mem, 0), NULL_RTX);
+
+ return dest_addr;
+ }
+}
+
+#ifdef HAVE_cmpstrsi
+/* Expand expression EXP, which is a call to the memcmp or the strcmp builtin.
+ 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. */
+static rtx
+expand_builtin_memcmp (exp, arglist, target)
+ tree exp;
+ tree arglist;
+ rtx target;
+{
+ /* If we need to check memory accesses, call the library function. */
+ if (current_function_check_memory_usage)
+ return 0;
+
+ if (arglist == 0
+ /* Arg could be non-pointer if user redeclared this fcn wrong. */
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+ || TREE_CHAIN (arglist) == 0
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE
+ || TREE_CHAIN (TREE_CHAIN (arglist)) == 0
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))))) != INTEGER_TYPE)
+ return 0;
+ else if (!HAVE_cmpstrsi)
+ return 0;
+
+ {
+ enum machine_mode mode;
+ tree arg1 = TREE_VALUE (arglist);
+ tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+ tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+ rtx result;
+
+ 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_operand_mode[(int) CODE_FOR_cmpstrsi][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);
+
+ emit_insn (gen_cmpstrsi (result, get_memory_rtx (arg1),
+ get_memory_rtx (arg2),
+ expand_expr (len, NULL_RTX, VOIDmode, 0),
+ GEN_INT (MIN (arg1_align, arg2_align))));
+
+ /* 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);
+ }
+}
+
+/* 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 (exp, target)
+ tree exp;
+ rtx target;
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+
+ /* If we need to check memory accesses, call the library function. */
+ if (current_function_check_memory_usage)
+ return 0;
+
+ if (arglist == 0
+ /* Arg could be non-pointer if user redeclared this fcn wrong. */
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+ || TREE_CHAIN (arglist) == 0
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE)
+ return 0;
+ else if (!HAVE_cmpstrsi)
+ return 0;
+ {
+ tree arg1 = TREE_VALUE (arglist);
+ tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+ tree len, len2;
+ rtx result;
+ len = c_strlen (arg1);
+ if (len)
+ len = size_binop (PLUS_EXPR, integer_one_node, len);
+ len2 = c_strlen (arg2);
+ if (len2)
+ len2 = size_binop (PLUS_EXPR, integer_one_node, 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.
+
+ If both strings have constant lengths, use the smaller. This
+ could arise if optimization results in strcpy being called with
+ two fixed strings, or if the code was machine-generated. We should
+ add some code to the `memcmp' handler below to deal with such
+ situations, someday. */
+ if (!len || TREE_CODE (len) != INTEGER_CST)
+ {
+ if (len2)
+ len = len2;
+ else if (len == 0)
+ return 0;
+ }
+ else if (len2 && TREE_CODE (len2) == INTEGER_CST)
+ {
+ if (tree_int_cst_lt (len2, len))
+ len = len2;
+ }
+
+ chainon (arglist, build_tree_list (NULL_TREE, len));
+ result = expand_builtin_memcmp (exp, arglist, target);
+ if (! result)
+ TREE_CHAIN (TREE_CHAIN (arglist)) = 0;
+ return result;
+ }
+}
+#endif
+
+/* Expand expression EXP, which is a call to __builtin_saveregs,
+ generating the result in TARGET, if that's convenient.
+ IGNORE is nonzero if the value is to be ignored. */
+static rtx
+expand_builtin_saveregs (exp, target, ignore)
+ tree exp;
+ rtx target;
+ int ignore;
+{
+ enum machine_mode value_mode = TYPE_MODE (TREE_TYPE (exp));
+
+ /* 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. */
+ rtx temp;
+ rtx seq;
+
+ /* Now really call the function. `expand_call' does not call
+ expand_builtin, so there is no danger of infinite recursion here. */
+ start_sequence ();
+
+#ifdef EXPAND_BUILTIN_SAVEREGS
+ /* Do whatever the machine needs done in this case. */
+ temp = EXPAND_BUILTIN_SAVEREGS (arglist);
+#else
+ /* The register where the function returns its value
+ is likely to have something else in it, such as an argument.
+ So preserve that register around the call. */
+
+ if (value_mode != VOIDmode)
+ {
+ rtx valreg = hard_libcall_value (value_mode);
+ rtx saved_valreg = gen_reg_rtx (value_mode);
+
+ emit_move_insn (saved_valreg, valreg);
+ temp = expand_call (exp, target, ignore);
+ emit_move_insn (valreg, saved_valreg);
+ }
+ else
+ /* Generate the call, putting the value in a pseudo. */
+ temp = expand_call (exp, target, ignore);
+#endif
+
+ seq = get_insns ();
+ end_sequence ();
+
+ saveregs_value = temp;
+
+ /* Put the sequence after the NOTE that starts the function.
+ If this is inside a SEQUENCE, make the outer-level insn
+ chain current, so the code is placed at the start of the
+ function. */
+ push_topmost_sequence ();
+ emit_insns_before (seq, NEXT_INSN (get_insns ()));
+ pop_topmost_sequence ();
+ return temp;
+ }
+}
+
+/* __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 (exp)
+ tree exp;
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+ int nwords = sizeof (CUMULATIVE_ARGS) / sizeof (int);
+ int *word_ptr = (int *) &current_function_args_info;
+#if 0
+ /* These are used by the code below that is if 0'ed away */
+ int i;
+ tree type, elts, result;
+#endif
+
+ if (sizeof (CUMULATIVE_ARGS) % sizeof (int) != 0)
+ fatal ("CUMULATIVE_ARGS type defined badly; see %s, line %d",
+ __FILE__, __LINE__);
+
+ if (arglist != 0)
+ {
+ tree arg = TREE_VALUE (arglist);
+ if (TREE_CODE (arg) != INTEGER_CST)
+ error ("argument of `__builtin_args_info' must be constant");
+ else
+ {
+ int wordnum = TREE_INT_CST_LOW (arg);
+
+ if (wordnum < 0 || wordnum >= nwords || TREE_INT_CST_HIGH (arg))
+ 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;
+
+#if 0
+ for (i = 0; i < nwords; i++)
+ elts = tree_cons (NULL_TREE, build_int_2 (word_ptr[i], 0));
+
+ type = build_array_type (integer_type_node,
+ build_index_type (build_int_2 (nwords, 0)));
+ result = build (CONSTRUCTOR, type, NULL_TREE, nreverse (elts));
+ TREE_CONSTANT (result) = 1;
+ TREE_STATIC (result) = 1;
+ result = build (INDIRECT_REF, build_pointer_type (type), result);
+ TREE_CONSTANT (result) = 1;
+ return expand_expr (result, NULL_RTX, VOIDmode, EXPAND_MEMORY_USE_BAD);
+#endif
+}
+
+/* Expand expression EXP, which is a call to __builtin_next_arg. */
+static rtx
+expand_builtin_next_arg (exp)
+ tree exp;
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree fntype = TREE_TYPE (current_function_decl);
+
+ if ((TYPE_ARG_TYPES (fntype) == 0
+ || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
+ == void_type_node))
+ && ! current_function_varargs)
+ {
+ 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 if (! current_function_varargs)
+ /* Evidently an out of date version of <stdarg.h>; 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);
+}
+
+/* Expand a call to one of the builtin functions __builtin_frame_address or
+ __builtin_return_address. */
+static rtx
+expand_builtin_frame_address (exp)
+ tree exp;
+{
+ tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ tree arglist = TREE_OPERAND (exp, 1);
+
+ /* 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 (TREE_CODE (TREE_VALUE (arglist)) != INTEGER_CST
+ || tree_int_cst_sgn (TREE_VALUE (arglist)) < 0)
+ {
+ 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_INT_CST_LOW (TREE_VALUE (arglist)),
+ 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 (arglist, target)
+ tree arglist;
+ rtx target;
+{
+ rtx op0;
+
+ if (arglist == 0
+ /* Arg could be non-integer if user redeclared this fcn wrong. */
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE)
+ return 0;
+
+ /* Compute the argument. */
+ op0 = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0);
+
+ /* Allocate the desired space. */
+ return allocate_dynamic_stack_space (op0, target, BITS_PER_UNIT);
+}
+
+/* Expand a call to the ffs 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_ffs (arglist, target, subtarget)
+ tree arglist;
+ rtx target, subtarget;
+{
+ rtx op0;
+ if (arglist == 0
+ /* Arg could be non-integer if user redeclared this fcn wrong. */
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE)
+ return 0;
+
+ /* Compute the argument. */
+ op0 = expand_expr (TREE_VALUE (arglist), subtarget, VOIDmode, 0);
+ /* Compute ffs, into TARGET if possible.
+ Set TARGET to wherever the result comes back. */
+ target = expand_unop (TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist))),
+ ffs_optab, op0, target, 1);
+ if (target == 0)
+ abort ();
+ return 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
+expand_builtin (exp, target, subtarget, mode, ignore)
+ tree exp;
+ rtx target;
+ rtx subtarget;
+ enum machine_mode mode;
+ int ignore;
+{
+ tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ tree arglist = TREE_OPERAND (exp, 1);
+ enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+
+ /* When not optimizing, generate calls to library functions for a certain
+ set of builtins. */
+ if (! optimize && ! CALLED_AS_BUILT_IN (fndecl)
+ && (fcode == BUILT_IN_SIN || fcode == BUILT_IN_COS
+ || fcode == BUILT_IN_FSQRT || fcode == BUILT_IN_MEMSET
+ || fcode == BUILT_IN_MEMCPY || fcode == BUILT_IN_MEMCMP
+ || fcode == BUILT_IN_STRLEN || fcode == BUILT_IN_STRCPY
+ || fcode == BUILT_IN_STRCMP || fcode == BUILT_IN_FFS))
+ return expand_call (exp, target, ignore);
+
+ switch (fcode)
+ {
+ case BUILT_IN_ABS:
+ case BUILT_IN_LABS:
+ case BUILT_IN_FABS:
+ /* build_function_call changes these into ABS_EXPR. */
+ abort ();
+
+ case BUILT_IN_SIN:
+ case BUILT_IN_COS:
+ /* Treat these like sqrt, but only if the user asks for them. */
+ if (! flag_fast_math)
+ break;
+ case BUILT_IN_FSQRT:
+ target = expand_builtin_mathfn (exp, target, subtarget);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_FMOD:
+ 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 (arglist == 0
+ /* Arg could be non-pointer if user redeclared this fcn wrong. */
+ || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))
+ || TREE_CHAIN (arglist) == 0
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE
+ || TREE_CHAIN (TREE_CHAIN (arglist)) == 0
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))))) != INTEGER_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 (arglist
+ /* Arg could be non-pointer if user redeclared this fcn wrong. */
+ && TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) == POINTER_TYPE)
+ expand_builtin_return (expand_expr (TREE_VALUE (arglist),
+ NULL_RTX, VOIDmode, 0));
+ return const0_rtx;
+
+ case BUILT_IN_SAVEREGS:
+ return expand_builtin_saveregs (exp, target, ignore);
+
+ case BUILT_IN_ARGS_INFO:
+ return expand_builtin_args_info (exp);
+
+ /* Return the address of the first anonymous stack arg. */
+ case BUILT_IN_NEXT_ARG:
+ return expand_builtin_next_arg (exp);
+
+ case BUILT_IN_CLASSIFY_TYPE:
+ return expand_builtin_classify_type (arglist);
+
+ case BUILT_IN_CONSTANT_P:
+ return expand_builtin_constant_p (exp);
+
+ case BUILT_IN_FRAME_ADDRESS:
+ case BUILT_IN_RETURN_ADDRESS:
+ return expand_builtin_frame_address (exp);
+
+ /* 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:
+ target = expand_builtin_ffs (exp, target, subtarget);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_STRLEN:
+ target = expand_builtin_strlen (exp, target, mode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_STRCPY:
+ target = expand_builtin_strcpy (exp);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_MEMCPY:
+ target = expand_builtin_memcpy (arglist);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_MEMSET:
+ target = expand_builtin_memset (exp);
+ if (target)
+ return target;
+ break;
+
+/* These comparison functions need an instruction that returns an actual
+ index. An ordinary compare that just sets the condition codes
+ is not enough. */
+#ifdef HAVE_cmpstrsi
+ case BUILT_IN_STRCMP:
+ target = expand_builtin_strcmp (exp, target);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_MEMCMP:
+ target = expand_builtin_memcmp (exp, arglist, target);
+ if (target)
+ return target;
+ break;
+#else
+ case BUILT_IN_STRCMP:
+ case BUILT_IN_MEMCMP:
+ break;
+#endif
+
+ case BUILT_IN_SETJMP:
+ if (arglist == 0
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE)
+ break;
+ else
+ {
+ rtx buf_addr = expand_expr (TREE_VALUE (arglist), subtarget,
+ VOIDmode, 0);
+ rtx lab = gen_label_rtx ();
+ rtx ret = expand_builtin_setjmp (buf_addr, target, lab, lab);
+ emit_label (lab);
+ return ret;
+ }
+
+ /* __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 (arglist == 0 || TREE_CHAIN (arglist) == 0
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_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:
+#ifdef HAVE_trap
+ if (HAVE_trap)
+ emit_insn (gen_trap ());
+ else
+#endif
+ error ("__builtin_trap not supported by this target");
+ emit_barrier ();
+ return const0_rtx;
+
+ /* 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_FP_REGNUM:
+ return expand_builtin_dwarf_fp_regnum ();
+ case BUILT_IN_DWARF_REG_SIZE:
+ return expand_builtin_dwarf_reg_size (TREE_VALUE (arglist), target);
+#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)),
+ TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))));
+ return const0_rtx;
+
+ default: /* just do library call, if unknown builtin */
+ 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);
+}
diff --git a/gcc/expr.c b/gcc/expr.c
index 7bbb499..8245cb8 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -104,14 +104,6 @@ int pending_stack_adjust;
NO_DEFER_POP and OK_DEFER_POP. */
int inhibit_defer_pop;
-/* Nonzero means __builtin_saveregs has already been done in this function.
- The value is the pseudoreg containing the value __builtin_saveregs
- returned. */
-static rtx saveregs_value;
-
-/* Similarly for __builtin_apply_args. */
-static rtx apply_args_value;
-
/* Don't check memory usage, since code is being emitted to check a memory
usage. Used when current_function_check_memory_usage is true, to avoid
infinite recursion. */
@@ -182,18 +174,6 @@ static tree init_noncopied_parts PROTO((tree, tree));
static int safe_from_p PROTO((rtx, tree, int));
static int fixed_type_p PROTO((tree));
static rtx var_rtx PROTO((tree));
-static int get_pointer_alignment PROTO((tree, unsigned));
-static tree string_constant PROTO((tree, tree *));
-static tree c_strlen PROTO((tree));
-static rtx get_memory_rtx PROTO((tree));
-static rtx expand_builtin PROTO((tree, rtx, rtx,
- enum machine_mode, int));
-static int apply_args_size PROTO((void));
-static int apply_result_size PROTO((void));
-static rtx result_vector PROTO((int, rtx));
-static rtx expand_builtin_apply_args PROTO((void));
-static rtx expand_builtin_apply PROTO((rtx, rtx, rtx));
-static void expand_builtin_return PROTO((rtx));
static rtx expand_increment PROTO((tree, int, int));
static void preexpand_calls PROTO((tree));
static void do_jump_by_parts_greater PROTO((tree, int, rtx, rtx));
@@ -239,14 +219,6 @@ enum insn_code clrstr_optab[NUM_MACHINE_MODES];
#ifndef SLOW_UNALIGNED_ACCESS
#define SLOW_UNALIGNED_ACCESS STRICT_ALIGNMENT
#endif
-
-/* 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
/* This is run once per compilation to set up which modes can be used
directly in memory and to initialize the block move optab. */
@@ -8287,82 +8259,11 @@ expand_expr (exp, target, tmode, modifier)
abort ();
return temp;
}
-
-
-
-/* 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 (exp, max_align)
- tree exp;
- unsigned max_align;
-{
- unsigned 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 (TREE_CODE (TREE_OPERAND (exp, 1)) != INTEGER_CST)
- return align;
-
- while (((TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)) * BITS_PER_UNIT)
- & (max_align - 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 (TREE_CODE_CLASS (TREE_CODE (exp)) == 'd')
- 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;
- }
- }
-}
/* Return the tree node and offset if a given argument corresponds to
a string constant. */
-static tree
+tree
string_constant (arg, ptr_offset)
tree arg;
tree *ptr_offset;
@@ -8399,1854 +8300,6 @@ string_constant (arg, ptr_offset)
return 0;
}
-
-/* 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.
-
- 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 (src)
- tree src;
-{
- tree offset_node;
- int offset, max;
- char *ptr;
-
- src = string_constant (src, &offset_node);
- if (src == 0)
- return 0;
- max = TREE_STRING_LENGTH (src);
- 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_binop (MINUS_EXPR, size_int (max), offset_node);
- }
-
- /* We have a known offset into the string. Start searching there for
- a null character. */
- if (offset_node == 0)
- offset = 0;
- else
- {
- /* Did we get a long long offset? If so, punt. */
- if (TREE_INT_CST_HIGH (offset_node) != 0)
- return 0;
- offset = TREE_INT_CST_LOW (offset_node);
- }
- /* 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 size_int (strlen (ptr + offset));
-}
-
-rtx
-expand_builtin_return_addr (fndecl_code, count, tem)
- 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 = copy_to_reg (gen_rtx_MEM (Pmode, 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);
-#endif
- return tem;
-}
-
-/* __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. */
-
-rtx
-expand_builtin_setjmp (buf_addr, target, first_label, next_label)
- rtx buf_addr;
- rtx target;
- rtx first_label, next_label;
-{
- rtx lab1 = gen_label_rtx ();
- enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL);
- enum machine_mode value_mode;
- rtx stack_save;
-
- value_mode = TYPE_MODE (integer_type_node);
-
-#ifdef POINTERS_EXTEND_UNSIGNED
- buf_addr = convert_memory_address (Pmode, buf_addr);
-#endif
-
- buf_addr = force_reg (Pmode, buf_addr);
-
- if (target == 0 || GET_CODE (target) != REG
- || REGNO (target) < FIRST_PSEUDO_REGISTER)
- target = gen_reg_rtx (value_mode);
-
- emit_queue ();
-
- /* We store the frame pointer and the address of lab1 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
-
- emit_move_insn (gen_rtx_MEM (Pmode, buf_addr),
- BUILTIN_SETJMP_FRAME_VALUE);
- emit_move_insn (validize_mem
- (gen_rtx_MEM (Pmode,
- plus_constant (buf_addr,
- GET_MODE_SIZE (Pmode)))),
- force_reg (Pmode, gen_rtx_LABEL_REF (Pmode, lab1)));
-
- stack_save = gen_rtx_MEM (sa_mode,
- plus_constant (buf_addr,
- 2 * GET_MODE_SIZE (Pmode)));
- 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
-
- /* Set TARGET to zero and branch to the first-time-through label. */
- emit_move_insn (target, const0_rtx);
- emit_jump_insn (gen_jump (first_label));
- emit_barrier ();
- emit_label (lab1);
-
- /* Tell flow about the strange goings on. Putting `lab1' 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, lab1, nonlocal_goto_handler_labels);
-
- /* 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 struct elims {int from, to;} elim_regs[] = ELIMINABLE_REGS;
-
- for (i = 0; i < sizeof elim_regs / sizeof elim_regs[0]; i++)
- if (elim_regs[i].from == ARG_POINTER_REGNUM
- && elim_regs[i].to == HARD_FRAME_POINTER_REGNUM)
- break;
-
- if (i == sizeof elim_regs / sizeof elim_regs [0])
-#endif
- {
- /* Now restore our arg pointer from the address at which it
- was saved in our stack frame.
- If there hasn't be space allocated for it yet, make
- some now. */
- if (arg_pointer_save_area == 0)
- arg_pointer_save_area
- = assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0);
- emit_move_insn (virtual_incoming_args_rtx,
- copy_to_reg (arg_pointer_save_area));
- }
- }
-#endif
-
-#ifdef HAVE_builtin_setjmp_receiver
- if (HAVE_builtin_setjmp_receiver)
- emit_insn (gen_builtin_setjmp_receiver (lab1));
- else
-#endif
-#ifdef HAVE_nonlocal_goto_receiver
- if (HAVE_nonlocal_goto_receiver)
- emit_insn (gen_nonlocal_goto_receiver ());
- else
-#endif
- {
- ; /* Nothing */
- }
-
- /* Set TARGET, and branch to the next-time-through label. */
- emit_move_insn (target, const1_rtx);
- emit_jump_insn (gen_jump (next_label));
- emit_barrier ();
-
- return target;
-}
-
-void
-expand_builtin_longjmp (buf_addr, value)
- rtx buf_addr, value;
-{
- rtx fp, lab, stack;
- enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL);
-
-#ifdef POINTERS_EXTEND_UNSIGNED
- buf_addr = convert_memory_address (Pmode, buf_addr);
-#endif
- 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 ();
-
-#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)));
-
- /* 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, fp, stack, lab));
- else
-#endif
- {
- lab = copy_to_reg (lab);
-
- 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);
- }
- }
-}
-
-static rtx
-get_memory_rtx (exp)
- tree exp;
-{
- rtx mem;
- int is_aggregate;
-
- mem = gen_rtx_MEM (BLKmode,
- memory_address (BLKmode,
- expand_expr (exp, NULL_RTX,
- ptr_mode, EXPAND_SUM)));
-
- RTX_UNCHANGING_P (mem) = TREE_READONLY (exp);
-
- /* Figure out the type of the object pointed to. Set MEM_IN_STRUCT_P
- if the value is the address of a structure or if the expression is
- cast to a pointer to structure type. */
- is_aggregate = 0;
-
- while (TREE_CODE (exp) == NOP_EXPR)
- {
- tree cast_type = TREE_TYPE (exp);
- if (TREE_CODE (cast_type) == POINTER_TYPE
- && AGGREGATE_TYPE_P (TREE_TYPE (cast_type)))
- {
- is_aggregate = 1;
- break;
- }
- exp = TREE_OPERAND (exp, 0);
- }
-
- if (is_aggregate == 0)
- {
- tree type;
-
- if (TREE_CODE (exp) == ADDR_EXPR)
- /* If this is the address of an object, check whether the
- object is an array. */
- type = TREE_TYPE (TREE_OPERAND (exp, 0));
- else
- type = TREE_TYPE (TREE_TYPE (exp));
- is_aggregate = AGGREGATE_TYPE_P (type);
- }
-
- MEM_SET_IN_STRUCT_P (mem, is_aggregate);
- return mem;
-}
-
-
-/* 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. */
-
-#define CALLED_AS_BUILT_IN(NODE) \
- (!strncmp (IDENTIFIER_POINTER (DECL_NAME (NODE)), "__builtin_", 10))
-
-static rtx
-expand_builtin (exp, target, subtarget, mode, ignore)
- tree exp;
- rtx target;
- rtx subtarget;
- enum machine_mode mode;
- int ignore;
-{
- tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
- tree arglist = TREE_OPERAND (exp, 1);
- rtx op0;
- rtx lab1, insns;
- enum machine_mode value_mode = TYPE_MODE (TREE_TYPE (exp));
- optab builtin_optab;
-
- switch (DECL_FUNCTION_CODE (fndecl))
- {
- case BUILT_IN_ABS:
- case BUILT_IN_LABS:
- case BUILT_IN_FABS:
- /* build_function_call changes these into ABS_EXPR. */
- abort ();
-
- case BUILT_IN_SIN:
- case BUILT_IN_COS:
- /* Treat these like sqrt, but only if the user asks for them. */
- if (! flag_fast_math)
- break;
- case BUILT_IN_FSQRT:
- /* If not optimizing, call the library function. */
- if (! optimize)
- break;
-
- if (arglist == 0
- /* Arg could be wrong type if user redeclared this fcn wrong. */
- || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != REAL_TYPE)
- break;
-
- /* Stabilize and compute the argument. */
- if (TREE_CODE (TREE_VALUE (arglist)) != VAR_DECL
- && TREE_CODE (TREE_VALUE (arglist)) != PARM_DECL)
- {
- exp = copy_node (exp);
- arglist = copy_node (arglist);
- TREE_OPERAND (exp, 1) = arglist;
- TREE_VALUE (arglist) = save_expr (TREE_VALUE (arglist));
- }
- op0 = expand_expr (TREE_VALUE (arglist), subtarget, VOIDmode, 0);
-
- /* Make a suitable register to place result in. */
- target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
-
- emit_queue ();
- start_sequence ();
-
- switch (DECL_FUNCTION_CODE (fndecl))
- {
- case BUILT_IN_SIN:
- builtin_optab = sin_optab; break;
- case BUILT_IN_COS:
- builtin_optab = cos_optab; break;
- case BUILT_IN_FSQRT:
- builtin_optab = sqrt_optab; break;
- default:
- abort ();
- }
-
- /* Compute into TARGET.
- Set TARGET to wherever the result comes back. */
- target = expand_unop (TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist))),
- builtin_optab, op0, target, 0);
-
- /* If we were unable to expand via the builtin, stop the
- sequence (without outputting the insns) and break, causing
- a call to the library function. */
- if (target == 0)
- {
- end_sequence ();
- break;
- }
-
- /* Check the results by default. But if flag_fast_math is turned on,
- then assume sqrt will always be called with valid arguments. */
-
- if (flag_errno_math && ! flag_fast_math)
- {
- /* Don't define the builtin FP instructions
- if your machine is not IEEE. */
- if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT)
- abort ();
-
- lab1 = 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, 0, lab1);
-
-#ifdef TARGET_EDOM
- {
-#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));
- }
-#else
- /* 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;
-#endif
-
- emit_label (lab1);
- }
-
- /* Output the entire sequence. */
- insns = get_insns ();
- end_sequence ();
- emit_insns (insns);
-
- return target;
-
- case BUILT_IN_FMOD:
- break;
-
- /* __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. */
- case BUILT_IN_APPLY_ARGS:
- /* 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 ();
- seq = get_insns ();
- end_sequence ();
-
- apply_args_value = temp;
-
- /* Put the sequence after the NOTE that starts the function.
- If this is inside a SEQUENCE, make the outer-level insn
- chain current, so the code is placed at the start of the
- function. */
- push_topmost_sequence ();
- emit_insns_before (seq, NEXT_INSN (get_insns ()));
- pop_topmost_sequence ();
- return temp;
- }
-
- /* __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 (arglist == 0
- /* Arg could be non-pointer if user redeclared this fcn wrong. */
- || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))
- || TREE_CHAIN (arglist) == 0
- || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE
- || TREE_CHAIN (TREE_CHAIN (arglist)) == 0
- || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))))) != INTEGER_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 (arglist
- /* Arg could be non-pointer if user redeclared this fcn wrong. */
- && TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) == POINTER_TYPE)
- expand_builtin_return (expand_expr (TREE_VALUE (arglist),
- NULL_RTX, VOIDmode, 0));
- return const0_rtx;
-
- case BUILT_IN_SAVEREGS:
- /* 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. */
- rtx temp;
- rtx seq;
-
- /* Now really call the function. `expand_call' does not call
- expand_builtin, so there is no danger of infinite recursion here. */
- start_sequence ();
-
-#ifdef EXPAND_BUILTIN_SAVEREGS
- /* Do whatever the machine needs done in this case. */
- temp = EXPAND_BUILTIN_SAVEREGS (arglist);
-#else
- /* The register where the function returns its value
- is likely to have something else in it, such as an argument.
- So preserve that register around the call. */
-
- if (value_mode != VOIDmode)
- {
- rtx valreg = hard_libcall_value (value_mode);
- rtx saved_valreg = gen_reg_rtx (value_mode);
-
- emit_move_insn (saved_valreg, valreg);
- temp = expand_call (exp, target, ignore);
- emit_move_insn (valreg, saved_valreg);
- }
- else
- /* Generate the call, putting the value in a pseudo. */
- temp = expand_call (exp, target, ignore);
-#endif
-
- seq = get_insns ();
- end_sequence ();
-
- saveregs_value = temp;
-
- /* Put the sequence after the NOTE that starts the function.
- If this is inside a SEQUENCE, make the outer-level insn
- chain current, so the code is placed at the start of the
- function. */
- push_topmost_sequence ();
- emit_insns_before (seq, NEXT_INSN (get_insns ()));
- pop_topmost_sequence ();
- return temp;
- }
-
- /* __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. */
- case BUILT_IN_ARGS_INFO:
- {
- int nwords = sizeof (CUMULATIVE_ARGS) / sizeof (int);
- int *word_ptr = (int *) &current_function_args_info;
-#if 0
- /* These are used by the code below that is if 0'ed away */
- int i;
- tree type, elts, result;
-#endif
-
- if (sizeof (CUMULATIVE_ARGS) % sizeof (int) != 0)
- fatal ("CUMULATIVE_ARGS type defined badly; see %s, line %d",
- __FILE__, __LINE__);
-
- if (arglist != 0)
- {
- tree arg = TREE_VALUE (arglist);
- if (TREE_CODE (arg) != INTEGER_CST)
- error ("argument of `__builtin_args_info' must be constant");
- else
- {
- int wordnum = TREE_INT_CST_LOW (arg);
-
- if (wordnum < 0 || wordnum >= nwords || TREE_INT_CST_HIGH (arg))
- 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;
-
-#if 0
- for (i = 0; i < nwords; i++)
- elts = tree_cons (NULL_TREE, build_int_2 (word_ptr[i], 0));
-
- type = build_array_type (integer_type_node,
- build_index_type (build_int_2 (nwords, 0)));
- result = build (CONSTRUCTOR, type, NULL_TREE, nreverse (elts));
- TREE_CONSTANT (result) = 1;
- TREE_STATIC (result) = 1;
- result = build (INDIRECT_REF, build_pointer_type (type), result);
- TREE_CONSTANT (result) = 1;
- return expand_expr (result, NULL_RTX, VOIDmode, EXPAND_MEMORY_USE_BAD);
-#endif
- }
-
- /* Return the address of the first anonymous stack arg. */
- case BUILT_IN_NEXT_ARG:
- {
- tree fntype = TREE_TYPE (current_function_decl);
-
- if ((TYPE_ARG_TYPES (fntype) == 0
- || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
- == void_type_node))
- && ! current_function_varargs)
- {
- 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 if (! current_function_varargs)
- /* Evidently an out of date version of <stdarg.h>; 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);
-
- case BUILT_IN_CLASSIFY_TYPE:
- if (arglist != 0)
- {
- tree type = TREE_TYPE (TREE_VALUE (arglist));
- enum tree_code code = TREE_CODE (type);
- if (code == VOID_TYPE)
- return GEN_INT (void_type_class);
- if (code == INTEGER_TYPE)
- return GEN_INT (integer_type_class);
- if (code == CHAR_TYPE)
- return GEN_INT (char_type_class);
- if (code == ENUMERAL_TYPE)
- return GEN_INT (enumeral_type_class);
- if (code == BOOLEAN_TYPE)
- return GEN_INT (boolean_type_class);
- if (code == POINTER_TYPE)
- return GEN_INT (pointer_type_class);
- if (code == REFERENCE_TYPE)
- return GEN_INT (reference_type_class);
- if (code == OFFSET_TYPE)
- return GEN_INT (offset_type_class);
- if (code == REAL_TYPE)
- return GEN_INT (real_type_class);
- if (code == COMPLEX_TYPE)
- return GEN_INT (complex_type_class);
- if (code == FUNCTION_TYPE)
- return GEN_INT (function_type_class);
- if (code == METHOD_TYPE)
- return GEN_INT (method_type_class);
- if (code == RECORD_TYPE)
- return GEN_INT (record_type_class);
- if (code == UNION_TYPE || code == QUAL_UNION_TYPE)
- return GEN_INT (union_type_class);
- if (code == ARRAY_TYPE)
- {
- if (TYPE_STRING_FLAG (type))
- return GEN_INT (string_type_class);
- else
- return GEN_INT (array_type_class);
- }
- if (code == SET_TYPE)
- return GEN_INT (set_type_class);
- if (code == FILE_TYPE)
- return GEN_INT (file_type_class);
- if (code == LANG_TYPE)
- return GEN_INT (lang_type_class);
- }
- return GEN_INT (no_type_class);
-
- case BUILT_IN_CONSTANT_P:
- if (arglist == 0)
- return const0_rtx;
- else
- {
- tree arg = TREE_VALUE (arglist);
- rtx tmp;
-
- /* 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 (arg);
-
- /* If we know this is a constant, emit the constant of one. */
- if (TREE_CODE_CLASS (TREE_CODE (arg)) == 'c'
- || (TREE_CODE (arg) == CONSTRUCTOR
- && TREE_CONSTANT (arg))
- || (TREE_CODE (arg) == ADDR_EXPR
- && TREE_CODE (TREE_OPERAND (arg, 0)) == STRING_CST))
- return const1_rtx;
-
- /* If we aren't going to be running CSE or 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. */
- if (TREE_SIDE_EFFECTS (arg) || cse_not_expected
- || AGGREGATE_TYPE_P (TREE_TYPE (arg))
- || POINTER_TYPE_P (TREE_TYPE (arg)))
- return const0_rtx;
-
- /* Otherwise, emit (constant_p_rtx (ARG)) and let CSE get a
- chance to see if it can deduce whether ARG is constant. */
-
- tmp = expand_expr (arg, NULL_RTX, VOIDmode, 0);
- tmp = gen_rtx_CONSTANT_P_RTX (value_mode, tmp);
- return tmp;
- }
-
- case BUILT_IN_FRAME_ADDRESS:
- /* The argument must be a nonnegative integer constant.
- It counts the number of frames to scan up the stack.
- The value is the address of that frame. */
- case BUILT_IN_RETURN_ADDRESS:
- /* 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 (TREE_CODE (TREE_VALUE (arglist)) != INTEGER_CST
- || tree_int_cst_sgn (TREE_VALUE (arglist)) < 0)
- {
- 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_INT_CST_LOW (TREE_VALUE (arglist)),
- 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;
- }
-
- /* 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:
- if (arglist == 0
- /* Arg could be non-integer if user redeclared this fcn wrong. */
- || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE)
- break;
-
- /* Compute the argument. */
- op0 = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0);
-
- /* Allocate the desired space. */
- return allocate_dynamic_stack_space (op0, target, BITS_PER_UNIT);
-
- case BUILT_IN_FFS:
- /* If not optimizing, call the library function. */
- if (!optimize && ! CALLED_AS_BUILT_IN (fndecl))
- break;
-
- if (arglist == 0
- /* Arg could be non-integer if user redeclared this fcn wrong. */
- || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE)
- break;
-
- /* Compute the argument. */
- op0 = expand_expr (TREE_VALUE (arglist), subtarget, VOIDmode, 0);
- /* Compute ffs, into TARGET if possible.
- Set TARGET to wherever the result comes back. */
- target = expand_unop (TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist))),
- ffs_optab, op0, target, 1);
- if (target == 0)
- abort ();
- return target;
-
- case BUILT_IN_STRLEN:
- /* If not optimizing, call the library function. */
- if (!optimize && ! CALLED_AS_BUILT_IN (fndecl))
- break;
-
- if (arglist == 0
- /* Arg could be non-pointer if user redeclared this fcn wrong. */
- || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE)
- break;
- else
- {
- tree src = TREE_VALUE (arglist);
- tree len = c_strlen (src);
-
- int align
- = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
-
- rtx result, src_rtx, char_rtx;
- enum machine_mode insn_mode = value_mode, char_mode;
- enum insn_code icode;
-
- /* If the length is known, just return it. */
- if (len != 0)
- return expand_expr (len, target, mode, EXPAND_MEMORY_USE_BAD);
-
- /* If SRC is not a pointer type, don't do this operation inline. */
- if (align == 0)
- break;
-
- /* Call a function 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)
- break;
-
- /* 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 sure the operands are acceptable to the predicates. */
-
- if (! (*insn_operand_predicate[(int)icode][0]) (result, insn_mode))
- result = gen_reg_rtx (insn_mode);
- src_rtx = memory_address (BLKmode,
- expand_expr (src, NULL_RTX, ptr_mode,
- EXPAND_NORMAL));
-
- if (! (*insn_operand_predicate[(int)icode][1]) (src_rtx, Pmode))
- src_rtx = copy_to_mode_reg (Pmode, src_rtx);
-
- /* Check the string is readable and has an end. */
- if (current_function_check_memory_usage)
- emit_library_call (chkr_check_str_libfunc, 1, VOIDmode, 2,
- src_rtx, Pmode,
- GEN_INT (MEMORY_USE_RO),
- TYPE_MODE (integer_type_node));
-
- char_rtx = const0_rtx;
- char_mode = insn_operand_mode[(int)icode][2];
- if (! (*insn_operand_predicate[(int)icode][2]) (char_rtx, char_mode))
- char_rtx = copy_to_mode_reg (char_mode, char_rtx);
-
- emit_insn (GEN_FCN (icode) (result,
- gen_rtx_MEM (BLKmode, src_rtx),
- char_rtx, GEN_INT (align)));
-
- /* Return the value in the proper mode for this function. */
- if (GET_MODE (result) == value_mode)
- return result;
- else if (target != 0)
- {
- convert_move (target, result, 0);
- return target;
- }
- else
- return convert_to_mode (value_mode, result, 0);
- }
-
- case BUILT_IN_STRCPY:
- /* If not optimizing, call the library function. */
- if (!optimize && ! CALLED_AS_BUILT_IN (fndecl))
- break;
-
- if (arglist == 0
- /* Arg could be non-pointer if user redeclared this fcn wrong. */
- || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
- || TREE_CHAIN (arglist) == 0
- || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE)
- break;
- else
- {
- tree len = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)));
-
- if (len == 0)
- break;
-
- len = size_binop (PLUS_EXPR, len, integer_one_node);
-
- chainon (arglist, build_tree_list (NULL_TREE, len));
- }
-
- /* Drops in. */
- case BUILT_IN_MEMCPY:
- /* If not optimizing, call the library function. */
- if (!optimize && ! CALLED_AS_BUILT_IN (fndecl))
- break;
-
- if (arglist == 0
- /* Arg could be non-pointer if user redeclared this fcn wrong. */
- || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
- || TREE_CHAIN (arglist) == 0
- || (TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist))))
- != POINTER_TYPE)
- || TREE_CHAIN (TREE_CHAIN (arglist)) == 0
- || (TREE_CODE (TREE_TYPE (TREE_VALUE
- (TREE_CHAIN (TREE_CHAIN (arglist)))))
- != INTEGER_TYPE))
- break;
- else
- {
- tree dest = TREE_VALUE (arglist);
- tree src = TREE_VALUE (TREE_CHAIN (arglist));
- tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
-
- int src_align
- = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
- int dest_align
- = get_pointer_alignment (dest, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
- rtx dest_mem, src_mem, dest_addr, len_rtx;
-
- /* If either SRC or DEST is not a pointer type, don't do
- this operation in-line. */
- if (src_align == 0 || dest_align == 0)
- {
- if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_STRCPY)
- TREE_CHAIN (TREE_CHAIN (arglist)) = 0;
- break;
- }
-
- dest_mem = get_memory_rtx (dest);
- src_mem = get_memory_rtx (src);
- len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
-
- /* Just copy the rights of SRC to the rights of DEST. */
- if (current_function_check_memory_usage)
- emit_library_call (chkr_copy_bitmap_libfunc, 1, VOIDmode, 3,
- XEXP (dest_mem, 0), Pmode,
- XEXP (src_mem, 0), Pmode,
- len_rtx, TYPE_MODE (sizetype));
-
- /* Copy word part most expediently. */
- dest_addr
- = emit_block_move (dest_mem, src_mem, len_rtx,
- MIN (src_align, dest_align));
-
- if (dest_addr == 0)
- dest_addr = force_operand (XEXP (dest_mem, 0), NULL_RTX);
-
- return dest_addr;
- }
-
- case BUILT_IN_MEMSET:
- /* If not optimizing, call the library function. */
- if (!optimize && ! CALLED_AS_BUILT_IN (fndecl))
- break;
-
- if (arglist == 0
- /* Arg could be non-pointer if user redeclared this fcn wrong. */
- || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
- || TREE_CHAIN (arglist) == 0
- || (TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist))))
- != INTEGER_TYPE)
- || TREE_CHAIN (TREE_CHAIN (arglist)) == 0
- || (INTEGER_TYPE
- != (TREE_CODE (TREE_TYPE
- (TREE_VALUE
- (TREE_CHAIN (TREE_CHAIN (arglist))))))))
- break;
- else
- {
- tree dest = TREE_VALUE (arglist);
- tree val = TREE_VALUE (TREE_CHAIN (arglist));
- tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
-
- int dest_align
- = get_pointer_alignment (dest, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
- 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)
- break;
-
- /* If the arguments have side-effects, then we can only evaluate
- them at most once. The following code evaluates them twice if
- they are not constants because we break out to expand_call
- in that case. They can't be constants if they have side-effects
- so we can check for that first. Alternatively, we could call
- save_expr to make multiple evaluation safe. */
- if (TREE_SIDE_EFFECTS (val) || TREE_SIDE_EFFECTS (len))
- break;
-
- /* If VAL is not 0, don't do this operation in-line. */
- if (expand_expr (val, NULL_RTX, VOIDmode, 0) != const0_rtx)
- break;
-
- /* If LEN does not expand to a constant, don't do this
- operation in-line. */
- len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
- if (GET_CODE (len_rtx) != CONST_INT)
- break;
-
- dest_mem = get_memory_rtx (dest);
-
- /* Just check DST is writable and mark it as readable. */
- if (current_function_check_memory_usage)
- emit_library_call (chkr_check_addr_libfunc, 1, VOIDmode, 3,
- XEXP (dest_mem, 0), Pmode,
- len_rtx, TYPE_MODE (sizetype),
- GEN_INT (MEMORY_USE_WO),
- TYPE_MODE (integer_type_node));
-
-
- dest_addr = clear_storage (dest_mem, len_rtx, dest_align);
-
- if (dest_addr == 0)
- dest_addr = force_operand (XEXP (dest_mem, 0), NULL_RTX);
-
- return dest_addr;
- }
-
-/* These comparison functions need an instruction that returns an actual
- index. An ordinary compare that just sets the condition codes
- is not enough. */
-#ifdef HAVE_cmpstrsi
- case BUILT_IN_STRCMP:
- /* If not optimizing, call the library function. */
- if (!optimize && ! CALLED_AS_BUILT_IN (fndecl))
- break;
-
- /* If we need to check memory accesses, call the library function. */
- if (current_function_check_memory_usage)
- break;
-
- if (arglist == 0
- /* Arg could be non-pointer if user redeclared this fcn wrong. */
- || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
- || TREE_CHAIN (arglist) == 0
- || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE)
- break;
- else if (!HAVE_cmpstrsi)
- break;
- {
- tree arg1 = TREE_VALUE (arglist);
- tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
- tree len, len2;
-
- len = c_strlen (arg1);
- if (len)
- len = size_binop (PLUS_EXPR, integer_one_node, len);
- len2 = c_strlen (arg2);
- if (len2)
- len2 = size_binop (PLUS_EXPR, integer_one_node, 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.
-
- If both strings have constant lengths, use the smaller. This
- could arise if optimization results in strcpy being called with
- two fixed strings, or if the code was machine-generated. We should
- add some code to the `memcmp' handler below to deal with such
- situations, someday. */
- if (!len || TREE_CODE (len) != INTEGER_CST)
- {
- if (len2)
- len = len2;
- else if (len == 0)
- break;
- }
- else if (len2 && TREE_CODE (len2) == INTEGER_CST)
- {
- if (tree_int_cst_lt (len2, len))
- len = len2;
- }
-
- chainon (arglist, build_tree_list (NULL_TREE, len));
- }
-
- /* Drops in. */
- case BUILT_IN_MEMCMP:
- /* If not optimizing, call the library function. */
- if (!optimize && ! CALLED_AS_BUILT_IN (fndecl))
- break;
-
- /* If we need to check memory accesses, call the library function. */
- if (current_function_check_memory_usage)
- break;
-
- if (arglist == 0
- /* Arg could be non-pointer if user redeclared this fcn wrong. */
- || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
- || TREE_CHAIN (arglist) == 0
- || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE
- || TREE_CHAIN (TREE_CHAIN (arglist)) == 0
- || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))))) != INTEGER_TYPE)
- break;
- else if (!HAVE_cmpstrsi)
- break;
- {
- tree arg1 = TREE_VALUE (arglist);
- tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
- tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
- rtx result;
-
- 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_operand_mode[(int) CODE_FOR_cmpstrsi][0];
-
- /* If we don't have POINTER_TYPE, call the function. */
- if (arg1_align == 0 || arg2_align == 0)
- {
- if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_STRCMP)
- TREE_CHAIN (TREE_CHAIN (arglist)) = 0;
- break;
- }
-
- /* 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);
-
- emit_insn (gen_cmpstrsi (result, get_memory_rtx (arg1),
- get_memory_rtx (arg2),
- expand_expr (len, NULL_RTX, VOIDmode, 0),
- GEN_INT (MIN (arg1_align, arg2_align))));
-
- /* 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);
- }
-#else
- case BUILT_IN_STRCMP:
- case BUILT_IN_MEMCMP:
- break;
-#endif
-
- case BUILT_IN_SETJMP:
- if (arglist == 0
- || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE)
- break;
- else
- {
- rtx buf_addr = expand_expr (TREE_VALUE (arglist), subtarget,
- VOIDmode, 0);
- rtx lab = gen_label_rtx ();
- rtx ret = expand_builtin_setjmp (buf_addr, target, lab, lab);
- emit_label (lab);
- return ret;
- }
-
- /* __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 (arglist == 0 || TREE_CHAIN (arglist) == 0
- || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_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:
-#ifdef HAVE_trap
- if (HAVE_trap)
- emit_insn (gen_trap ());
- else
-#endif
- error ("__builtin_trap not supported by this target");
- emit_barrier ();
- return const0_rtx;
-
- /* 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_FP_REGNUM:
- return expand_builtin_dwarf_fp_regnum ();
- case BUILT_IN_DWARF_REG_SIZE:
- return expand_builtin_dwarf_reg_size (TREE_VALUE (arglist), target);
-#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)),
- TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))));
- return const0_rtx;
-
- default: /* just do library call, if unknown builtin */
- 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);
-}
-
-/* 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 (regno)
- 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 ()
-{
- static int size = -1;
- int align, 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 (struct_value_rtx)
- 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)
- && (mov_optab->handlers[(int) mode].insn_code
- != CODE_FOR_nothing))
- 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 ()
-{
- 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)
- && (mov_optab->handlers[(int) mode].insn_code
- != CODE_FOR_nothing))
- 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 (savep, result)
- int savep;
- rtx result;
-{
- int regno, size, align, nelts;
- enum machine_mode mode;
- rtx reg, mem;
- rtx *savevec = (rtx *) 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 = change_address (result, mode,
- plus_constant (XEXP (result, 0), 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 ()
-{
- rtx registers;
- int size, align, regno;
- enum machine_mode mode;
-
- /* 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 (struct_value_rtx)
- 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)
- {
- rtx tem;
-
- 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));
-
-#ifdef STACK_REGS
- /* For reg-stack.c's stack register household.
- Compare with a similar piece of code in function.c. */
-
- emit_insn (gen_rtx_USE (mode, tem));
-#endif
-
- emit_move_insn (change_address (registers, mode,
- plus_constant (XEXP (registers, 0),
- size)),
- tem);
- size += GET_MODE_SIZE (mode);
- }
-
- /* Save the arg pointer to the block. */
- emit_move_insn (change_address (registers, Pmode, XEXP (registers, 0)),
- copy_to_reg (virtual_incoming_args_rtx));
- size = GET_MODE_SIZE (Pmode);
-
- /* Save the structure value address unless this is passed as an
- "invisible" first argument. */
- if (struct_value_incoming_rtx)
- {
- emit_move_insn (change_address (registers, Pmode,
- plus_constant (XEXP (registers, 0),
- size)),
- copy_to_reg (struct_value_incoming_rtx));
- size += GET_MODE_SIZE (Pmode);
- }
-
- /* Return the address of the block. */
- return copy_addr_to_reg (XEXP (registers, 0));
-}
-
-/* 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 (function, arguments, argsize)
- rtx function, arguments, argsize;
-{
- int size, align, regno;
- enum machine_mode mode;
- rtx incoming_args, result, reg, dest, call_insn;
- rtx old_stack_level = 0;
- rtx call_fusage = 0;
-
- /* Create a block where the return registers can be saved. */
- result = assign_stack_local (BLKmode, apply_result_size (), -1);
-
- /* ??? The argsize value should be adjusted here. */
-
- /* 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_binop (Pmode, sub_optab, 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_pending_stack_adjust ();
-
- /* 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);
-
- /* Push a block of memory onto the stack to store the memory arguments.
- Save the address in a register, and copy the memory arguments. ??? I
- haven't figured out how the calling convention macros effect this,
- but it's likely that the source and/or destination addresses in
- the block copy will need updating in machine specific ways. */
- dest = allocate_dynamic_stack_space (argsize, 0, 0);
- emit_block_move (gen_rtx_MEM (BLKmode, dest),
- gen_rtx_MEM (BLKmode, incoming_args),
- argsize,
- PARM_BOUNDARY / BITS_PER_UNIT);
-
- /* Refer to the argument block. */
- apply_args_size ();
- arguments = gen_rtx_MEM (BLKmode, arguments);
-
- /* Walk past the arg-pointer and structure value address. */
- size = GET_MODE_SIZE (Pmode);
- if (struct_value_rtx)
- 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,
- change_address (arguments, mode,
- plus_constant (XEXP (arguments, 0),
- 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)
- {
- rtx value = gen_reg_rtx (Pmode);
- emit_move_insn (value,
- change_address (arguments, Pmode,
- plus_constant (XEXP (arguments, 0),
- size)));
- emit_move_insn (struct_value_rtx, value);
- if (GET_CODE (struct_value_rtx) == REG)
- use_reg (&call_fusage, struct_value_rtx);
- 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);
-
- /* 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 (change_address (result, GET_MODE (valreg),
- XEXP (result, 0)),
- valreg);
- }
- else
-#endif
- abort ();
-
- /* Find the CALL insn we just emitted. */
- for (call_insn = get_last_insn ();
- call_insn && GET_CODE (call_insn) != CALL_INSN;
- call_insn = PREV_INSN (call_insn))
- ;
-
- if (! call_insn)
- abort ();
-
- /* Put the register usage information on the CALL. If there is already
- some usage information, put ours at the end. */
- if (CALL_INSN_FUNCTION_USAGE (call_insn))
- {
- rtx link;
-
- for (link = CALL_INSN_FUNCTION_USAGE (call_insn); XEXP (link, 1) != 0;
- link = XEXP (link, 1))
- ;
-
- XEXP (link, 1) = call_fusage;
- }
- else
- CALL_INSN_FUNCTION_USAGE (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);
-
- /* Return the address of the result block. */
- return copy_addr_to_reg (XEXP (result, 0));
-}
-
-/* Perform an untyped return. */
-
-static void
-expand_builtin_return (result)
- rtx result;
-{
- int size, align, regno;
- enum machine_mode mode;
- rtx reg;
- rtx call_fusage = 0;
-
- 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,
- change_address (result, mode,
- plus_constant (XEXP (result, 0),
- 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_insns (call_fusage);
-
- /* Return whatever values was restored by jumping directly to the end
- of the function. */
- expand_null_return ();
-}
/* Expand code for a post- or pre- increment or decrement
and return the RTX for the result.
diff --git a/gcc/expr.h b/gcc/expr.h
index 55e82e6..6bde47a 100644
--- a/gcc/expr.h
+++ b/gcc/expr.h
@@ -75,6 +75,14 @@ extern rtx forced_labels;
So we can mark them all live at the end of the function, if stupid. */
extern rtx save_expr_regs;
+/* Nonzero means __builtin_saveregs has already been done in this function.
+ The value is the pseudoreg containing the value __builtin_saveregs
+ returned. */
+extern rtx saveregs_value;
+
+/* Similarly for __builtin_apply_args. */
+extern rtx apply_args_value;
+
extern int current_function_calls_alloca;
extern int current_function_outgoing_args_size;
@@ -727,6 +735,13 @@ extern rtx get_condition PROTO((rtx, rtx *));
/* Generate a conditional trap instruction. */
extern rtx gen_cond_trap PROTO((enum rtx_code, rtx, rtx, rtx));
+/* Functions from builtins.c: */
+#ifdef TREE_CODE
+extern rtx expand_builtin PROTO((tree, rtx, rtx, enum machine_mode, int));
+#endif
+
+extern rtx expand_builtin_setjmp PROTO((rtx, rtx, rtx, rtx));
+
/* Functions from expr.c: */
/* This is run once per compilation to set up which modes can be used
@@ -831,8 +846,6 @@ extern rtx store_expr PROTO((tree, rtx, int));
Useful after calling expand_expr with 1 as sum_ok. */
extern rtx force_operand PROTO((rtx, rtx));
-extern rtx expand_builtin_setjmp PROTO((rtx, rtx, rtx, rtx));
-
#ifdef TREE_CODE
/* Generate code for computing expression EXP.
An rtx for the computed value is returned. The value is never null.
@@ -853,6 +866,10 @@ extern void clear_pending_stack_adjust PROTO((void));
extern void do_pending_stack_adjust PROTO((void));
#ifdef TREE_CODE
+/* Return the tree node and offset if a given argument corresponds to
+ a string constant. */
+extern tree string_constant PROTO((tree, tree *));
+
/* Generate code to evaluate EXP and jump to LABEL if the value is zero. */
extern void jumpifnot PROTO((tree, rtx));