aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/loop-iv.c2465
-rw-r--r--gcc/passes.c2177
-rw-r--r--gcc/testsuite/g++.dg/abi/mangle21.C13
-rw-r--r--gcc/testsuite/g++.dg/charset/asm1.c14
-rw-r--r--gcc/testsuite/g++.dg/charset/asm2.c33
-rw-r--r--gcc/testsuite/g++.dg/charset/asm3.c10
-rw-r--r--gcc/testsuite/g++.dg/charset/asm4.c8
-rw-r--r--gcc/testsuite/g++.dg/charset/attribute1.c10
-rw-r--r--gcc/testsuite/g++.dg/charset/attribute2.c8
-rw-r--r--gcc/testsuite/g++.dg/charset/extern1.cc15
-rw-r--r--gcc/testsuite/g++.dg/charset/extern2.cc5
-rw-r--r--gcc/testsuite/g++.dg/charset/string.c5
-rw-r--r--gcc/testsuite/g++.dg/debug/crash1.C17
-rw-r--r--gcc/testsuite/g++.dg/expr/crash2.C14
-rw-r--r--gcc/testsuite/g++.dg/ext/altivec-3.C128
-rw-r--r--gcc/testsuite/g++.dg/parse/comma1.C13
-rw-r--r--gcc/testsuite/g++.dg/parse/constructor2.C11
-rw-r--r--gcc/testsuite/g++.dg/parse/defarg7.C5
-rw-r--r--gcc/testsuite/g++.dg/template/cond4.C20
-rw-r--r--gcc/testsuite/g++.dg/template/friend26.C15
-rw-r--r--gcc/testsuite/g++.dg/template/sfinae1.C21
-rw-r--r--gcc/testsuite/g++.dg/template/ttp8.C16
-rw-r--r--gcc/testsuite/g++.dg/warn/Wunused-6.C11
-rw-r--r--gcc/testsuite/gcc.dg/always_inline2.c8
-rw-r--r--gcc/testsuite/gcc.dg/always_inline3.c8
-rw-r--r--gcc/testsuite/gcc.dg/attr-alias-1.c8
-rw-r--r--gcc/testsuite/gcc.dg/charset/asm1.c14
-rw-r--r--gcc/testsuite/gcc.dg/charset/asm3.c33
-rw-r--r--gcc/testsuite/gcc.dg/charset/asm4.c10
-rw-r--r--gcc/testsuite/gcc.dg/charset/asm5.c8
-rw-r--r--gcc/testsuite/gcc.dg/charset/attribute1.c10
-rw-r--r--gcc/testsuite/gcc.dg/charset/attribute2.c8
-rw-r--r--gcc/testsuite/gcc.dg/charset/charset.exp44
-rw-r--r--gcc/testsuite/gcc.dg/charset/string.c5
-rw-r--r--gcc/testsuite/gcc.dg/fixuns-trunc-1.c31
-rw-r--r--gcc/testsuite/gcc.dg/inline-5.c13
-rw-r--r--intl/configure.ac33
-rw-r--r--libjava/gnu/java/nio/channels/FileChannelImpl.java420
-rw-r--r--libjava/gnu/java/nio/channels/natFileChannelEcos.cc159
-rw-r--r--libjava/gnu/java/nio/channels/natFileChannelPosix.cc522
-rw-r--r--libjava/gnu/java/nio/channels/natFileChannelWin32.cc432
-rw-r--r--libstdc++-v3/config/cpu/cris/atomic_word.h36
-rw-r--r--libstdc++-v3/config/cpu/generic/atomic_word.h35
-rw-r--r--libstdc++-v3/config/cpu/sparc/atomic_word.h39
-rw-r--r--libstdc++-v3/config/os/irix/atomic_word.h35
-rw-r--r--libstdc++-v3/config/os/irix/atomicity.h42
-rw-r--r--libstdc++-v3/docs/html/20_util/allocator.html472
-rw-r--r--libstdc++-v3/docs/html/ext/mt_allocator.html420
-rw-r--r--libstdc++-v3/include/bits/atomicity.h46
-rw-r--r--libstdc++-v3/include/ext/demangle.h2789
-rw-r--r--libstdc++-v3/testsuite/22_locale/money_get/get/char/15.cc68
-rw-r--r--libstdc++-v3/testsuite/22_locale/money_get/get/wchar_t/15.cc69
-rw-r--r--libstdc++-v3/testsuite/22_locale/num_get/get/char/14.cc59
-rw-r--r--libstdc++-v3/testsuite/22_locale/num_get/get/char/15.cc75
-rw-r--r--libstdc++-v3/testsuite/22_locale/num_get/get/wchar_t/14.cc59
-rw-r--r--libstdc++-v3/testsuite/22_locale/num_get/get/wchar_t/15.cc75
-rw-r--r--libstdc++-v3/testsuite/lib/dg-options.exp29
57 files changed, 11148 insertions, 0 deletions
diff --git a/gcc/loop-iv.c b/gcc/loop-iv.c
new file mode 100644
index 00000000000..9c170dec2ec
--- /dev/null
+++ b/gcc/loop-iv.c
@@ -0,0 +1,2465 @@
+/* Rtl-level induction variable analysis.
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
+
+/* This is just a very simplistic analysis of induction variables of the loop.
+ The major use is for determining the number of iterations of a loop for
+ loop unrolling, doloop optimization and branch prediction. For this we
+ are only interested in bivs and a fairly limited set of givs that are
+ needed in the exit condition. We also only compute the iv information on
+ demand.
+
+ The interesting registers are determined. A register is interesting if
+
+ -- it is set only in the blocks that dominate the latch of the current loop
+ -- all its sets are simple -- i.e. in the form we understand
+
+ We also number the insns sequentially in each basic block. For a use of the
+ interesting reg, it is now easy to find a reaching definition (there may be
+ only one).
+
+ Induction variable is then simply analyzed by walking the use-def
+ chains.
+
+ Usage:
+
+ iv_analysis_loop_init (loop);
+ insn = iv_get_reaching_def (where, reg);
+ if (iv_analyze (insn, reg, &iv))
+ {
+ ...
+ }
+ iv_analysis_done (); */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "hard-reg-set.h"
+#include "basic-block.h"
+#include "cfgloop.h"
+#include "expr.h"
+#include "output.h"
+
+/* The insn information. */
+
+struct insn_info
+{
+ /* Id of the insn. */
+ unsigned luid;
+
+ /* The previous definition of the register defined by the single
+ set in the insn. */
+ rtx prev_def;
+
+ /* The description of the iv. */
+ struct rtx_iv iv;
+};
+
+static struct insn_info *insn_info;
+
+/* The last definition of register. */
+
+static rtx *last_def;
+
+/* The bivs. */
+
+static struct rtx_iv *bivs;
+
+/* Maximal insn number for that there is place in insn_info array. */
+
+static unsigned max_insn_no;
+
+/* Maximal register number for that there is place in bivs and last_def
+ arrays. */
+
+static unsigned max_reg_no;
+
+/* Dumps information about IV to FILE. */
+
+extern void dump_iv_info (FILE *, struct rtx_iv *);
+void
+dump_iv_info (FILE *file, struct rtx_iv *iv)
+{
+ if (!iv->base)
+ {
+ fprintf (file, "not simple");
+ return;
+ }
+
+ if (iv->step == const0_rtx)
+ {
+ fprintf (file, "invariant ");
+ print_rtl (file, iv->base);
+ return;
+ }
+
+ print_rtl (file, iv->base);
+ fprintf (file, " + ");
+ print_rtl (file, iv->step);
+ fprintf (file, " * iteration");
+ fprintf (file, " (in %s)", GET_MODE_NAME (iv->mode));
+
+ if (iv->mode != iv->extend_mode)
+ fprintf (file, " %s to %s",
+ rtx_name[iv->extend],
+ GET_MODE_NAME (iv->extend_mode));
+
+ if (iv->mult != const1_rtx)
+ {
+ fprintf (file, " * ");
+ print_rtl (file, iv->mult);
+ }
+ if (iv->delta != const0_rtx)
+ {
+ fprintf (file, " + ");
+ print_rtl (file, iv->delta);
+ }
+ if (iv->first_special)
+ fprintf (file, " (first special)");
+}
+
+/* Assigns luids to insns in basic block BB. */
+
+static void
+assign_luids (basic_block bb)
+{
+ unsigned i = 0, uid;
+ rtx insn;
+
+ FOR_BB_INSNS (bb, insn)
+ {
+ uid = INSN_UID (insn);
+ insn_info[uid].luid = i++;
+ insn_info[uid].prev_def = NULL_RTX;
+ insn_info[uid].iv.analysed = false;
+ }
+}
+
+/* Generates a subreg to get the least significant part of EXPR (in mode
+ INNER_MODE) to OUTER_MODE. */
+
+static rtx
+lowpart_subreg (enum machine_mode outer_mode, rtx expr,
+ enum machine_mode inner_mode)
+{
+ return simplify_gen_subreg (outer_mode, expr, inner_mode,
+ subreg_lowpart_offset (outer_mode, inner_mode));
+}
+
+/* Checks whether REG is a well-behaved register. */
+
+static bool
+simple_reg_p (rtx reg)
+{
+ unsigned r;
+
+ if (GET_CODE (reg) == SUBREG)
+ {
+ if (!subreg_lowpart_p (reg))
+ return false;
+ reg = SUBREG_REG (reg);
+ }
+
+ if (!REG_P (reg))
+ return false;
+
+ r = REGNO (reg);
+ if (HARD_REGISTER_NUM_P (r))
+ return false;
+
+ if (GET_MODE_CLASS (GET_MODE (reg)) != MODE_INT)
+ return false;
+
+ if (last_def[r] == const0_rtx)
+ return false;
+
+ return true;
+}
+
+/* Checks whether assignment LHS = RHS is simple enough for us to process. */
+
+static bool
+simple_set_p (rtx lhs, rtx rhs)
+{
+ rtx op0, op1;
+
+ if (!REG_P (lhs)
+ || !simple_reg_p (lhs))
+ return false;
+
+ if (CONSTANT_P (rhs))
+ return true;
+
+ switch (GET_CODE (rhs))
+ {
+ case SUBREG:
+ case REG:
+ return simple_reg_p (rhs);
+
+ case SIGN_EXTEND:
+ case ZERO_EXTEND:
+ case NEG:
+ return simple_reg_p (XEXP (rhs, 0));
+
+ case PLUS:
+ case MINUS:
+ case MULT:
+ op0 = XEXP (rhs, 0);
+ op1 = XEXP (rhs, 1);
+
+ if (!simple_reg_p (op0)
+ && !CONSTANT_P (op0))
+ return false;
+
+ if (!simple_reg_p (op1)
+ && !CONSTANT_P (op1))
+ return false;
+
+ if (GET_CODE (rhs) == MULT
+ && !CONSTANT_P (op0)
+ && !CONSTANT_P (op1))
+ return false;
+
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/* Mark single SET in INSN. */
+
+static rtx
+mark_single_set (rtx insn, rtx set)
+{
+ rtx def = SET_DEST (set), src;
+ unsigned regno, uid;
+
+ src = find_reg_equal_equiv_note (insn);
+ if (!src)
+ src = SET_SRC (set);
+
+ if (!simple_set_p (SET_DEST (set), src))
+ return NULL_RTX;
+
+ regno = REGNO (def);
+ uid = INSN_UID (insn);
+
+ bivs[regno].analysed = false;
+ insn_info[uid].prev_def = last_def[regno];
+ last_def[regno] = insn;
+
+ return def;
+}
+
+/* Invalidate register REG unless it is equal to EXCEPT. */
+
+static void
+kill_sets (rtx reg, rtx by ATTRIBUTE_UNUSED, void *except)
+{
+ if (GET_CODE (reg) == SUBREG)
+ reg = SUBREG_REG (reg);
+ if (!REG_P (reg))
+ return;
+ if (reg == except)
+ return;
+
+ last_def[REGNO (reg)] = const0_rtx;
+}
+
+/* Marks sets in basic block BB. If DOM is true, BB dominates the loop
+ latch. */
+
+static void
+mark_sets (basic_block bb, bool dom)
+{
+ rtx insn, set, def;
+
+ FOR_BB_INSNS (bb, insn)
+ {
+ if (!INSN_P (insn))
+ continue;
+
+ if (dom
+ && (set = single_set (insn)))
+ def = mark_single_set (insn, set);
+ else
+ def = NULL_RTX;
+
+ note_stores (PATTERN (insn), kill_sets, def);
+ }
+}
+
+/* Prepare the data for an induction variable analysis of a LOOP. */
+
+void
+iv_analysis_loop_init (struct loop *loop)
+{
+ basic_block *body = get_loop_body_in_dom_order (loop);
+ unsigned b;
+
+ if ((unsigned) get_max_uid () >= max_insn_no)
+ {
+ /* Add some reserve for insns and registers produced in optimizations. */
+ max_insn_no = get_max_uid () + 100;
+ if (insn_info)
+ free (insn_info);
+ insn_info = xmalloc (max_insn_no * sizeof (struct insn_info));
+ }
+
+ if ((unsigned) max_reg_num () >= max_reg_no)
+ {
+ max_reg_no = max_reg_num () + 100;
+ if (last_def)
+ free (last_def);
+ last_def = xmalloc (max_reg_no * sizeof (rtx));
+ if (bivs)
+ free (bivs);
+ bivs = xmalloc (max_reg_no * sizeof (struct rtx_iv));
+ }
+
+ memset (last_def, 0, max_reg_num () * sizeof (rtx));
+
+ for (b = 0; b < loop->num_nodes; b++)
+ {
+ assign_luids (body[b]);
+ mark_sets (body[b], just_once_each_iteration_p (loop, body[b]));
+ }
+
+ free (body);
+}
+
+/* Gets definition of REG reaching the INSN. If REG is not simple, const0_rtx
+ is returned. If INSN is before the first def in the loop, NULL_RTX is
+ returned. */
+
+rtx
+iv_get_reaching_def (rtx insn, rtx reg)
+{
+ unsigned regno, luid, auid;
+ rtx ainsn;
+ basic_block bb, abb;
+
+ if (GET_CODE (reg) == SUBREG)
+ {
+ if (!subreg_lowpart_p (reg))
+ return const0_rtx;
+ reg = SUBREG_REG (reg);
+ }
+ if (!REG_P (reg))
+ return NULL_RTX;
+
+ regno = REGNO (reg);
+ if (!last_def[regno]
+ || last_def[regno] == const0_rtx)
+ return last_def[regno];
+
+ bb = BLOCK_FOR_INSN (insn);
+ luid = insn_info[INSN_UID (insn)].luid;
+
+ ainsn = last_def[regno];
+ while (1)
+ {
+ abb = BLOCK_FOR_INSN (ainsn);
+
+ if (dominated_by_p (CDI_DOMINATORS, bb, abb))
+ break;
+
+ auid = INSN_UID (ainsn);
+ ainsn = insn_info[auid].prev_def;
+
+ if (!ainsn)
+ return NULL_RTX;
+ }
+
+ while (1)
+ {
+ abb = BLOCK_FOR_INSN (ainsn);
+ if (abb != bb)
+ return ainsn;
+
+ auid = INSN_UID (ainsn);
+ if (luid > insn_info[auid].luid)
+ return ainsn;
+
+ ainsn = insn_info[auid].prev_def;
+ if (!ainsn)
+ return NULL_RTX;
+ }
+}
+
+/* Sets IV to invariant CST in MODE. Always returns true (just for
+ consistency with other iv manipulation functions that may fail). */
+
+static bool
+iv_constant (struct rtx_iv *iv, rtx cst, enum machine_mode mode)
+{
+ if (mode == VOIDmode)
+ mode = GET_MODE (cst);
+
+ iv->analysed = true;
+ iv->mode = mode;
+ iv->base = cst;
+ iv->step = const0_rtx;
+ iv->first_special = false;
+ iv->extend = NIL;
+ iv->extend_mode = iv->mode;
+ iv->delta = const0_rtx;
+ iv->mult = const1_rtx;
+
+ return true;
+}
+
+/* Evaluates application of subreg to MODE on IV. */
+
+static bool
+iv_subreg (struct rtx_iv *iv, enum machine_mode mode)
+{
+ if (iv->extend_mode == mode)
+ return true;
+
+ if (GET_MODE_BITSIZE (mode) > GET_MODE_BITSIZE (iv->mode))
+ return false;
+
+ iv->extend = NIL;
+ iv->mode = mode;
+
+ iv->base = simplify_gen_binary (PLUS, iv->extend_mode, iv->delta,
+ simplify_gen_binary (MULT, iv->extend_mode,
+ iv->base, iv->mult));
+ iv->step = simplify_gen_binary (MULT, iv->extend_mode, iv->step, iv->mult);
+ iv->mult = const1_rtx;
+ iv->delta = const0_rtx;
+ iv->first_special = false;
+
+ return true;
+}
+
+/* Evaluates application of EXTEND to MODE on IV. */
+
+static bool
+iv_extend (struct rtx_iv *iv, enum rtx_code extend, enum machine_mode mode)
+{
+ if (mode != iv->extend_mode)
+ return false;
+
+ if (iv->extend != NIL
+ && iv->extend != extend)
+ return false;
+
+ iv->extend = extend;
+
+ return true;
+}
+
+/* Evaluates negation of IV. */
+
+static bool
+iv_neg (struct rtx_iv *iv)
+{
+ if (iv->extend == NIL)
+ {
+ iv->base = simplify_gen_unary (NEG, iv->extend_mode,
+ iv->base, iv->extend_mode);
+ iv->step = simplify_gen_unary (NEG, iv->extend_mode,
+ iv->step, iv->extend_mode);
+ }
+ else
+ {
+ iv->delta = simplify_gen_unary (NEG, iv->extend_mode,
+ iv->delta, iv->extend_mode);
+ iv->mult = simplify_gen_unary (NEG, iv->extend_mode,
+ iv->mult, iv->extend_mode);
+ }
+
+ return true;
+}
+
+/* Evaluates addition or subtraction (according to OP) of IV1 to IV0. */
+
+static bool
+iv_add (struct rtx_iv *iv0, struct rtx_iv *iv1, enum rtx_code op)
+{
+ enum machine_mode mode;
+ rtx arg;
+
+ /* Extend the constant to extend_mode of the other operand if necessary. */
+ if (iv0->extend == NIL
+ && iv0->mode == iv0->extend_mode
+ && iv0->step == const0_rtx
+ && GET_MODE_SIZE (iv0->extend_mode) < GET_MODE_SIZE (iv1->extend_mode))
+ {
+ iv0->extend_mode = iv1->extend_mode;
+ iv0->base = simplify_gen_unary (ZERO_EXTEND, iv0->extend_mode,
+ iv0->base, iv0->mode);
+ }
+ if (iv1->extend == NIL
+ && iv1->mode == iv1->extend_mode
+ && iv1->step == const0_rtx
+ && GET_MODE_SIZE (iv1->extend_mode) < GET_MODE_SIZE (iv0->extend_mode))
+ {
+ iv1->extend_mode = iv0->extend_mode;
+ iv1->base = simplify_gen_unary (ZERO_EXTEND, iv1->extend_mode,
+ iv1->base, iv1->mode);
+ }
+
+ mode = iv0->extend_mode;
+ if (mode != iv1->extend_mode)
+ return false;
+
+ if (iv0->extend == NIL && iv1->extend == NIL)
+ {
+ if (iv0->mode != iv1->mode)
+ return false;
+
+ iv0->base = simplify_gen_binary (op, mode, iv0->base, iv1->base);
+ iv0->step = simplify_gen_binary (op, mode, iv0->step, iv1->step);
+
+ return true;
+ }
+
+ /* Handle addition of constant. */
+ if (iv1->extend == NIL
+ && iv1->mode == mode
+ && iv1->step == const0_rtx)
+ {
+ iv0->delta = simplify_gen_binary (op, mode, iv0->delta, iv1->base);
+ return true;
+ }
+
+ if (iv0->extend == NIL
+ && iv0->mode == mode
+ && iv0->step == const0_rtx)
+ {
+ arg = iv0->base;
+ *iv0 = *iv1;
+ if (op == MINUS
+ && !iv_neg (iv0))
+ return false;
+
+ iv0->delta = simplify_gen_binary (PLUS, mode, iv0->delta, arg);
+ return true;
+ }
+
+ return false;
+}
+
+/* Evaluates multiplication of IV by constant CST. */
+
+static bool
+iv_mult (struct rtx_iv *iv, rtx mby)
+{
+ enum machine_mode mode = iv->extend_mode;
+
+ if (GET_MODE (mby) != VOIDmode
+ && GET_MODE (mby) != mode)
+ return false;
+
+ if (iv->extend == NIL)
+ {
+ iv->base = simplify_gen_binary (MULT, mode, iv->base, mby);
+ iv->step = simplify_gen_binary (MULT, mode, iv->step, mby);
+ }
+ else
+ {
+ iv->delta = simplify_gen_binary (MULT, mode, iv->delta, mby);
+ iv->mult = simplify_gen_binary (MULT, mode, iv->mult, mby);
+ }
+
+ return true;
+}
+
+/* The recursive part of get_biv_step. Gets the value of the single value
+ defined in INSN wrto initial value of REG inside loop, in shape described
+ at get_biv_step. */
+
+static bool
+get_biv_step_1 (rtx insn, rtx reg,
+ rtx *inner_step, enum machine_mode *inner_mode,
+ enum rtx_code *extend, enum machine_mode outer_mode,
+ rtx *outer_step)
+{
+ rtx set, lhs, rhs, op0 = NULL_RTX, op1 = NULL_RTX;
+ rtx next, nextr, def_insn, tmp;
+ enum rtx_code code;
+
+ set = single_set (insn);
+ rhs = find_reg_equal_equiv_note (insn);
+ if (!rhs)
+ rhs = SET_SRC (set);
+ lhs = SET_DEST (set);
+
+ code = GET_CODE (rhs);
+ switch (code)
+ {
+ case SUBREG:
+ case REG:
+ next = rhs;
+ break;
+
+ case PLUS:
+ case MINUS:
+ op0 = XEXP (rhs, 0);
+ op1 = XEXP (rhs, 1);
+
+ if (code == PLUS && CONSTANT_P (op0))
+ {
+ tmp = op0; op0 = op1; op1 = tmp;
+ }
+
+ if (!simple_reg_p (op0)
+ || !CONSTANT_P (op1))
+ return false;
+
+ if (GET_MODE (rhs) != outer_mode)
+ {
+ /* ppc64 uses expressions like
+
+ (set x:SI (plus:SI (subreg:SI y:DI) 1)).
+
+ this is equivalent to
+
+ (set x':DI (plus:DI y:DI 1))
+ (set x:SI (subreg:SI (x':DI)). */
+ if (GET_CODE (op0) != SUBREG)
+ return false;
+ if (GET_MODE (SUBREG_REG (op0)) != outer_mode)
+ return false;
+ }
+
+ next = op0;
+ break;
+
+ case SIGN_EXTEND:
+ case ZERO_EXTEND:
+ if (GET_MODE (rhs) != outer_mode)
+ return false;
+
+ op0 = XEXP (rhs, 0);
+ if (!simple_reg_p (op0))
+ return false;
+
+ next = op0;
+ break;
+
+ default:
+ return false;
+ }
+
+ if (GET_CODE (next) == SUBREG)
+ {
+ if (!subreg_lowpart_p (next))
+ return false;
+
+ nextr = SUBREG_REG (next);
+ if (GET_MODE (nextr) != outer_mode)
+ return false;
+ }
+ else
+ nextr = next;
+
+ def_insn = iv_get_reaching_def (insn, nextr);
+ if (def_insn == const0_rtx)
+ return false;
+
+ if (!def_insn)
+ {
+ if (!rtx_equal_p (nextr, reg))
+ return false;
+
+ *inner_step = const0_rtx;
+ *extend = NIL;
+ *inner_mode = outer_mode;
+ *outer_step = const0_rtx;
+ }
+ else if (!get_biv_step_1 (def_insn, reg,
+ inner_step, inner_mode, extend, outer_mode,
+ outer_step))
+ return false;
+
+ if (GET_CODE (next) == SUBREG)
+ {
+ enum machine_mode amode = GET_MODE (next);
+
+ if (GET_MODE_SIZE (amode) > GET_MODE_SIZE (*inner_mode))
+ return false;
+
+ *inner_mode = amode;
+ *inner_step = simplify_gen_binary (PLUS, outer_mode,
+ *inner_step, *outer_step);
+ *outer_step = const0_rtx;
+ *extend = NIL;
+ }
+
+ switch (code)
+ {
+ case REG:
+ case SUBREG:
+ break;
+
+ case PLUS:
+ case MINUS:
+ if (*inner_mode == outer_mode
+ /* See comment in previous switch. */
+ || GET_MODE (rhs) != outer_mode)
+ *inner_step = simplify_gen_binary (code, outer_mode,
+ *inner_step, op1);
+ else
+ *outer_step = simplify_gen_binary (code, outer_mode,
+ *outer_step, op1);
+ break;
+
+ case SIGN_EXTEND:
+ case ZERO_EXTEND:
+ if (GET_MODE (op0) != *inner_mode
+ || *extend != NIL
+ || *outer_step != const0_rtx)
+ abort ();
+
+ *extend = code;
+ break;
+
+ default:
+ abort ();
+ }
+
+ return true;
+}
+
+/* Gets the operation on register REG inside loop, in shape
+
+ OUTER_STEP + EXTEND_{OUTER_MODE} (SUBREG_{INNER_MODE} (REG + INNER_STEP))
+
+ If the operation cannot be described in this shape, return false. */
+
+static bool
+get_biv_step (rtx reg, rtx *inner_step, enum machine_mode *inner_mode,
+ enum rtx_code *extend, enum machine_mode *outer_mode,
+ rtx *outer_step)
+{
+ *outer_mode = GET_MODE (reg);
+
+ if (!get_biv_step_1 (last_def[REGNO (reg)], reg,
+ inner_step, inner_mode, extend, *outer_mode,
+ outer_step))
+ return false;
+
+ if (*inner_mode != *outer_mode
+ && *extend == NIL)
+ abort ();
+
+ if (*inner_mode == *outer_mode
+ && *extend != NIL)
+ abort ();
+
+ if (*inner_mode == *outer_mode
+ && *outer_step != const0_rtx)
+ abort ();
+
+ return true;
+}
+
+/* Determines whether DEF is a biv and if so, stores its description
+ to *IV. */
+
+static bool
+iv_analyze_biv (rtx def, struct rtx_iv *iv)
+{
+ unsigned regno;
+ rtx inner_step, outer_step;
+ enum machine_mode inner_mode, outer_mode;
+ enum rtx_code extend;
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "Analysing ");
+ print_rtl (dump_file, def);
+ fprintf (dump_file, " for bivness.\n");
+ }
+
+ if (!REG_P (def))
+ {
+ if (!CONSTANT_P (def))
+ return false;
+
+ return iv_constant (iv, def, VOIDmode);
+ }
+
+ regno = REGNO (def);
+ if (last_def[regno] == const0_rtx)
+ {
+ if (dump_file)
+ fprintf (dump_file, " not simple.\n");
+ return false;
+ }
+
+ if (last_def[regno] && bivs[regno].analysed)
+ {
+ if (dump_file)
+ fprintf (dump_file, " already analysed.\n");
+
+ *iv = bivs[regno];
+ return iv->base != NULL_RTX;
+ }
+
+ if (!last_def[regno])
+ {
+ iv_constant (iv, def, VOIDmode);
+ goto end;
+ }
+
+ iv->analysed = true;
+ if (!get_biv_step (def, &inner_step, &inner_mode, &extend,
+ &outer_mode, &outer_step))
+ {
+ iv->base = NULL_RTX;
+ goto end;
+ }
+
+ /* Loop transforms base to es (base + inner_step) + outer_step,
+ where es means extend of subreg between inner_mode and outer_mode.
+ The corresponding induction variable is
+
+ es ((base - outer_step) + i * (inner_step + outer_step)) + outer_step */
+
+ iv->base = simplify_gen_binary (MINUS, outer_mode, def, outer_step);
+ iv->step = simplify_gen_binary (PLUS, outer_mode, inner_step, outer_step);
+ iv->mode = inner_mode;
+ iv->extend_mode = outer_mode;
+ iv->extend = extend;
+ iv->mult = const1_rtx;
+ iv->delta = outer_step;
+ iv->first_special = inner_mode != outer_mode;
+
+ end:
+ if (dump_file)
+ {
+ fprintf (dump_file, " ");
+ dump_iv_info (dump_file, iv);
+ fprintf (dump_file, "\n");
+ }
+
+ bivs[regno] = *iv;
+
+ return iv->base != NULL_RTX;
+}
+
+/* Analyzes operand OP of INSN and stores the result to *IV. */
+
+static bool
+iv_analyze_op (rtx insn, rtx op, struct rtx_iv *iv)
+{
+ rtx def_insn;
+ unsigned regno;
+ bool inv = CONSTANT_P (op);
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "Analysing operand ");
+ print_rtl (dump_file, op);
+ fprintf (dump_file, " of insn ");
+ print_rtl_single (dump_file, insn);
+ }
+
+ if (GET_CODE (op) == SUBREG)
+ {
+ if (!subreg_lowpart_p (op))
+ return false;
+
+ if (!iv_analyze_op (insn, SUBREG_REG (op), iv))
+ return false;
+
+ return iv_subreg (iv, GET_MODE (op));
+ }
+
+ if (!inv)
+ {
+ regno = REGNO (op);
+ if (!last_def[regno])
+ inv = true;
+ else if (last_def[regno] == const0_rtx)
+ {
+ if (dump_file)
+ fprintf (dump_file, " not simple.\n");
+ return false;
+ }
+ }
+
+ if (inv)
+ {
+ iv_constant (iv, op, VOIDmode);
+
+ if (dump_file)
+ {
+ fprintf (dump_file, " ");
+ dump_iv_info (dump_file, iv);
+ fprintf (dump_file, "\n");
+ }
+ return true;
+ }
+
+ def_insn = iv_get_reaching_def (insn, op);
+ if (def_insn == const0_rtx)
+ {
+ if (dump_file)
+ fprintf (dump_file, " not simple.\n");
+ return false;
+ }
+
+ return iv_analyze (def_insn, op, iv);
+}
+
+/* Analyzes iv DEF defined in INSN and stores the result to *IV. */
+
+bool
+iv_analyze (rtx insn, rtx def, struct rtx_iv *iv)
+{
+ unsigned uid;
+ rtx set, rhs, mby = NULL_RTX, tmp;
+ rtx op0 = NULL_RTX, op1 = NULL_RTX;
+ struct rtx_iv iv0, iv1;
+ enum machine_mode amode;
+ enum rtx_code code;
+
+ if (insn == const0_rtx)
+ return false;
+
+ if (GET_CODE (def) == SUBREG)
+ {
+ if (!subreg_lowpart_p (def))
+ return false;
+
+ if (!iv_analyze (insn, SUBREG_REG (def), iv))
+ return false;
+
+ return iv_subreg (iv, GET_MODE (def));
+ }
+
+ if (!insn)
+ return iv_analyze_biv (def, iv);
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "Analysing def of ");
+ print_rtl (dump_file, def);
+ fprintf (dump_file, " in insn ");
+ print_rtl_single (dump_file, insn);
+ }
+
+ uid = INSN_UID (insn);
+ if (insn_info[uid].iv.analysed)
+ {
+ if (dump_file)
+ fprintf (dump_file, " already analysed.\n");
+ *iv = insn_info[uid].iv;
+ return iv->base != NULL_RTX;
+ }
+
+ iv->mode = VOIDmode;
+ iv->base = NULL_RTX;
+ iv->step = NULL_RTX;
+
+ set = single_set (insn);
+ rhs = find_reg_equal_equiv_note (insn);
+ if (!rhs)
+ rhs = SET_SRC (set);
+ code = GET_CODE (rhs);
+
+ if (CONSTANT_P (rhs))
+ {
+ op0 = rhs;
+ amode = GET_MODE (def);
+ }
+ else
+ {
+ switch (code)
+ {
+ case SUBREG:
+ if (!subreg_lowpart_p (rhs))
+ goto end;
+ op0 = rhs;
+ break;
+
+ case REG:
+ op0 = rhs;
+ break;
+
+ case SIGN_EXTEND:
+ case ZERO_EXTEND:
+ case NEG:
+ op0 = XEXP (rhs, 0);
+ break;
+
+ case PLUS:
+ case MINUS:
+ op0 = XEXP (rhs, 0);
+ op1 = XEXP (rhs, 1);
+ break;
+
+ case MULT:
+ op0 = XEXP (rhs, 0);
+ mby = XEXP (rhs, 1);
+ if (!CONSTANT_P (mby))
+ {
+ if (!CONSTANT_P (op0))
+ abort ();
+ tmp = op0;
+ op0 = mby;
+ mby = tmp;
+ }
+ break;
+
+ default:
+ abort ();
+ }
+
+ amode = GET_MODE (rhs);
+ }
+
+ if (op0)
+ {
+ if (!iv_analyze_op (insn, op0, &iv0))
+ goto end;
+
+ if (iv0.mode == VOIDmode)
+ {
+ iv0.mode = amode;
+ iv0.extend_mode = amode;
+ }
+ }
+
+ if (op1)
+ {
+ if (!iv_analyze_op (insn, op1, &iv1))
+ goto end;
+
+ if (iv1.mode == VOIDmode)
+ {
+ iv1.mode = amode;
+ iv1.extend_mode = amode;
+ }
+ }
+
+ switch (code)
+ {
+ case SIGN_EXTEND:
+ case ZERO_EXTEND:
+ if (!iv_extend (&iv0, code, amode))
+ goto end;
+ break;
+
+ case NEG:
+ if (!iv_neg (&iv0))
+ goto end;
+ break;
+
+ case PLUS:
+ case MINUS:
+ if (!iv_add (&iv0, &iv1, code))
+ goto end;
+ break;
+
+ case MULT:
+ if (!iv_mult (&iv0, mby))
+ goto end;
+ break;
+
+ default:
+ break;
+ }
+
+ *iv = iv0;
+
+ end:
+ iv->analysed = true;
+ insn_info[uid].iv = *iv;
+
+ if (dump_file)
+ {
+ print_rtl (dump_file, def);
+ fprintf (dump_file, " in insn ");
+ print_rtl_single (dump_file, insn);
+ fprintf (dump_file, " is ");
+ dump_iv_info (dump_file, iv);
+ fprintf (dump_file, "\n");
+ }
+
+ return iv->base != NULL_RTX;
+}
+
+/* Calculates value of IV at ITERATION-th iteration. */
+
+rtx
+get_iv_value (struct rtx_iv *iv, rtx iteration)
+{
+ rtx val;
+
+ /* We would need to generate some if_then_else patterns, and so far
+ it is not needed anywhere. */
+ if (iv->first_special)
+ abort ();
+
+ if (iv->step != const0_rtx && iteration != const0_rtx)
+ val = simplify_gen_binary (PLUS, iv->extend_mode, iv->base,
+ simplify_gen_binary (MULT, iv->extend_mode,
+ iv->step, iteration));
+ else
+ val = iv->base;
+
+ if (iv->extend_mode == iv->mode)
+ return val;
+
+ val = lowpart_subreg (iv->mode, val, iv->extend_mode);
+
+ if (iv->extend == NIL)
+ return val;
+
+ val = simplify_gen_unary (iv->extend, iv->extend_mode, val, iv->mode);
+ val = simplify_gen_binary (PLUS, iv->extend_mode, iv->delta,
+ simplify_gen_binary (MULT, iv->extend_mode,
+ iv->mult, val));
+
+ return val;
+}
+
+/* Free the data for an induction variable analysis. */
+
+void
+iv_analysis_done (void)
+{
+ max_insn_no = 0;
+ max_reg_no = 0;
+ if (insn_info)
+ {
+ free (insn_info);
+ insn_info = NULL;
+ }
+ if (last_def)
+ {
+ free (last_def);
+ last_def = NULL;
+ }
+ if (bivs)
+ {
+ free (bivs);
+ bivs = NULL;
+ }
+}
+
+/* Computes inverse to X modulo (1 << MOD). */
+
+static unsigned HOST_WIDEST_INT
+inverse (unsigned HOST_WIDEST_INT x, int mod)
+{
+ unsigned HOST_WIDEST_INT mask =
+ ((unsigned HOST_WIDEST_INT) 1 << (mod - 1) << 1) - 1;
+ unsigned HOST_WIDEST_INT rslt = 1;
+ int i;
+
+ for (i = 0; i < mod - 1; i++)
+ {
+ rslt = (rslt * x) & mask;
+ x = (x * x) & mask;
+ }
+
+ return rslt;
+}
+
+/* Tries to estimate the maximum number of iterations. */
+
+static unsigned HOST_WIDEST_INT
+determine_max_iter (struct niter_desc *desc)
+{
+ rtx niter = desc->niter_expr;
+ rtx mmin, mmax, left, right;
+ unsigned HOST_WIDEST_INT nmax, inc;
+
+ if (GET_CODE (niter) == AND
+ && GET_CODE (XEXP (niter, 0)) == CONST_INT)
+ {
+ nmax = INTVAL (XEXP (niter, 0));
+ if (!(nmax & (nmax + 1)))
+ {
+ desc->niter_max = nmax;
+ return nmax;
+ }
+ }
+
+ get_mode_bounds (desc->mode, desc->signed_p, &mmin, &mmax);
+ nmax = INTVAL (mmax) - INTVAL (mmin);
+
+ if (GET_CODE (niter) == UDIV)
+ {
+ if (GET_CODE (XEXP (niter, 1)) != CONST_INT)
+ {
+ desc->niter_max = nmax;
+ return nmax;
+ }
+ inc = INTVAL (XEXP (niter, 1));
+ niter = XEXP (niter, 0);
+ }
+ else
+ inc = 1;
+
+ if (GET_CODE (niter) == PLUS)
+ {
+ left = XEXP (niter, 0);
+ right = XEXP (niter, 0);
+
+ if (GET_CODE (right) == CONST_INT)
+ right = GEN_INT (-INTVAL (right));
+ }
+ else if (GET_CODE (niter) == MINUS)
+ {
+ left = XEXP (niter, 0);
+ right = XEXP (niter, 0);
+ }
+ else
+ {
+ left = niter;
+ right = mmin;
+ }
+
+ if (GET_CODE (left) == CONST_INT)
+ mmax = left;
+ if (GET_CODE (right) == CONST_INT)
+ mmin = right;
+ nmax = INTVAL (mmax) - INTVAL (mmin);
+
+ desc->niter_max = nmax / inc;
+ return nmax / inc;
+}
+
+/* Checks whether register *REG is in set ALT. Callback for for_each_rtx. */
+
+static int
+altered_reg_used (rtx *reg, void *alt)
+{
+ if (!REG_P (*reg))
+ return 0;
+
+ return REGNO_REG_SET_P (alt, REGNO (*reg));
+}
+
+/* Marks registers altered by EXPR in set ALT. */
+
+static void
+mark_altered (rtx expr, rtx by ATTRIBUTE_UNUSED, void *alt)
+{
+ if (GET_CODE (expr) == SUBREG)
+ expr = SUBREG_REG (expr);
+ if (!REG_P (expr))
+ return;
+
+ SET_REGNO_REG_SET (alt, REGNO (expr));
+}
+
+/* Checks whether RHS is simple enough to process. */
+
+static bool
+simple_rhs_p (rtx rhs)
+{
+ rtx op0, op1;
+
+ if (CONSTANT_P (rhs)
+ || REG_P (rhs))
+ return true;
+
+ switch (GET_CODE (rhs))
+ {
+ case PLUS:
+ case MINUS:
+ op0 = XEXP (rhs, 0);
+ op1 = XEXP (rhs, 1);
+ /* Allow reg + const sets only. */
+ if (REG_P (op0) && CONSTANT_P (op1))
+ return true;
+ if (REG_P (op1) && CONSTANT_P (op0))
+ return true;
+
+ return false;
+
+ default:
+ return false;
+ }
+}
+
+/* Simplifies *EXPR using assignment in INSN. ALTERED is the set of registers
+ altered so far. */
+
+static void
+simplify_using_assignment (rtx insn, rtx *expr, regset altered)
+{
+ rtx set = single_set (insn);
+ rtx lhs, rhs;
+ bool ret = false;
+
+ if (set)
+ {
+ lhs = SET_DEST (set);
+ if (GET_CODE (lhs) != REG
+ || altered_reg_used (&lhs, altered))
+ ret = true;
+ }
+ else
+ ret = true;
+
+ note_stores (PATTERN (insn), mark_altered, altered);
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ int i;
+
+ /* Kill all call clobbered registers. */
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i))
+ SET_REGNO_REG_SET (altered, i);
+ }
+
+ if (ret)
+ return;
+
+ rhs = find_reg_equal_equiv_note (insn);
+ if (!rhs)
+ rhs = SET_SRC (set);
+
+ if (!simple_rhs_p (rhs))
+ return;
+
+ if (for_each_rtx (&rhs, altered_reg_used, altered))
+ return;
+
+ *expr = simplify_replace_rtx (*expr, lhs, rhs);
+}
+
+/* Checks whether A implies B. */
+
+static bool
+implies_p (rtx a, rtx b)
+{
+ rtx op0, op1, r;
+
+ if (GET_CODE (a) == EQ)
+ {
+ op0 = XEXP (a, 0);
+ op1 = XEXP (a, 1);
+
+ if (REG_P (op0))
+ {
+ r = simplify_replace_rtx (b, op0, op1);
+ if (r == const_true_rtx)
+ return true;
+ }
+
+ if (REG_P (op1))
+ {
+ r = simplify_replace_rtx (b, op1, op0);
+ if (r == const_true_rtx)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Canonicalizes COND so that
+
+ (1) Ensure that operands are ordered according to
+ swap_commutative_operands_p.
+ (2) (LE x const) will be replaced with (LT x <const+1>) and similarly
+ for GE, GEU, and LEU. */
+
+rtx
+canon_condition (rtx cond)
+{
+ rtx tem;
+ rtx op0, op1;
+ enum rtx_code code;
+ enum machine_mode mode;
+
+ code = GET_CODE (cond);
+ op0 = XEXP (cond, 0);
+ op1 = XEXP (cond, 1);
+
+ if (swap_commutative_operands_p (op0, op1))
+ {
+ code = swap_condition (code);
+ tem = op0;
+ op0 = op1;
+ op1 = tem;
+ }
+
+ mode = GET_MODE (op0);
+ if (mode == VOIDmode)
+ mode = GET_MODE (op1);
+ if (mode == VOIDmode)
+ abort ();
+
+ if (GET_CODE (op1) == CONST_INT
+ && GET_MODE_CLASS (mode) != MODE_CC
+ && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
+ {
+ HOST_WIDE_INT const_val = INTVAL (op1);
+ unsigned HOST_WIDE_INT uconst_val = const_val;
+ unsigned HOST_WIDE_INT max_val
+ = (unsigned HOST_WIDE_INT) GET_MODE_MASK (mode);
+
+ switch (code)
+ {
+ case LE:
+ if ((unsigned HOST_WIDE_INT) const_val != max_val >> 1)
+ code = LT, op1 = gen_int_mode (const_val + 1, GET_MODE (op0));
+ break;
+
+ /* When cross-compiling, const_val might be sign-extended from
+ BITS_PER_WORD to HOST_BITS_PER_WIDE_INT */
+ case GE:
+ if ((HOST_WIDE_INT) (const_val & max_val)
+ != (((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (GET_MODE (op0)) - 1))))
+ code = GT, op1 = gen_int_mode (const_val - 1, mode);
+ break;
+
+ case LEU:
+ if (uconst_val < max_val)
+ code = LTU, op1 = gen_int_mode (uconst_val + 1, mode);
+ break;
+
+ case GEU:
+ if (uconst_val != 0)
+ code = GTU, op1 = gen_int_mode (uconst_val - 1, mode);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (op0 != XEXP (cond, 0)
+ || op1 != XEXP (cond, 1)
+ || code != GET_CODE (cond)
+ || GET_MODE (cond) != SImode)
+ cond = gen_rtx_fmt_ee (code, SImode, op0, op1);
+
+ return cond;
+}
+
+/* Tries to use the fact that COND holds to simplify EXPR. ALTERED is the
+ set of altered regs. */
+
+void
+simplify_using_condition (rtx cond, rtx *expr, regset altered)
+{
+ rtx rev, reve, exp = *expr;
+
+ if (GET_RTX_CLASS (GET_CODE (*expr)) != '<')
+ return;
+
+ /* If some register gets altered later, we do not really speak about its
+ value at the time of comparison. */
+ if (altered
+ && for_each_rtx (&cond, altered_reg_used, altered))
+ return;
+
+ rev = reversed_condition (cond);
+ reve = reversed_condition (exp);
+
+ cond = canon_condition (cond);
+ exp = canon_condition (exp);
+ if (rev)
+ rev = canon_condition (rev);
+ if (reve)
+ reve = canon_condition (reve);
+
+ if (rtx_equal_p (exp, cond))
+ {
+ *expr = const_true_rtx;
+ return;
+ }
+
+
+ if (rev && rtx_equal_p (exp, rev))
+ {
+ *expr = const0_rtx;
+ return;
+ }
+
+ if (implies_p (cond, exp))
+ {
+ *expr = const_true_rtx;
+ return;
+ }
+
+ if (reve && implies_p (cond, reve))
+ {
+ *expr = const0_rtx;
+ return;
+ }
+
+ /* A proof by contradiction. If *EXPR implies (not cond), *EXPR must
+ be false. */
+ if (rev && implies_p (exp, rev))
+ {
+ *expr = const0_rtx;
+ return;
+ }
+
+ /* Similarly, If (not *EXPR) implies (not cond), *EXPR must be true. */
+ if (rev && reve && implies_p (reve, rev))
+ {
+ *expr = const_true_rtx;
+ return;
+ }
+
+ /* We would like to have some other tests here. TODO. */
+
+ return;
+}
+
+/* Use relationship between A and *B to eventually eliminate *B.
+ OP is the operation we consider. */
+
+static void
+eliminate_implied_condition (enum rtx_code op, rtx a, rtx *b)
+{
+ if (op == AND)
+ {
+ /* If A implies *B, we may replace *B by true. */
+ if (implies_p (a, *b))
+ *b = const_true_rtx;
+ }
+ else if (op == IOR)
+ {
+ /* If *B implies A, we may replace *B by false. */
+ if (implies_p (*b, a))
+ *b = const0_rtx;
+ }
+ else
+ abort ();
+}
+
+/* Eliminates the conditions in TAIL that are implied by HEAD. OP is the
+ operation we consider. */
+
+static void
+eliminate_implied_conditions (enum rtx_code op, rtx *head, rtx tail)
+{
+ rtx elt;
+
+ for (elt = tail; elt; elt = XEXP (elt, 1))
+ eliminate_implied_condition (op, *head, &XEXP (elt, 0));
+ for (elt = tail; elt; elt = XEXP (elt, 1))
+ eliminate_implied_condition (op, XEXP (elt, 0), head);
+}
+
+/* Simplifies *EXPR using initial values at the start of the LOOP. If *EXPR
+ is a list, its elements are assumed to be combined using OP. */
+
+static void
+simplify_using_initial_values (struct loop *loop, enum rtx_code op, rtx *expr)
+{
+ rtx head, tail, insn;
+ rtx neutral, aggr;
+ regset altered;
+ regset_head altered_head;
+ edge e;
+
+ if (!*expr)
+ return;
+
+ if (CONSTANT_P (*expr))
+ return;
+
+ if (GET_CODE (*expr) == EXPR_LIST)
+ {
+ head = XEXP (*expr, 0);
+ tail = XEXP (*expr, 1);
+
+ eliminate_implied_conditions (op, &head, tail);
+
+ if (op == AND)
+ {
+ neutral = const_true_rtx;
+ aggr = const0_rtx;
+ }
+ else if (op == IOR)
+ {
+ neutral = const0_rtx;
+ aggr = const_true_rtx;
+ }
+ else
+ abort ();
+
+ simplify_using_initial_values (loop, NIL, &head);
+ if (head == aggr)
+ {
+ XEXP (*expr, 0) = aggr;
+ XEXP (*expr, 1) = NULL_RTX;
+ return;
+ }
+ else if (head == neutral)
+ {
+ *expr = tail;
+ simplify_using_initial_values (loop, op, expr);
+ return;
+ }
+ simplify_using_initial_values (loop, op, &tail);
+
+ if (tail && XEXP (tail, 0) == aggr)
+ {
+ *expr = tail;
+ return;
+ }
+
+ XEXP (*expr, 0) = head;
+ XEXP (*expr, 1) = tail;
+ return;
+ }
+
+ if (op != NIL)
+ abort ();
+
+ e = loop_preheader_edge (loop);
+ if (e->src == ENTRY_BLOCK_PTR)
+ return;
+
+ altered = INITIALIZE_REG_SET (altered_head);
+
+ while (1)
+ {
+ insn = BB_END (e->src);
+ if (any_condjump_p (insn))
+ {
+ /* FIXME -- slightly wrong -- what if compared register
+ gets altered between start of the condition and insn? */
+ rtx cond = get_condition (BB_END (e->src), NULL, false);
+
+ if (cond && (e->flags & EDGE_FALLTHRU))
+ cond = reversed_condition (cond);
+ if (cond)
+ {
+ simplify_using_condition (cond, expr, altered);
+ if (CONSTANT_P (*expr))
+ {
+ FREE_REG_SET (altered);
+ return;
+ }
+ }
+ }
+
+ FOR_BB_INSNS_REVERSE (e->src, insn)
+ {
+ if (!INSN_P (insn))
+ continue;
+
+ simplify_using_assignment (insn, expr, altered);
+ if (CONSTANT_P (*expr))
+ {
+ FREE_REG_SET (altered);
+ return;
+ }
+ }
+
+ e = e->src->pred;
+ if (e->pred_next
+ || e->src == ENTRY_BLOCK_PTR)
+ break;
+ }
+
+ FREE_REG_SET (altered);
+}
+
+/* Transforms invariant IV into MODE. Adds assumptions based on the fact
+ that IV occurs as left operands of comparison COND and its signedness
+ is SIGNED_P to DESC. */
+
+static void
+shorten_into_mode (struct rtx_iv *iv, enum machine_mode mode,
+ enum rtx_code cond, bool signed_p, struct niter_desc *desc)
+{
+ rtx mmin, mmax, cond_over, cond_under;
+
+ get_mode_bounds (mode, signed_p, &mmin, &mmax);
+ cond_under = simplify_gen_relational (LT, SImode, iv->extend_mode,
+ iv->base, mmin);
+ cond_over = simplify_gen_relational (GT, SImode, iv->extend_mode,
+ iv->base, mmax);
+
+ switch (cond)
+ {
+ case LE:
+ case LT:
+ case LEU:
+ case LTU:
+ if (cond_under != const0_rtx)
+ desc->infinite =
+ alloc_EXPR_LIST (0, cond_under, desc->infinite);
+ if (cond_over != const0_rtx)
+ desc->noloop_assumptions =
+ alloc_EXPR_LIST (0, cond_over, desc->noloop_assumptions);
+ break;
+
+ case GE:
+ case GT:
+ case GEU:
+ case GTU:
+ if (cond_over != const0_rtx)
+ desc->infinite =
+ alloc_EXPR_LIST (0, cond_over, desc->infinite);
+ if (cond_under != const0_rtx)
+ desc->noloop_assumptions =
+ alloc_EXPR_LIST (0, cond_under, desc->noloop_assumptions);
+ break;
+
+ case NE:
+ if (cond_over != const0_rtx)
+ desc->infinite =
+ alloc_EXPR_LIST (0, cond_over, desc->infinite);
+ if (cond_under != const0_rtx)
+ desc->infinite =
+ alloc_EXPR_LIST (0, cond_under, desc->infinite);
+ break;
+
+ default:
+ abort ();
+ }
+
+ iv->mode = mode;
+ iv->extend = signed_p ? SIGN_EXTEND : ZERO_EXTEND;
+}
+
+/* Transforms IV0 and IV1 compared by COND so that they are both compared as
+ subregs of the same mode if possible (sometimes it is necessary to add
+ some assumptions to DESC). */
+
+static bool
+canonicalize_iv_subregs (struct rtx_iv *iv0, struct rtx_iv *iv1,
+ enum rtx_code cond, struct niter_desc *desc)
+{
+ enum machine_mode comp_mode;
+ bool signed_p;
+
+ /* If the ivs behave specially in the first iteration, or are
+ added/multiplied after extending, we ignore them. */
+ if (iv0->first_special || iv0->mult != const1_rtx || iv0->delta != const0_rtx)
+ return false;
+ if (iv1->first_special || iv1->mult != const1_rtx || iv1->delta != const0_rtx)
+ return false;
+
+ /* If there is some extend, it must match signedness of the comparison. */
+ switch (cond)
+ {
+ case LE:
+ case LT:
+ if (iv0->extend == ZERO_EXTEND
+ || iv1->extend == ZERO_EXTEND)
+ return false;
+ signed_p = true;
+ break;
+
+ case LEU:
+ case LTU:
+ if (iv0->extend == SIGN_EXTEND
+ || iv1->extend == SIGN_EXTEND)
+ return false;
+ signed_p = false;
+ break;
+
+ case NE:
+ if (iv0->extend != NIL
+ && iv1->extend != NIL
+ && iv0->extend != iv1->extend)
+ return false;
+
+ signed_p = false;
+ if (iv0->extend != NIL)
+ signed_p = iv0->extend == SIGN_EXTEND;
+ if (iv1->extend != NIL)
+ signed_p = iv1->extend == SIGN_EXTEND;
+ break;
+
+ default:
+ abort ();
+ }
+
+ /* Values of both variables should be computed in the same mode. These
+ might indeed be different, if we have comparison like
+
+ (compare (subreg:SI (iv0)) (subreg:SI (iv1)))
+
+ and iv0 and iv1 are both ivs iterating in SI mode, but calculated
+ in different modes. This does not seem impossible to handle, but
+ it hardly ever occurs in practice.
+
+ The only exception is the case when one of operands is invariant.
+ For example pentium 3 generates comparisons like
+ (lt (subreg:HI (reg:SI)) 100). Here we assign HImode to 100, but we
+ definitely do not want this prevent the optimization. */
+ comp_mode = iv0->extend_mode;
+ if (GET_MODE_BITSIZE (comp_mode) < GET_MODE_BITSIZE (iv1->extend_mode))
+ comp_mode = iv1->extend_mode;
+
+ if (iv0->extend_mode != comp_mode)
+ {
+ if (iv0->mode != iv0->extend_mode
+ || iv0->step != const0_rtx)
+ return false;
+
+ iv0->base = simplify_gen_unary (signed_p ? SIGN_EXTEND : ZERO_EXTEND,
+ comp_mode, iv0->base, iv0->mode);
+ iv0->extend_mode = comp_mode;
+ }
+
+ if (iv1->extend_mode != comp_mode)
+ {
+ if (iv1->mode != iv1->extend_mode
+ || iv1->step != const0_rtx)
+ return false;
+
+ iv1->base = simplify_gen_unary (signed_p ? SIGN_EXTEND : ZERO_EXTEND,
+ comp_mode, iv1->base, iv1->mode);
+ iv1->extend_mode = comp_mode;
+ }
+
+ /* Check that both ivs belong to a range of a single mode. If one of the
+ operands is an invariant, we may need to shorten it into the common
+ mode. */
+ if (iv0->mode == iv0->extend_mode
+ && iv0->step == const0_rtx
+ && iv0->mode != iv1->mode)
+ shorten_into_mode (iv0, iv1->mode, cond, signed_p, desc);
+
+ if (iv1->mode == iv1->extend_mode
+ && iv1->step == const0_rtx
+ && iv0->mode != iv1->mode)
+ shorten_into_mode (iv1, iv0->mode, swap_condition (cond), signed_p, desc);
+
+ if (iv0->mode != iv1->mode)
+ return false;
+
+ desc->mode = iv0->mode;
+ desc->signed_p = signed_p;
+
+ return true;
+}
+
+/* Computes number of iterations of the CONDITION in INSN in LOOP and stores
+ the result into DESC. Very similar to determine_number_of_iterations
+ (basically its rtl version), complicated by things like subregs. */
+
+void
+iv_number_of_iterations (struct loop *loop, rtx insn, rtx condition,
+ struct niter_desc *desc)
+{
+ rtx op0, op1, delta, step, bound, may_xform, def_insn, tmp, tmp0, tmp1;
+ struct rtx_iv iv0, iv1, tmp_iv;
+ rtx assumption;
+ enum rtx_code cond;
+ enum machine_mode mode, comp_mode;
+ rtx mmin, mmax;
+ unsigned HOST_WIDEST_INT s, size, d;
+ HOST_WIDEST_INT up, down, inc;
+ int was_sharp = false;
+
+ /* The meaning of these assumptions is this:
+ if !assumptions
+ then the rest of information does not have to be valid
+ if noloop_assumptions then the loop does not roll
+ if infinite then this exit is never used */
+
+ desc->assumptions = NULL_RTX;
+ desc->noloop_assumptions = NULL_RTX;
+ desc->infinite = NULL_RTX;
+ desc->simple_p = true;
+
+ desc->const_iter = false;
+ desc->niter_expr = NULL_RTX;
+ desc->niter_max = 0;
+
+ cond = GET_CODE (condition);
+ if (GET_RTX_CLASS (cond) != '<')
+ abort ();
+
+ mode = GET_MODE (XEXP (condition, 0));
+ if (mode == VOIDmode)
+ mode = GET_MODE (XEXP (condition, 1));
+ /* The constant comparisons should be folded. */
+ if (mode == VOIDmode)
+ abort ();
+
+ /* We only handle integers or pointers. */
+ if (GET_MODE_CLASS (mode) != MODE_INT
+ && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
+ goto fail;
+
+ op0 = XEXP (condition, 0);
+ def_insn = iv_get_reaching_def (insn, op0);
+ if (!iv_analyze (def_insn, op0, &iv0))
+ goto fail;
+ if (iv0.extend_mode == VOIDmode)
+ iv0.mode = iv0.extend_mode = mode;
+
+ op1 = XEXP (condition, 1);
+ def_insn = iv_get_reaching_def (insn, op1);
+ if (!iv_analyze (def_insn, op1, &iv1))
+ goto fail;
+ if (iv1.extend_mode == VOIDmode)
+ iv1.mode = iv1.extend_mode = mode;
+
+ if (GET_MODE_BITSIZE (iv0.extend_mode) > HOST_BITS_PER_WIDE_INT
+ || GET_MODE_BITSIZE (iv1.extend_mode) > HOST_BITS_PER_WIDE_INT)
+ goto fail;
+
+ /* Check condition and normalize it. */
+
+ switch (cond)
+ {
+ case GE:
+ case GT:
+ case GEU:
+ case GTU:
+ tmp_iv = iv0; iv0 = iv1; iv1 = tmp_iv;
+ cond = swap_condition (cond);
+ break;
+ case NE:
+ case LE:
+ case LEU:
+ case LT:
+ case LTU:
+ break;
+ default:
+ goto fail;
+ }
+
+ /* Handle extends. This is relatively nontrivial, so we only try in some
+ easy cases, when we can canonicalize the ivs (possibly by adding some
+ assumptions) to shape subreg (base + i * step). This function also fills
+ in desc->mode and desc->signed_p. */
+
+ if (!canonicalize_iv_subregs (&iv0, &iv1, cond, desc))
+ goto fail;
+
+ comp_mode = iv0.extend_mode;
+ mode = iv0.mode;
+ size = GET_MODE_BITSIZE (mode);
+ get_mode_bounds (mode, (cond == LE || cond == LT), &mmin, &mmax);
+
+ if (GET_CODE (iv0.step) != CONST_INT || GET_CODE (iv1.step) != CONST_INT)
+ goto fail;
+
+ /* We can take care of the case of two induction variables chasing each other
+ if the test is NE. I have never seen a loop using it, but still it is
+ cool. */
+ if (iv0.step != const0_rtx && iv1.step != const0_rtx)
+ {
+ if (cond != NE)
+ goto fail;
+
+ iv0.step = simplify_gen_binary (MINUS, comp_mode, iv0.step, iv1.step);
+ iv1.step = const0_rtx;
+ }
+
+ /* This is either infinite loop or the one that ends immediately, depending
+ on initial values. Unswitching should remove this kind of conditions. */
+ if (iv0.step == const0_rtx && iv1.step == const0_rtx)
+ goto fail;
+
+ /* Ignore loops of while (i-- < 10) type. */
+ if (cond != NE
+ && (INTVAL (iv0.step) < 0 || INTVAL (iv1.step) > 0))
+ goto fail;
+
+ /* Some more condition normalization. We must record some assumptions
+ due to overflows. */
+ switch (cond)
+ {
+ case LT:
+ case LTU:
+ /* We want to take care only of non-sharp relationals; this is easy,
+ as in cases the overflow would make the transformation unsafe
+ the loop does not roll. Seemingly it would make more sense to want
+ to take care of sharp relationals instead, as NE is more similar to
+ them, but the problem is that here the transformation would be more
+ difficult due to possibly infinite loops. */
+ if (iv0.step == const0_rtx)
+ {
+ tmp = lowpart_subreg (mode, iv0.base, comp_mode);
+ assumption = simplify_gen_relational (EQ, SImode, mode, tmp, mmax);
+ if (assumption == const_true_rtx)
+ goto zero_iter;
+ iv0.base = simplify_gen_binary (PLUS, comp_mode,
+ iv0.base, const1_rtx);
+ }
+ else
+ {
+ tmp = lowpart_subreg (mode, iv1.base, comp_mode);
+ assumption = simplify_gen_relational (EQ, SImode, mode, tmp, mmin);
+ if (assumption == const_true_rtx)
+ goto zero_iter;
+ iv1.base = simplify_gen_binary (PLUS, comp_mode,
+ iv1.base, constm1_rtx);
+ }
+
+ if (assumption != const0_rtx)
+ desc->noloop_assumptions =
+ alloc_EXPR_LIST (0, assumption, desc->noloop_assumptions);
+ cond = (cond == LT) ? LE : LEU;
+
+ /* It will be useful to be able to tell the difference once more in
+ LE -> NE reduction. */
+ was_sharp = true;
+ break;
+ default: ;
+ }
+
+ /* Take care of trivially infinite loops. */
+ if (cond != NE)
+ {
+ if (iv0.step == const0_rtx)
+ {
+ tmp = lowpart_subreg (mode, iv0.base, comp_mode);
+ if (rtx_equal_p (tmp, mmin))
+ {
+ desc->infinite =
+ alloc_EXPR_LIST (0, const_true_rtx, NULL_RTX);
+ return;
+ }
+ }
+ else
+ {
+ tmp = lowpart_subreg (mode, iv1.base, comp_mode);
+ if (rtx_equal_p (tmp, mmax))
+ {
+ desc->infinite =
+ alloc_EXPR_LIST (0, const_true_rtx, NULL_RTX);
+ return;
+ }
+ }
+ }
+
+ /* If we can we want to take care of NE conditions instead of size
+ comparisons, as they are much more friendly (most importantly
+ this takes care of special handling of loops with step 1). We can
+ do it if we first check that upper bound is greater or equal to
+ lower bound, their difference is constant c modulo step and that
+ there is not an overflow. */
+ if (cond != NE)
+ {
+ if (iv0.step == const0_rtx)
+ step = simplify_gen_unary (NEG, comp_mode, iv1.step, comp_mode);
+ else
+ step = iv0.step;
+ delta = simplify_gen_binary (MINUS, comp_mode, iv1.base, iv0.base);
+ delta = lowpart_subreg (mode, delta, comp_mode);
+ delta = simplify_gen_binary (UMOD, mode, delta, step);
+ may_xform = const0_rtx;
+
+ if (GET_CODE (delta) == CONST_INT)
+ {
+ if (was_sharp && INTVAL (delta) == INTVAL (step) - 1)
+ {
+ /* A special case. We have transformed condition of type
+ for (i = 0; i < 4; i += 4)
+ into
+ for (i = 0; i <= 3; i += 4)
+ obviously if the test for overflow during that transformation
+ passed, we cannot overflow here. Most importantly any
+ loop with sharp end condition and step 1 falls into this
+ category, so handling this case specially is definitely
+ worth the troubles. */
+ may_xform = const_true_rtx;
+ }
+ else if (iv0.step == const0_rtx)
+ {
+ bound = simplify_gen_binary (PLUS, comp_mode, mmin, step);
+ bound = simplify_gen_binary (MINUS, comp_mode, bound, delta);
+ bound = lowpart_subreg (mode, bound, comp_mode);
+ tmp = lowpart_subreg (mode, iv0.base, comp_mode);
+ may_xform = simplify_gen_relational (cond, SImode, mode,
+ bound, tmp);
+ }
+ else
+ {
+ bound = simplify_gen_binary (MINUS, comp_mode, mmax, step);
+ bound = simplify_gen_binary (PLUS, comp_mode, bound, delta);
+ bound = lowpart_subreg (mode, bound, comp_mode);
+ tmp = lowpart_subreg (mode, iv1.base, comp_mode);
+ may_xform = simplify_gen_relational (cond, SImode, mode,
+ tmp, bound);
+ }
+ }
+
+ if (may_xform != const0_rtx)
+ {
+ /* We perform the transformation always provided that it is not
+ completely senseless. This is OK, as we would need this assumption
+ to determine the number of iterations anyway. */
+ if (may_xform != const_true_rtx)
+ desc->assumptions = alloc_EXPR_LIST (0, may_xform,
+ desc->assumptions);
+
+ /* We are going to lose some information about upper bound on
+ number of iterations in this step, so record the information
+ here. */
+ inc = INTVAL (iv0.step) - INTVAL (iv1.step);
+ if (GET_CODE (iv1.base) == CONST_INT)
+ up = INTVAL (iv1.base);
+ else
+ up = INTVAL (mmax) - inc;
+ down = INTVAL (GET_CODE (iv0.base) == CONST_INT ? iv0.base : mmin);
+ desc->niter_max = (up - down) / inc + 1;
+
+ if (iv0.step == const0_rtx)
+ {
+ iv0.base = simplify_gen_binary (PLUS, comp_mode, iv0.base, delta);
+ iv0.base = simplify_gen_binary (MINUS, comp_mode, iv0.base, step);
+ }
+ else
+ {
+ iv1.base = simplify_gen_binary (MINUS, comp_mode, iv1.base, delta);
+ iv1.base = simplify_gen_binary (PLUS, comp_mode, iv1.base, step);
+ }
+
+ tmp0 = lowpart_subreg (mode, iv0.base, comp_mode);
+ tmp1 = lowpart_subreg (mode, iv1.base, comp_mode);
+ assumption = simplify_gen_relational (reverse_condition (cond),
+ SImode, mode, tmp0, tmp1);
+ if (assumption == const_true_rtx)
+ goto zero_iter;
+ else if (assumption != const0_rtx)
+ desc->noloop_assumptions =
+ alloc_EXPR_LIST (0, assumption, desc->noloop_assumptions);
+ cond = NE;
+ }
+ }
+
+ /* Count the number of iterations. */
+ if (cond == NE)
+ {
+ /* Everything we do here is just arithmetics modulo size of mode. This
+ makes us able to do more involved computations of number of iterations
+ than in other cases. First transform the condition into shape
+ s * i <> c, with s positive. */
+ iv1.base = simplify_gen_binary (MINUS, comp_mode, iv1.base, iv0.base);
+ iv0.base = const0_rtx;
+ iv0.step = simplify_gen_binary (MINUS, comp_mode, iv0.step, iv1.step);
+ iv1.step = const0_rtx;
+ if (INTVAL (iv0.step) < 0)
+ {
+ iv0.step = simplify_gen_unary (NEG, comp_mode, iv0.step, mode);
+ iv1.base = simplify_gen_unary (NEG, comp_mode, iv1.base, mode);
+ }
+ iv0.step = lowpart_subreg (mode, iv0.step, comp_mode);
+
+ /* Let nsd (s, size of mode) = d. If d does not divide c, the loop
+ is infinite. Otherwise, the number of iterations is
+ (inverse(s/d) * (c/d)) mod (size of mode/d). */
+ s = INTVAL (iv0.step); d = 1;
+ while (s % 2 != 1)
+ {
+ s /= 2;
+ d *= 2;
+ size--;
+ }
+ bound = GEN_INT (((unsigned HOST_WIDEST_INT) 1 << (size - 1 ) << 1) - 1);
+
+ tmp1 = lowpart_subreg (mode, iv1.base, comp_mode);
+ tmp = simplify_gen_binary (UMOD, mode, tmp1, GEN_INT (d));
+ assumption = simplify_gen_relational (NE, SImode, mode, tmp, const0_rtx);
+ desc->infinite = alloc_EXPR_LIST (0, assumption, desc->infinite);
+
+ tmp = simplify_gen_binary (UDIV, mode, tmp1, GEN_INT (d));
+ tmp = simplify_gen_binary (MULT, mode,
+ tmp, GEN_INT (inverse (s, size)));
+ desc->niter_expr = simplify_gen_binary (AND, mode, tmp, bound);
+ }
+ else
+ {
+ if (iv1.step == const0_rtx)
+ /* Condition in shape a + s * i <= b
+ We must know that b + s does not overflow and a <= b + s and then we
+ can compute number of iterations as (b + s - a) / s. (It might
+ seem that we in fact could be more clever about testing the b + s
+ overflow condition using some information about b - a mod s,
+ but it was already taken into account during LE -> NE transform). */
+ {
+ step = iv0.step;
+ tmp0 = lowpart_subreg (mode, iv0.base, comp_mode);
+ tmp1 = lowpart_subreg (mode, iv1.base, comp_mode);
+
+ bound = simplify_gen_binary (MINUS, mode, mmax, step);
+ assumption = simplify_gen_relational (cond, SImode, mode,
+ tmp1, bound);
+ desc->assumptions =
+ alloc_EXPR_LIST (0, assumption, desc->assumptions);
+
+ tmp = simplify_gen_binary (PLUS, comp_mode, iv1.base, iv0.step);
+ tmp = lowpart_subreg (mode, tmp, comp_mode);
+ assumption = simplify_gen_relational (reverse_condition (cond),
+ SImode, mode, tmp0, tmp);
+
+ delta = simplify_gen_binary (PLUS, mode, tmp1, step);
+ delta = simplify_gen_binary (MINUS, mode, delta, tmp0);
+ }
+ else
+ {
+ /* Condition in shape a <= b - s * i
+ We must know that a - s does not overflow and a - s <= b and then
+ we can again compute number of iterations as (b - (a - s)) / s. */
+ step = simplify_gen_unary (NEG, mode, iv1.step, mode);
+ tmp0 = lowpart_subreg (mode, iv0.base, comp_mode);
+ tmp1 = lowpart_subreg (mode, iv1.base, comp_mode);
+
+ bound = simplify_gen_binary (MINUS, mode, mmin, step);
+ assumption = simplify_gen_relational (cond, SImode, mode,
+ bound, tmp0);
+ desc->assumptions =
+ alloc_EXPR_LIST (0, assumption, desc->assumptions);
+
+ tmp = simplify_gen_binary (PLUS, comp_mode, iv0.base, iv1.step);
+ tmp = lowpart_subreg (mode, tmp, comp_mode);
+ assumption = simplify_gen_relational (reverse_condition (cond),
+ SImode, mode,
+ tmp, tmp1);
+ delta = simplify_gen_binary (MINUS, mode, tmp0, step);
+ delta = simplify_gen_binary (MINUS, mode, tmp1, delta);
+ }
+ if (assumption == const_true_rtx)
+ goto zero_iter;
+ else if (assumption != const0_rtx)
+ desc->noloop_assumptions =
+ alloc_EXPR_LIST (0, assumption, desc->noloop_assumptions);
+ delta = simplify_gen_binary (UDIV, mode, delta, step);
+ desc->niter_expr = delta;
+ }
+
+ simplify_using_initial_values (loop, AND, &desc->assumptions);
+ if (desc->assumptions
+ && XEXP (desc->assumptions, 0) == const0_rtx)
+ goto fail;
+ simplify_using_initial_values (loop, IOR, &desc->noloop_assumptions);
+ simplify_using_initial_values (loop, IOR, &desc->infinite);
+ simplify_using_initial_values (loop, NIL, &desc->niter_expr);
+
+ /* Rerun the simplification. Consider code (created by copying loop headers)
+
+ i = 0;
+
+ if (0 < n)
+ {
+ do
+ {
+ i++;
+ } while (i < n);
+ }
+
+ The first pass determines that i = 0, the second pass uses it to eliminate
+ noloop assumption. */
+
+ simplify_using_initial_values (loop, AND, &desc->assumptions);
+ if (desc->assumptions
+ && XEXP (desc->assumptions, 0) == const0_rtx)
+ goto fail;
+ simplify_using_initial_values (loop, IOR, &desc->noloop_assumptions);
+ simplify_using_initial_values (loop, IOR, &desc->infinite);
+ simplify_using_initial_values (loop, NIL, &desc->niter_expr);
+
+ if (GET_CODE (desc->niter_expr) == CONST_INT)
+ {
+ unsigned HOST_WIDEST_INT val = INTVAL (desc->niter_expr);
+
+ desc->const_iter = true;
+ desc->niter_max = desc->niter = val & GET_MODE_MASK (desc->mode);
+ }
+ else if (!desc->niter_max)
+ desc->niter_max = determine_max_iter (desc);
+
+ return;
+
+fail:
+ desc->simple_p = false;
+ return;
+
+zero_iter:
+ desc->const_iter = true;
+ desc->niter = 0;
+ desc->niter_max = 0;
+ desc->niter_expr = const0_rtx;
+ return;
+}
+
+/* Checks whether E is a simple exit from LOOP and stores its description
+ into DESC. TODO Should replace cfgloopanal.c:simple_loop_exit_p. */
+
+static void
+check_simple_exit (struct loop *loop, edge e, struct niter_desc *desc)
+{
+ basic_block exit_bb;
+ rtx condition, at;
+ edge ei;
+
+ exit_bb = e->src;
+ desc->simple_p = false;
+
+ /* It must belong directly to the loop. */
+ if (exit_bb->loop_father != loop)
+ return;
+
+ /* It must be tested (at least) once during any iteration. */
+ if (!dominated_by_p (CDI_DOMINATORS, loop->latch, exit_bb))
+ return;
+
+ /* It must end in a simple conditional jump. */
+ if (!any_condjump_p (BB_END (exit_bb)))
+ return;
+
+ ei = exit_bb->succ;
+ if (ei == e)
+ ei = ei->succ_next;
+
+ desc->out_edge = e;
+ desc->in_edge = ei;
+
+ /* Test whether the condition is suitable. */
+ if (!(condition = get_condition (BB_END (ei->src), &at, false)))
+ return;
+
+ if (ei->flags & EDGE_FALLTHRU)
+ {
+ condition = reversed_condition (condition);
+ if (!condition)
+ return;
+ }
+
+ /* Check that we are able to determine number of iterations and fill
+ in information about it. */
+ iv_number_of_iterations (loop, at, condition, desc);
+}
+
+/* Finds a simple exit of LOOP and stores its description into DESC.
+ TODO Should replace cfgloopanal.c:simple_loop_p. */
+
+void
+find_simple_exit (struct loop *loop, struct niter_desc *desc)
+{
+ unsigned i;
+ basic_block *body;
+ edge e;
+ struct niter_desc act;
+ bool any = false;
+
+ desc->simple_p = false;
+ body = get_loop_body (loop);
+
+ for (i = 0; i < loop->num_nodes; i++)
+ {
+ for (e = body[i]->succ; e; e = e->succ_next)
+ {
+ if (flow_bb_inside_loop_p (loop, e->dest))
+ continue;
+
+ check_simple_exit (loop, e, &act);
+ if (!act.simple_p)
+ continue;
+
+ /* Prefer constant iterations; the less the better. */
+ if (!any)
+ any = true;
+ else if (!act.const_iter
+ || (desc->const_iter && act.niter >= desc->niter))
+ continue;
+ *desc = act;
+ }
+ }
+
+ if (dump_file)
+ {
+ if (desc->simple_p)
+ {
+ fprintf (dump_file, "Loop %d is simple:\n", loop->num);
+ fprintf (dump_file, " simple exit %d -> %d\n",
+ desc->out_edge->src->index,
+ desc->out_edge->dest->index);
+ if (desc->assumptions)
+ {
+ fprintf (dump_file, " assumptions: ");
+ print_rtl (dump_file, desc->assumptions);
+ fprintf (dump_file, "\n");
+ }
+ if (desc->noloop_assumptions)
+ {
+ fprintf (dump_file, " does not roll if: ");
+ print_rtl (dump_file, desc->noloop_assumptions);
+ fprintf (dump_file, "\n");
+ }
+ if (desc->infinite)
+ {
+ fprintf (dump_file, " infinite if: ");
+ print_rtl (dump_file, desc->infinite);
+ fprintf (dump_file, "\n");
+ }
+
+ fprintf (dump_file, " number of iterations: ");
+ print_rtl (dump_file, desc->niter_expr);
+ fprintf (dump_file, "\n");
+
+ fprintf (dump_file, " upper bound: ");
+ fprintf (dump_file, HOST_WIDEST_INT_PRINT_DEC, desc->niter_max);
+ fprintf (dump_file, "\n");
+ }
+ else
+ fprintf (dump_file, "Loop %d is not simple.\n", loop->num);
+ }
+
+ free (body);
+}
+
+/* Creates a simple loop description of LOOP if it was not computed
+ already. */
+
+struct niter_desc *
+get_simple_loop_desc (struct loop *loop)
+{
+ struct niter_desc *desc = simple_loop_desc (loop);
+
+ if (desc)
+ return desc;
+
+ desc = xmalloc (sizeof (struct niter_desc));
+ iv_analysis_loop_init (loop);
+ find_simple_exit (loop, desc);
+ loop->aux = desc;
+
+ return desc;
+}
+
+/* Releases simple loop description for LOOP. */
+
+void
+free_simple_loop_desc (struct loop *loop)
+{
+ struct niter_desc *desc = simple_loop_desc (loop);
+
+ if (!desc)
+ return;
+
+ free (desc);
+ loop->aux = NULL;
+}
diff --git a/gcc/passes.c b/gcc/passes.c
new file mode 100644
index 00000000000..1b012772f4b
--- /dev/null
+++ b/gcc/passes.c
@@ -0,0 +1,2177 @@
+/* Top level of GCC compilers (cc1, cc1plus, etc.)
+ Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
+ 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
+
+/* This is the top level of cc1/c++.
+ It parses command args, opens files, invokes the various passes
+ in the proper order, and counts the time used by each.
+ Error messages and low-level interface to malloc also handled here. */
+
+#include "config.h"
+#undef FLOAT /* This is for hpux. They should change hpux. */
+#undef FFS /* Some systems define this in param.h. */
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include <signal.h>
+
+#ifdef HAVE_SYS_RESOURCE_H
+# include <sys/resource.h>
+#endif
+
+#ifdef HAVE_SYS_TIMES_H
+# include <sys/times.h>
+#endif
+
+#include "line-map.h"
+#include "input.h"
+#include "tree.h"
+#include "rtl.h"
+#include "tm_p.h"
+#include "flags.h"
+#include "insn-attr.h"
+#include "insn-config.h"
+#include "insn-flags.h"
+#include "hard-reg-set.h"
+#include "recog.h"
+#include "output.h"
+#include "except.h"
+#include "function.h"
+#include "toplev.h"
+#include "expr.h"
+#include "basic-block.h"
+#include "intl.h"
+#include "ggc.h"
+#include "graph.h"
+#include "loop.h"
+#include "regs.h"
+#include "timevar.h"
+#include "diagnostic.h"
+#include "params.h"
+#include "reload.h"
+#include "dwarf2asm.h"
+#include "integrate.h"
+#include "real.h"
+#include "debug.h"
+#include "target.h"
+#include "langhooks.h"
+#include "cfglayout.h"
+#include "cfgloop.h"
+#include "hosthooks.h"
+#include "cgraph.h"
+#include "opts.h"
+#include "coverage.h"
+#include "value-prof.h"
+#include "alloc-pool.h"
+
+#if defined (DWARF2_UNWIND_INFO) || defined (DWARF2_DEBUGGING_INFO)
+#include "dwarf2out.h"
+#endif
+
+#if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO)
+#include "dbxout.h"
+#endif
+
+#ifdef SDB_DEBUGGING_INFO
+#include "sdbout.h"
+#endif
+
+#ifdef XCOFF_DEBUGGING_INFO
+#include "xcoffout.h" /* Needed for external data
+ declarations for e.g. AIX 4.x. */
+#endif
+
+#ifndef HAVE_conditional_execution
+#define HAVE_conditional_execution 0
+#endif
+
+/* Format to use to print dumpfile index value */
+#ifndef DUMPFILE_FORMAT
+#define DUMPFILE_FORMAT ".%02d."
+#endif
+
+/* Describes a dump file. */
+
+struct dump_file_info
+{
+ /* The unique extension to apply, e.g. ".jump". */
+ const char *const extension;
+
+ /* The -d<c> character that enables this dump file. */
+ char const debug_switch;
+
+ /* True if there is a corresponding graph dump file. */
+ char const graph_dump_p;
+
+ /* True if the user selected this dump. */
+ char enabled;
+
+ /* True if the files have been initialized (ie truncated). */
+ char initialized;
+};
+
+/* Enumerate the extant dump files. */
+
+enum dump_file_index
+{
+ DFI_cgraph,
+ DFI_rtl,
+ DFI_sibling,
+ DFI_eh,
+ DFI_jump,
+ DFI_null,
+ DFI_cse,
+ DFI_addressof,
+ DFI_gcse,
+ DFI_loop,
+ DFI_bypass,
+ DFI_cfg,
+ DFI_bp,
+ DFI_vpt,
+ DFI_ce1,
+ DFI_tracer,
+ DFI_loop2,
+ DFI_web,
+ DFI_cse2,
+ DFI_life,
+ DFI_combine,
+ DFI_ce2,
+ DFI_regmove,
+ DFI_sched,
+ DFI_lreg,
+ DFI_greg,
+ DFI_postreload,
+ DFI_flow2,
+ DFI_peephole2,
+ DFI_ce3,
+ DFI_rnreg,
+ DFI_bbro,
+ DFI_branch_target_load,
+ DFI_sched2,
+ DFI_stack,
+ DFI_vartrack,
+ DFI_mach,
+ DFI_dbr,
+ DFI_MAX
+};
+
+/* Describes all the dump files. Should be kept in order of the
+ pass and in sync with dump_file_index above.
+
+ Remaining -d letters:
+
+ " e m q "
+ " JK O Q WXY "
+*/
+
+static struct dump_file_info dump_file_tbl[DFI_MAX] =
+{
+ { "cgraph", 'U', 0, 0, 0 },
+ { "rtl", 'r', 0, 0, 0 },
+ { "sibling", 'i', 0, 0, 0 },
+ { "eh", 'h', 0, 0, 0 },
+ { "jump", 'j', 0, 0, 0 },
+ { "null", 'u', 0, 0, 0 },
+ { "cse", 's', 0, 0, 0 },
+ { "addressof", 'F', 0, 0, 0 },
+ { "gcse", 'G', 1, 0, 0 },
+ { "loop", 'L', 1, 0, 0 },
+ { "bypass", 'G', 1, 0, 0 }, /* Yes, duplicate enable switch. */
+ { "cfg", 'f', 1, 0, 0 },
+ { "bp", 'b', 1, 0, 0 },
+ { "vpt", 'V', 1, 0, 0 },
+ { "ce1", 'C', 1, 0, 0 },
+ { "tracer", 'T', 1, 0, 0 },
+ { "loop2", 'L', 1, 0, 0 },
+ { "web", 'Z', 0, 0, 0 },
+ { "cse2", 't', 1, 0, 0 },
+ { "life", 'f', 1, 0, 0 }, /* Yes, duplicate enable switch. */
+ { "combine", 'c', 1, 0, 0 },
+ { "ce2", 'C', 1, 0, 0 },
+ { "regmove", 'N', 1, 0, 0 },
+ { "sched", 'S', 1, 0, 0 },
+ { "lreg", 'l', 1, 0, 0 },
+ { "greg", 'g', 1, 0, 0 },
+ { "postreload", 'o', 1, 0, 0 },
+ { "flow2", 'w', 1, 0, 0 },
+ { "peephole2", 'z', 1, 0, 0 },
+ { "ce3", 'E', 1, 0, 0 },
+ { "rnreg", 'n', 1, 0, 0 },
+ { "bbro", 'B', 1, 0, 0 },
+ { "btl", 'd', 1, 0, 0 }, /* Yes, duplicate enable switch. */
+ { "sched2", 'R', 1, 0, 0 },
+ { "stack", 'k', 1, 0, 0 },
+ { "vartrack", 'V', 1, 0, 0 }, /* Yes, duplicate enable switch. */
+ { "mach", 'M', 1, 0, 0 },
+ { "dbr", 'd', 0, 0, 0 },
+};
+
+/* Routine to open a dump file. Return true if the dump file is enabled. */
+
+static int
+open_dump_file (enum dump_file_index index, tree decl)
+{
+ char *dump_name;
+ const char *open_arg;
+ char seq[16];
+
+ if (! dump_file_tbl[index].enabled)
+ return 0;
+
+ timevar_push (TV_DUMP);
+ if (dump_file != NULL)
+ fclose (dump_file);
+
+ sprintf (seq, DUMPFILE_FORMAT, index);
+
+ if (! dump_file_tbl[index].initialized)
+ {
+ /* If we've not initialized the files, do so now. */
+ if (graph_dump_format != no_graph
+ && dump_file_tbl[index].graph_dump_p)
+ {
+ dump_name = concat (seq, dump_file_tbl[index].extension, NULL);
+ clean_graph_dump_file (dump_base_name, dump_name);
+ free (dump_name);
+ }
+ dump_file_tbl[index].initialized = 1;
+ open_arg = "w";
+ }
+ else
+ open_arg = "a";
+
+ dump_name = concat (dump_base_name, seq,
+ dump_file_tbl[index].extension, NULL);
+
+ dump_file = fopen (dump_name, open_arg);
+ if (dump_file == NULL)
+ fatal_error ("can't open %s: %m", dump_name);
+
+ free (dump_name);
+
+ if (decl)
+ fprintf (dump_file, "\n;; Function %s%s\n\n",
+ (*lang_hooks.decl_printable_name) (decl, 2),
+ cfun->function_frequency == FUNCTION_FREQUENCY_HOT
+ ? " (hot)"
+ : cfun->function_frequency == FUNCTION_FREQUENCY_UNLIKELY_EXECUTED
+ ? " (unlikely executed)"
+ : "");
+
+ timevar_pop (TV_DUMP);
+ return 1;
+}
+
+/* Routine to close a dump file. */
+
+static void
+close_dump_file (enum dump_file_index index,
+ void (*func) (FILE *, rtx),
+ rtx insns)
+{
+ if (! dump_file)
+ return;
+
+ timevar_push (TV_DUMP);
+ if (insns
+ && graph_dump_format != no_graph
+ && dump_file_tbl[index].graph_dump_p)
+ {
+ char seq[16];
+ char *suffix;
+
+ sprintf (seq, DUMPFILE_FORMAT, index);
+ suffix = concat (seq, dump_file_tbl[index].extension, NULL);
+ print_rtl_graph_with_bb (dump_base_name, suffix, insns);
+ free (suffix);
+ }
+
+ if (func && insns)
+ func (dump_file, insns);
+
+ fflush (dump_file);
+ fclose (dump_file);
+
+ dump_file = NULL;
+ timevar_pop (TV_DUMP);
+}
+
+/* This is called from various places for FUNCTION_DECL, VAR_DECL,
+ and TYPE_DECL nodes.
+
+ This does nothing for local (non-static) variables, unless the
+ variable is a register variable with an ASMSPEC. In that case, or
+ if the variable is not an automatic, it sets up the RTL and
+ outputs any assembler code (label definition, storage allocation
+ and initialization).
+
+ DECL is the declaration. If ASMSPEC is nonzero, it specifies
+ the assembler symbol name to be used. TOP_LEVEL is nonzero
+ if this declaration is not within a function. */
+
+void
+rest_of_decl_compilation (tree decl,
+ const char *asmspec,
+ int top_level,
+ int at_end)
+{
+ /* We deferred calling assemble_alias so that we could collect
+ other attributes such as visibility. Emit the alias now. */
+ {
+ tree alias;
+ alias = lookup_attribute ("alias", DECL_ATTRIBUTES (decl));
+ if (alias)
+ {
+ alias = TREE_VALUE (TREE_VALUE (alias));
+ alias = get_identifier (TREE_STRING_POINTER (alias));
+ assemble_alias (decl, alias);
+ }
+ }
+
+ /* Forward declarations for nested functions are not "external",
+ but we need to treat them as if they were. */
+ if (TREE_STATIC (decl) || DECL_EXTERNAL (decl)
+ || TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ timevar_push (TV_VARCONST);
+
+ if (asmspec)
+ make_decl_rtl (decl, asmspec);
+
+ /* Don't output anything when a tentative file-scope definition
+ is seen. But at end of compilation, do output code for them.
+
+ We do output all variables when unit-at-a-time is active and rely on
+ callgraph code to defer them except for forward declarations
+ (see gcc.c-torture/compile/920624-1.c) */
+ if ((at_end
+ || !DECL_DEFER_OUTPUT (decl)
+ || (flag_unit_at_a_time && DECL_INITIAL (decl)))
+ && !DECL_EXTERNAL (decl))
+ {
+ if (flag_unit_at_a_time && !cgraph_global_info_ready
+ && TREE_CODE (decl) != FUNCTION_DECL && top_level)
+ cgraph_varpool_finalize_decl (decl);
+ else
+ assemble_variable (decl, top_level, at_end, 0);
+ }
+
+#ifdef ASM_FINISH_DECLARE_OBJECT
+ if (decl == last_assemble_variable_decl)
+ {
+ ASM_FINISH_DECLARE_OBJECT (asm_out_file, decl,
+ top_level, at_end);
+ }
+#endif
+
+ timevar_pop (TV_VARCONST);
+ }
+ else if (DECL_REGISTER (decl) && asmspec != 0)
+ {
+ if (decode_reg_name (asmspec) >= 0)
+ {
+ SET_DECL_RTL (decl, NULL_RTX);
+ make_decl_rtl (decl, asmspec);
+ }
+ else
+ {
+ error ("invalid register name `%s' for register variable", asmspec);
+ DECL_REGISTER (decl) = 0;
+ if (!top_level)
+ expand_decl (decl);
+ }
+ }
+ else if (TREE_CODE (decl) == TYPE_DECL)
+ {
+ timevar_push (TV_SYMOUT);
+ debug_hooks->type_decl (decl, !top_level);
+ timevar_pop (TV_SYMOUT);
+ }
+}
+
+/* Called after finishing a record, union or enumeral type. */
+
+void
+rest_of_type_compilation (tree type, int toplev)
+{
+ /* Avoid confusing the debug information machinery when there are
+ errors. */
+ if (errorcount != 0 || sorrycount != 0)
+ return;
+
+ timevar_push (TV_SYMOUT);
+ debug_hooks->type_decl (TYPE_STUB_DECL (type), !toplev);
+ timevar_pop (TV_SYMOUT);
+}
+
+/* Turn the RTL into assembly. */
+static void
+rest_of_handle_final (tree decl, rtx insns)
+{
+ timevar_push (TV_FINAL);
+ {
+ rtx x;
+ const char *fnname;
+
+ /* Get the function's name, as described by its RTL. This may be
+ different from the DECL_NAME name used in the source file. */
+
+ x = DECL_RTL (decl);
+ if (GET_CODE (x) != MEM)
+ abort ();
+ x = XEXP (x, 0);
+ if (GET_CODE (x) != SYMBOL_REF)
+ abort ();
+ fnname = XSTR (x, 0);
+
+ assemble_start_function (decl, fnname);
+ final_start_function (insns, asm_out_file, optimize);
+ final (insns, asm_out_file, optimize, 0);
+ final_end_function ();
+
+#ifdef IA64_UNWIND_INFO
+ /* ??? The IA-64 ".handlerdata" directive must be issued before
+ the ".endp" directive that closes the procedure descriptor. */
+ output_function_exception_table ();
+#endif
+
+ assemble_end_function (decl, fnname);
+
+#ifndef IA64_UNWIND_INFO
+ /* Otherwise, it feels unclean to switch sections in the middle. */
+ output_function_exception_table ();
+#endif
+
+ if (! quiet_flag)
+ fflush (asm_out_file);
+
+ /* Release all memory allocated by flow. */
+ free_basic_block_vars (0);
+
+ /* Release all memory held by regsets now. */
+ regset_release_memory ();
+ }
+ timevar_pop (TV_FINAL);
+
+ ggc_collect ();
+}
+
+#ifdef DELAY_SLOTS
+/* Run delay slot optimization. */
+static void
+rest_of_handle_delay_slots (tree decl, rtx insns)
+{
+ timevar_push (TV_DBR_SCHED);
+ open_dump_file (DFI_dbr, decl);
+
+ dbr_schedule (insns, dump_file);
+
+ close_dump_file (DFI_dbr, print_rtl, insns);
+ timevar_pop (TV_DBR_SCHED);
+
+ ggc_collect ();
+}
+#endif
+
+#ifdef STACK_REGS
+/* Convert register usage from flat register file usage to a stack
+ register file. */
+static void
+rest_of_handle_stack_regs (tree decl, rtx insns)
+{
+#if defined (HAVE_ATTR_length)
+ /* If flow2 creates new instructions which need splitting
+ and scheduling after reload is not done, they might not be
+ split until final which doesn't allow splitting
+ if HAVE_ATTR_length. */
+#ifdef INSN_SCHEDULING
+ if (optimize && !flag_schedule_insns_after_reload)
+#else
+ if (optimize)
+#endif
+ {
+ timevar_push (TV_SHORTEN_BRANCH);
+ split_all_insns (1);
+ timevar_pop (TV_SHORTEN_BRANCH);
+ }
+#endif
+
+ timevar_push (TV_REG_STACK);
+ open_dump_file (DFI_stack, decl);
+
+ if (reg_to_stack (insns, dump_file) && optimize)
+ {
+ if (cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_POST_REGSTACK
+ | (flag_crossjumping ? CLEANUP_CROSSJUMP : 0))
+ && flag_reorder_blocks)
+ {
+ reorder_basic_blocks ();
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_POST_REGSTACK);
+ }
+ }
+
+ close_dump_file (DFI_stack, print_rtl_with_bb, insns);
+ timevar_pop (TV_REG_STACK);
+
+ ggc_collect ();
+}
+#endif
+
+/* Track the variables, ie. compute where the variable is stored at each position in function. */
+static void
+rest_of_handle_variable_tracking (tree decl, rtx insns)
+{
+ timevar_push (TV_VAR_TRACKING);
+ open_dump_file (DFI_vartrack, decl);
+
+ variable_tracking_main ();
+
+ close_dump_file (DFI_vartrack, print_rtl_with_bb, insns);
+ timevar_pop (TV_VAR_TRACKING);
+}
+
+/* Machine independent reorg pass. */
+static void
+rest_of_handle_machine_reorg (tree decl, rtx insns)
+{
+ timevar_push (TV_MACH_DEP);
+ open_dump_file (DFI_mach, decl);
+
+ (*targetm.machine_dependent_reorg) ();
+
+ close_dump_file (DFI_mach, print_rtl, insns);
+ timevar_pop (TV_MACH_DEP);
+
+ ggc_collect ();
+}
+
+
+/* Run new register allocator. Return TRUE if we must exit
+ rest_of_compilation upon return. */
+static bool
+rest_of_handle_new_regalloc (tree decl, rtx insns)
+{
+ int failure;
+
+ delete_trivially_dead_insns (insns, max_reg_num ());
+ reg_alloc ();
+
+ timevar_pop (TV_LOCAL_ALLOC);
+ if (dump_file_tbl[DFI_lreg].enabled)
+ {
+ timevar_push (TV_DUMP);
+
+ close_dump_file (DFI_lreg, NULL, NULL);
+ timevar_pop (TV_DUMP);
+ }
+
+ /* XXX clean up the whole mess to bring live info in shape again. */
+ timevar_push (TV_GLOBAL_ALLOC);
+ open_dump_file (DFI_greg, decl);
+
+ build_insn_chain (insns);
+ failure = reload (insns, 0);
+
+ timevar_pop (TV_GLOBAL_ALLOC);
+
+ if (dump_file_tbl[DFI_greg].enabled)
+ {
+ timevar_push (TV_DUMP);
+
+ dump_global_regs (dump_file);
+
+ close_dump_file (DFI_greg, print_rtl_with_bb, insns);
+ timevar_pop (TV_DUMP);
+ }
+
+ if (failure)
+ return true;
+
+ reload_completed = 1;
+
+ return false;
+}
+
+/* Run old register allocator. Return TRUE if we must exit
+ rest_of_compilation upon return. */
+static bool
+rest_of_handle_old_regalloc (tree decl, rtx insns)
+{
+ int failure;
+ int rebuild_notes;
+
+ /* Allocate the reg_renumber array. */
+ allocate_reg_info (max_regno, FALSE, TRUE);
+
+ /* And the reg_equiv_memory_loc array. */
+ reg_equiv_memory_loc = xcalloc (max_regno, sizeof (rtx));
+
+ allocate_initial_values (reg_equiv_memory_loc);
+
+ regclass (insns, max_reg_num (), dump_file);
+ rebuild_notes = local_alloc ();
+
+ timevar_pop (TV_LOCAL_ALLOC);
+
+ /* Local allocation may have turned an indirect jump into a direct
+ jump. If so, we must rebuild the JUMP_LABEL fields of jumping
+ instructions. */
+ if (rebuild_notes)
+ {
+ timevar_push (TV_JUMP);
+
+ rebuild_jump_labels (insns);
+ purge_all_dead_edges (0);
+
+ timevar_pop (TV_JUMP);
+ }
+
+ if (dump_file_tbl[DFI_lreg].enabled)
+ {
+ timevar_push (TV_DUMP);
+
+ dump_flow_info (dump_file);
+ dump_local_alloc (dump_file);
+
+ close_dump_file (DFI_lreg, print_rtl_with_bb, insns);
+ timevar_pop (TV_DUMP);
+ }
+
+ ggc_collect ();
+
+ timevar_push (TV_GLOBAL_ALLOC);
+ open_dump_file (DFI_greg, decl);
+
+ /* If optimizing, allocate remaining pseudo-regs. Do the reload
+ pass fixing up any insns that are invalid. */
+
+ if (optimize)
+ failure = global_alloc (dump_file);
+ else
+ {
+ build_insn_chain (insns);
+ failure = reload (insns, 0);
+ }
+
+ timevar_pop (TV_GLOBAL_ALLOC);
+
+ if (dump_file_tbl[DFI_greg].enabled)
+ {
+ timevar_push (TV_DUMP);
+
+ dump_global_regs (dump_file);
+
+ close_dump_file (DFI_greg, print_rtl_with_bb, insns);
+ timevar_pop (TV_DUMP);
+ }
+
+ return failure;
+}
+
+/* Run the regrename and cprop passes. */
+static void
+rest_of_handle_regrename (tree decl, rtx insns)
+{
+ timevar_push (TV_RENAME_REGISTERS);
+ open_dump_file (DFI_rnreg, decl);
+
+ if (flag_rename_registers)
+ regrename_optimize ();
+ if (flag_cprop_registers)
+ copyprop_hardreg_forward ();
+
+ close_dump_file (DFI_rnreg, print_rtl_with_bb, insns);
+ timevar_pop (TV_RENAME_REGISTERS);
+}
+
+/* Reorder basic blocks. */
+static void
+rest_of_handle_reorder_blocks (tree decl, rtx insns)
+{
+ bool changed;
+ open_dump_file (DFI_bbro, decl);
+
+ /* Last attempt to optimize CFG, as scheduling, peepholing and insn
+ splitting possibly introduced more crossjumping opportunities. */
+ changed = cleanup_cfg (CLEANUP_EXPENSIVE
+ | (!HAVE_conditional_execution
+ ? CLEANUP_UPDATE_LIFE : 0));
+
+ if (flag_sched2_use_traces && flag_schedule_insns_after_reload)
+ tracer ();
+ if (flag_reorder_blocks)
+ reorder_basic_blocks ();
+ if (flag_reorder_blocks
+ || (flag_sched2_use_traces && flag_schedule_insns_after_reload))
+ changed |= cleanup_cfg (CLEANUP_EXPENSIVE
+ | (!HAVE_conditional_execution
+ ? CLEANUP_UPDATE_LIFE : 0));
+
+ /* On conditional execution targets we can not update the life cheaply, so
+ we deffer the updating to after both cleanups. This may lose some cases
+ but should not be terribly bad. */
+ if (changed && HAVE_conditional_execution)
+ update_life_info (NULL, UPDATE_LIFE_GLOBAL_RM_NOTES,
+ PROP_DEATH_NOTES);
+ close_dump_file (DFI_bbro, print_rtl_with_bb, insns);
+}
+
+#ifdef INSN_SCHEDULING
+/* Run instruction scheduler. */
+static void
+rest_of_handle_sched (tree decl, rtx insns)
+{
+ timevar_push (TV_SCHED);
+
+ /* Print function header into sched dump now
+ because doing the sched analysis makes some of the dump. */
+ if (optimize > 0 && flag_schedule_insns)
+ {
+ open_dump_file (DFI_sched, decl);
+
+ /* Do control and data sched analysis,
+ and write some of the results to dump file. */
+
+ schedule_insns (dump_file);
+
+ close_dump_file (DFI_sched, print_rtl_with_bb, insns);
+ }
+ timevar_pop (TV_SCHED);
+
+ ggc_collect ();
+}
+
+/* Run second scheduling pass after reload. */
+static void
+rest_of_handle_sched2 (tree decl, rtx insns)
+{
+ timevar_push (TV_SCHED2);
+ open_dump_file (DFI_sched2, decl);
+
+ /* Do control and data sched analysis again,
+ and write some more of the results to dump file. */
+
+ split_all_insns (1);
+
+ if (flag_sched2_use_superblocks || flag_sched2_use_traces)
+ {
+ schedule_ebbs (dump_file);
+ /* No liveness updating code yet, but it should be easy to do.
+ reg-stack recomputes the liveness when needed for now. */
+ count_or_remove_death_notes (NULL, 1);
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+ }
+ else
+ schedule_insns (dump_file);
+
+ close_dump_file (DFI_sched2, print_rtl_with_bb, insns);
+ timevar_pop (TV_SCHED2);
+
+ ggc_collect ();
+}
+#endif
+
+/* Register allocation pre-pass, to reduce number of moves necessary
+ for two-address machines. */
+static void
+rest_of_handle_regmove (tree decl, rtx insns)
+{
+ timevar_push (TV_REGMOVE);
+ open_dump_file (DFI_regmove, decl);
+
+ regmove_optimize (insns, max_reg_num (), dump_file);
+
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_UPDATE_LIFE);
+ close_dump_file (DFI_regmove, print_rtl_with_bb, insns);
+ timevar_pop (TV_REGMOVE);
+
+ ggc_collect ();
+}
+
+/* Run tracer. */
+static void
+rest_of_handle_tracer (tree decl, rtx insns)
+{
+ open_dump_file (DFI_tracer, decl);
+ if (dump_file)
+ dump_flow_info (dump_file);
+ tracer ();
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+ reg_scan (insns, max_reg_num (), 0);
+ close_dump_file (DFI_tracer, print_rtl_with_bb, get_insns ());
+}
+
+/* If-conversion and CFG cleanup. */
+static void
+rest_of_handle_if_conversion (tree decl, rtx insns)
+{
+ open_dump_file (DFI_ce1, decl);
+ if (flag_if_conversion)
+ {
+ timevar_push (TV_IFCVT);
+ if (dump_file)
+ dump_flow_info (dump_file);
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+ reg_scan (insns, max_reg_num (), 0);
+ if_convert (0);
+ timevar_pop (TV_IFCVT);
+ }
+ timevar_push (TV_JUMP);
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+ reg_scan (insns, max_reg_num (), 0);
+ timevar_pop (TV_JUMP);
+ close_dump_file (DFI_ce1, print_rtl_with_bb, get_insns ());
+}
+
+/* Rerun if-conversion, as combine may have simplified things enough
+ to now meet sequence length restrictions. */
+static void
+rest_of_handle_if_after_combine (tree decl, rtx insns)
+{
+ timevar_push (TV_IFCVT);
+ open_dump_file (DFI_ce2, decl);
+
+ no_new_pseudos = 0;
+ if_convert (1);
+ no_new_pseudos = 1;
+
+ close_dump_file (DFI_ce2, print_rtl_with_bb, insns);
+ timevar_pop (TV_IFCVT);
+}
+
+static void
+rest_of_handle_web (tree decl, rtx insns)
+{
+ open_dump_file (DFI_web, decl);
+ timevar_push (TV_WEB);
+ web_main ();
+ delete_trivially_dead_insns (insns, max_reg_num ());
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+
+ timevar_pop (TV_WEB);
+ close_dump_file (DFI_web, print_rtl_with_bb, insns);
+ reg_scan (get_insns (), max_reg_num (), 0);
+}
+
+/* Do branch profiling and static profile estimation passes. */
+static void
+rest_of_handle_branch_prob (tree decl, rtx insns)
+{
+ struct loops loops;
+
+ timevar_push (TV_BRANCH_PROB);
+ open_dump_file (DFI_bp, decl);
+
+ if (profile_arc_flag || flag_test_coverage || flag_branch_probabilities)
+ branch_prob ();
+
+ /* Discover and record the loop depth at the head of each basic
+ block. The loop infrastructure does the real job for us. */
+ flow_loops_find (&loops, LOOP_TREE);
+
+ if (dump_file)
+ flow_loops_dump (&loops, dump_file, NULL, 0);
+
+ /* Estimate using heuristics if no profiling info is available. */
+ if (flag_guess_branch_prob)
+ estimate_probability (&loops);
+
+ flow_loops_free (&loops);
+ free_dominance_info (CDI_DOMINATORS);
+ close_dump_file (DFI_bp, print_rtl_with_bb, insns);
+ timevar_pop (TV_BRANCH_PROB);
+}
+
+/* Do optimizations based on expression value profiles. */
+static void
+rest_of_handle_value_profile_transformations (tree decl, rtx insns)
+{
+ open_dump_file (DFI_vpt, decl);
+ timevar_push (TV_VPT);
+
+ if (value_profile_transformations ())
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+
+ timevar_pop (TV_VPT);
+ close_dump_file (DFI_vpt, print_rtl_with_bb, insns);
+}
+
+/* Do control and data flow analysis; write some of the results to the
+ dump file. */
+static void
+rest_of_handle_cfg (tree decl, rtx insns)
+{
+ open_dump_file (DFI_cfg, decl);
+ if (dump_file)
+ dump_flow_info (dump_file);
+ if (optimize)
+ cleanup_cfg (CLEANUP_EXPENSIVE
+ | (flag_thread_jumps ? CLEANUP_THREADING : 0));
+
+ /* It may make more sense to mark constant functions after dead code is
+ eliminated by life_analysis, but we need to do it early, as -fprofile-arcs
+ may insert code making function non-constant, but we still must consider
+ it as constant, otherwise -fbranch-probabilities will not read data back.
+
+ life_analysis rarely eliminates modification of external memory.
+ */
+ if (optimize)
+ {
+ /* Alias analysis depends on this information and mark_constant_function
+ depends on alias analysis. */
+ reg_scan (insns, max_reg_num (), 1);
+ mark_constant_function ();
+ }
+
+ close_dump_file (DFI_cfg, print_rtl_with_bb, insns);
+}
+
+/* Purge addressofs. */
+static void
+rest_of_handle_addressof (tree decl, rtx insns)
+{
+ open_dump_file (DFI_addressof, decl);
+
+ purge_addressof (insns);
+ if (optimize && purge_all_dead_edges (0))
+ delete_unreachable_blocks ();
+ reg_scan (insns, max_reg_num (), 1);
+
+ close_dump_file (DFI_addressof, print_rtl, insns);
+}
+
+/* We may have potential sibling or tail recursion sites. Select one
+ (of possibly multiple) methods of performing the call. */
+static void
+rest_of_handle_sibling_calls (rtx insns)
+{
+ rtx insn;
+ optimize_sibling_and_tail_recursive_calls ();
+
+ /* Recompute the CFG as sibling optimization clobbers it randomly. */
+ free_bb_for_insn ();
+ find_exception_handler_labels ();
+ rebuild_jump_labels (insns);
+ find_basic_blocks (insns, max_reg_num (), dump_file);
+
+ /* There is pass ordering problem - we must lower NOTE_INSN_PREDICTION
+ notes before simplifying cfg and we must do lowering after sibcall
+ that unhides parts of RTL chain and cleans up the CFG.
+
+ Until sibcall is replaced by tree-level optimizer, lets just
+ sweep away the NOTE_INSN_PREDICTION notes that leaked out. */
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) == NOTE_INSN_PREDICTION)
+ delete_insn (insn);
+
+ close_dump_file (DFI_sibling, print_rtl, get_insns ());
+}
+
+/* Perform jump bypassing and control flow optimizations. */
+static void
+rest_of_handle_jump_bypass (tree decl, rtx insns)
+{
+ timevar_push (TV_BYPASS);
+ open_dump_file (DFI_bypass, decl);
+
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+ reg_scan (insns, max_reg_num (), 1);
+
+ if (bypass_jumps (dump_file))
+ {
+ rebuild_jump_labels (insns);
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+ delete_trivially_dead_insns (insns, max_reg_num ());
+ }
+
+ close_dump_file (DFI_bypass, print_rtl_with_bb, insns);
+ timevar_pop (TV_BYPASS);
+
+ ggc_collect ();
+
+#ifdef ENABLE_CHECKING
+ verify_flow_info ();
+#endif
+}
+
+/* Handle inlining of functions in rest_of_compilation. Return TRUE
+ if we must exit rest_of_compilation upon return. */
+static bool
+rest_of_handle_inlining (tree decl)
+{
+ rtx insns;
+ int inlinable = 0;
+ tree parent;
+ const char *lose;
+
+ /* If we are reconsidering an inline function at the end of
+ compilation, skip the stuff for making it inline. */
+ if (cfun->rtl_inline_init)
+ return 0;
+ cfun->rtl_inline_init = 1;
+
+ /* If this is nested inside an inlined external function, pretend
+ it was only declared. Since we cannot inline such functions,
+ generating code for this one is not only not necessary but will
+ confuse some debugging output writers. */
+ for (parent = DECL_CONTEXT (current_function_decl);
+ parent != NULL_TREE;
+ parent = get_containing_scope (parent))
+ if (TREE_CODE (parent) == FUNCTION_DECL
+ && DECL_INLINE (parent) && DECL_EXTERNAL (parent))
+ {
+ DECL_INITIAL (decl) = 0;
+ return true;
+ }
+ else if (TYPE_P (parent))
+ /* A function in a local class should be treated normally. */
+ break;
+
+ /* If requested, consider whether to make this function inline. */
+ if ((DECL_INLINE (decl) && !flag_no_inline)
+ || flag_inline_functions)
+ {
+ timevar_push (TV_INTEGRATION);
+ lose = function_cannot_inline_p (decl);
+ timevar_pop (TV_INTEGRATION);
+ if (lose || ! optimize)
+ {
+ if (warn_inline && lose && DECL_INLINE (decl))
+ {
+ char *msg = concat ("%J", lose, NULL);
+ warning (msg, decl);
+ free (msg);
+ }
+ DECL_ABSTRACT_ORIGIN (decl) = 0;
+ /* Don't really compile an extern inline function.
+ If we can't make it inline, pretend
+ it was only declared. */
+ if (DECL_EXTERNAL (decl))
+ {
+ DECL_INITIAL (decl) = 0;
+ return true;
+ }
+ }
+ else
+ inlinable = DECL_INLINE (decl) = 1;
+ }
+
+ insns = get_insns ();
+
+ /* Dump the rtl code if we are dumping rtl. */
+
+ if (open_dump_file (DFI_rtl, decl))
+ {
+ if (DECL_STRUCT_FUNCTION (decl)
+ && DECL_STRUCT_FUNCTION (decl)->saved_for_inline)
+ fprintf (dump_file, ";; (integrable)\n\n");
+ close_dump_file (DFI_rtl, print_rtl, insns);
+ }
+
+ /* Convert from NOTE_INSN_EH_REGION style notes, and do other
+ sorts of eh initialization. Delay this until after the
+ initial rtl dump so that we can see the original nesting. */
+ convert_from_eh_region_ranges ();
+
+ /* If function is inline, and we don't yet know whether to
+ compile it by itself, defer decision till end of compilation.
+ wrapup_global_declarations will (indirectly) call
+ rest_of_compilation again for those functions that need to
+ be output. Also defer those functions that we are supposed
+ to defer. */
+
+ if (inlinable
+ || (DECL_INLINE (decl)
+ /* Egad. This RTL deferral test conflicts with Fortran assumptions
+ for unreferenced symbols. See g77.f-torture/execute/980520-1.f.
+ But removing this line from the check breaks all languages that
+ use the call graph to output symbols. This hard-coded check is
+ the least invasive work-around. */
+ && (flag_inline_functions
+ || strcmp (lang_hooks.name, "GNU F77") == 0)
+ && ((! TREE_PUBLIC (decl) && ! TREE_ADDRESSABLE (decl)
+ && ! TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))
+ && ! flag_keep_inline_functions)
+ || DECL_EXTERNAL (decl))))
+ DECL_DEFER_OUTPUT (decl) = 1;
+
+ if (DECL_INLINE (decl))
+ /* DWARF wants separate debugging info for abstract and
+ concrete instances of all inline functions, including those
+ declared inline but not inlined, and those inlined even
+ though they weren't declared inline. Conveniently, that's
+ what DECL_INLINE means at this point. */
+ (*debug_hooks->deferred_inline_function) (decl);
+
+ if (DECL_DEFER_OUTPUT (decl))
+ {
+ /* If -Wreturn-type, we have to do a bit of compilation. We just
+ want to call cleanup the cfg to figure out whether or not we can
+ fall off the end of the function; we do the minimum amount of
+ work necessary to make that safe. */
+ if (warn_return_type)
+ {
+ int saved_optimize = optimize;
+
+ optimize = 0;
+ rebuild_jump_labels (insns);
+ find_exception_handler_labels ();
+ find_basic_blocks (insns, max_reg_num (), dump_file);
+ cleanup_cfg (CLEANUP_PRE_SIBCALL | CLEANUP_PRE_LOOP);
+ optimize = saved_optimize;
+
+ /* CFG is no longer maintained up-to-date. */
+ free_bb_for_insn ();
+ }
+
+ set_nothrow_function_flags ();
+ if (current_function_nothrow)
+ /* Now we know that this can't throw; set the flag for the benefit
+ of other functions later in this translation unit. */
+ TREE_NOTHROW (current_function_decl) = 1;
+
+ timevar_push (TV_INTEGRATION);
+ save_for_inline (decl);
+ timevar_pop (TV_INTEGRATION);
+ DECL_STRUCT_FUNCTION (decl)->inlinable = inlinable;
+ return true;
+ }
+
+ /* If specified extern inline but we aren't inlining it, we are
+ done. This goes for anything that gets here with DECL_EXTERNAL
+ set, not just things with DECL_INLINE. */
+ return (bool) DECL_EXTERNAL (decl);
+}
+
+/* Try to identify useless null pointer tests and delete them. */
+static void
+rest_of_handle_null_pointer (tree decl, rtx insns)
+{
+ open_dump_file (DFI_null, decl);
+ if (dump_file)
+ dump_flow_info (dump_file);
+
+ if (delete_null_pointer_checks (insns))
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP);
+
+ close_dump_file (DFI_null, print_rtl_with_bb, insns);
+}
+
+/* Try combining insns through substitution. */
+static void
+rest_of_handle_combine (tree decl, rtx insns)
+{
+ int rebuild_jump_labels_after_combine = 0;
+
+ timevar_push (TV_COMBINE);
+ open_dump_file (DFI_combine, decl);
+
+ rebuild_jump_labels_after_combine
+ = combine_instructions (insns, max_reg_num ());
+
+ /* Combining insns may have turned an indirect jump into a
+ direct jump. Rebuild the JUMP_LABEL fields of jumping
+ instructions. */
+ if (rebuild_jump_labels_after_combine)
+ {
+ timevar_push (TV_JUMP);
+ rebuild_jump_labels (insns);
+ timevar_pop (TV_JUMP);
+
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_UPDATE_LIFE);
+ }
+
+ close_dump_file (DFI_combine, print_rtl_with_bb, insns);
+ timevar_pop (TV_COMBINE);
+
+ ggc_collect ();
+}
+
+/* Perform life analysis. */
+static void
+rest_of_handle_life (tree decl, rtx insns)
+{
+ open_dump_file (DFI_life, decl);
+ regclass_init ();
+
+#ifdef ENABLE_CHECKING
+ verify_flow_info ();
+#endif
+ life_analysis (insns, dump_file, PROP_FINAL);
+ if (optimize)
+ cleanup_cfg ((optimize ? CLEANUP_EXPENSIVE : 0) | CLEANUP_UPDATE_LIFE
+ | CLEANUP_LOG_LINKS
+ | (flag_thread_jumps ? CLEANUP_THREADING : 0));
+ timevar_pop (TV_FLOW);
+
+ if (warn_uninitialized)
+ {
+ uninitialized_vars_warning (DECL_INITIAL (decl));
+ if (extra_warnings)
+ setjmp_args_warning ();
+ }
+
+ if (optimize)
+ {
+ if (!flag_new_regalloc && initialize_uninitialized_subregs ())
+ {
+ /* Insns were inserted, and possibly pseudos created, so
+ things might look a bit different. */
+ insns = get_insns ();
+ allocate_reg_life_data ();
+ update_life_info (NULL, UPDATE_LIFE_GLOBAL_RM_NOTES,
+ PROP_LOG_LINKS | PROP_REG_INFO | PROP_DEATH_NOTES);
+ }
+ }
+
+ no_new_pseudos = 1;
+
+ close_dump_file (DFI_life, print_rtl_with_bb, insns);
+
+ ggc_collect ();
+}
+
+/* Perform common subexpression elimination. Nonzero value from
+ `cse_main' means that jumps were simplified and some code may now
+ be unreachable, so do jump optimization again. */
+static void
+rest_of_handle_cse (tree decl, rtx insns)
+{
+ int tem;
+
+ open_dump_file (DFI_cse, decl);
+ if (dump_file)
+ dump_flow_info (dump_file);
+ timevar_push (TV_CSE);
+
+ reg_scan (insns, max_reg_num (), 1);
+
+ tem = cse_main (insns, max_reg_num (), 0, dump_file);
+ if (tem)
+ rebuild_jump_labels (insns);
+ if (purge_all_dead_edges (0))
+ delete_unreachable_blocks ();
+
+ delete_trivially_dead_insns (insns, max_reg_num ());
+
+ /* If we are not running more CSE passes, then we are no longer
+ expecting CSE to be run. But always rerun it in a cheap mode. */
+ cse_not_expected = !flag_rerun_cse_after_loop && !flag_gcse;
+
+ if (tem || optimize > 1)
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP);
+ /* Try to identify useless null pointer tests and delete them. */
+ if (flag_delete_null_pointer_checks)
+ {
+ timevar_push (TV_JUMP);
+
+ if (delete_null_pointer_checks (insns))
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP);
+ timevar_pop (TV_JUMP);
+ }
+
+ /* The second pass of jump optimization is likely to have
+ removed a bunch more instructions. */
+ renumber_insns (dump_file);
+
+ timevar_pop (TV_CSE);
+ close_dump_file (DFI_cse, print_rtl_with_bb, insns);
+}
+
+/* Run second CSE pass after loop optimizations. */
+static void
+rest_of_handle_cse2 (tree decl, rtx insns)
+{
+ int tem;
+
+ timevar_push (TV_CSE2);
+ open_dump_file (DFI_cse2, decl);
+ if (dump_file)
+ dump_flow_info (dump_file);
+ /* CFG is no longer maintained up-to-date. */
+ tem = cse_main (insns, max_reg_num (), 1, dump_file);
+
+ /* Run a pass to eliminate duplicated assignments to condition code
+ registers. We have to run this after bypass_jumps, because it
+ makes it harder for that pass to determine whether a jump can be
+ bypassed safely. */
+ cse_condition_code_reg ();
+
+ purge_all_dead_edges (0);
+ delete_trivially_dead_insns (insns, max_reg_num ());
+
+ if (tem)
+ {
+ timevar_push (TV_JUMP);
+ rebuild_jump_labels (insns);
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+ timevar_pop (TV_JUMP);
+ }
+ reg_scan (insns, max_reg_num (), 0);
+ close_dump_file (DFI_cse2, print_rtl_with_bb, insns);
+ ggc_collect ();
+ timevar_pop (TV_CSE2);
+}
+
+/* Perform global cse. */
+static void
+rest_of_handle_gcse (tree decl, rtx insns)
+{
+ int save_csb, save_cfj;
+ int tem2 = 0, tem;
+
+ timevar_push (TV_GCSE);
+ open_dump_file (DFI_gcse, decl);
+
+ tem = gcse_main (insns, dump_file);
+ rebuild_jump_labels (insns);
+ delete_trivially_dead_insns (insns, max_reg_num ());
+
+ save_csb = flag_cse_skip_blocks;
+ save_cfj = flag_cse_follow_jumps;
+ flag_cse_skip_blocks = flag_cse_follow_jumps = 0;
+
+ /* Instantiate any remaining CONSTANT_P_RTX nodes. */
+ if (current_function_calls_constant_p)
+ purge_builtin_constant_p ();
+
+ /* If -fexpensive-optimizations, re-run CSE to clean up things done
+ by gcse. */
+ if (flag_expensive_optimizations)
+ {
+ timevar_push (TV_CSE);
+ reg_scan (insns, max_reg_num (), 1);
+ tem2 = cse_main (insns, max_reg_num (), 0, dump_file);
+ purge_all_dead_edges (0);
+ delete_trivially_dead_insns (insns, max_reg_num ());
+ timevar_pop (TV_CSE);
+ cse_not_expected = !flag_rerun_cse_after_loop;
+ }
+
+ /* If gcse or cse altered any jumps, rerun jump optimizations to clean
+ things up. Then possibly re-run CSE again. */
+ while (tem || tem2)
+ {
+ tem = tem2 = 0;
+ timevar_push (TV_JUMP);
+ rebuild_jump_labels (insns);
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP);
+ timevar_pop (TV_JUMP);
+
+ if (flag_expensive_optimizations)
+ {
+ timevar_push (TV_CSE);
+ reg_scan (insns, max_reg_num (), 1);
+ tem2 = cse_main (insns, max_reg_num (), 0, dump_file);
+ purge_all_dead_edges (0);
+ delete_trivially_dead_insns (insns, max_reg_num ());
+ timevar_pop (TV_CSE);
+ }
+ }
+
+ close_dump_file (DFI_gcse, print_rtl_with_bb, insns);
+ timevar_pop (TV_GCSE);
+
+ ggc_collect ();
+ flag_cse_skip_blocks = save_csb;
+ flag_cse_follow_jumps = save_cfj;
+#ifdef ENABLE_CHECKING
+ verify_flow_info ();
+#endif
+}
+
+/* Move constant computations out of loops. */
+static void
+rest_of_handle_loop_optimize (tree decl, rtx insns)
+{
+ int do_unroll, do_prefetch;
+
+ timevar_push (TV_LOOP);
+ delete_dead_jumptables ();
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP);
+ open_dump_file (DFI_loop, decl);
+
+ /* CFG is no longer maintained up-to-date. */
+ free_bb_for_insn ();
+
+ if (flag_unroll_loops)
+ do_unroll = LOOP_AUTO_UNROLL; /* Having two unrollers is useless. */
+ else
+ do_unroll = flag_old_unroll_loops ? LOOP_UNROLL : LOOP_AUTO_UNROLL;
+ do_prefetch = flag_prefetch_loop_arrays ? LOOP_PREFETCH : 0;
+
+ if (flag_rerun_loop_opt)
+ {
+ cleanup_barriers ();
+
+ /* We only want to perform unrolling once. */
+ loop_optimize (insns, dump_file, do_unroll);
+ do_unroll = 0;
+
+ /* The first call to loop_optimize makes some instructions
+ trivially dead. We delete those instructions now in the
+ hope that doing so will make the heuristics in loop work
+ better and possibly speed up compilation. */
+ delete_trivially_dead_insns (insns, max_reg_num ());
+
+ /* The regscan pass is currently necessary as the alias
+ analysis code depends on this information. */
+ reg_scan (insns, max_reg_num (), 1);
+ }
+ cleanup_barriers ();
+ loop_optimize (insns, dump_file, do_unroll | LOOP_BCT | do_prefetch);
+
+ /* Loop can create trivially dead instructions. */
+ delete_trivially_dead_insns (insns, max_reg_num ());
+ close_dump_file (DFI_loop, print_rtl, insns);
+ timevar_pop (TV_LOOP);
+ find_basic_blocks (insns, max_reg_num (), dump_file);
+
+ ggc_collect ();
+}
+
+/* Perform loop optimizations. It might be better to do them a bit
+ sooner, but we want the profile feedback to work more
+ efficiently. */
+static void
+rest_of_handle_loop2 (tree decl, rtx insns)
+{
+ struct loops *loops;
+ basic_block bb;
+
+ timevar_push (TV_LOOP);
+ open_dump_file (DFI_loop2, decl);
+ if (dump_file)
+ dump_flow_info (dump_file);
+
+ /* Initialize structures for layout changes. */
+ cfg_layout_initialize ();
+
+ loops = loop_optimizer_init (dump_file);
+
+ if (loops)
+ {
+ /* The optimizations: */
+ if (flag_unswitch_loops)
+ unswitch_loops (loops);
+
+ if (flag_peel_loops || flag_unroll_loops)
+ unroll_and_peel_loops (loops,
+ (flag_peel_loops ? UAP_PEEL : 0) |
+ (flag_unroll_loops ? UAP_UNROLL : 0) |
+ (flag_unroll_all_loops ? UAP_UNROLL_ALL : 0));
+
+ loop_optimizer_finalize (loops, dump_file);
+ }
+
+ /* Finalize layout changes. */
+ FOR_EACH_BB (bb)
+ if (bb->next_bb != EXIT_BLOCK_PTR)
+ bb->rbi->next = bb->next_bb;
+ cfg_layout_finalize ();
+
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+ delete_trivially_dead_insns (insns, max_reg_num ());
+ reg_scan (insns, max_reg_num (), 0);
+ if (dump_file)
+ dump_flow_info (dump_file);
+ close_dump_file (DFI_loop2, print_rtl_with_bb, get_insns ());
+ timevar_pop (TV_LOOP);
+ ggc_collect ();
+}
+
+/* This is called from finish_function (within langhooks.parse_file)
+ after each top-level definition is parsed.
+ It is supposed to compile that function or variable
+ and output the assembler code for it.
+ After we return, the tree storage is freed. */
+
+void
+rest_of_compilation (tree decl)
+{
+ rtx insns;
+
+ timevar_push (TV_REST_OF_COMPILATION);
+
+ /* Register rtl specific functions for cfg. */
+ rtl_register_cfg_hooks ();
+
+ /* Now that we're out of the frontend, we shouldn't have any more
+ CONCATs anywhere. */
+ generating_concat_p = 0;
+
+ /* When processing delayed functions, prepare_function_start() won't
+ have been run to re-initialize it. */
+ cse_not_expected = ! optimize;
+
+ /* First, make sure that NOTE_BLOCK is set correctly for each
+ NOTE_INSN_BLOCK_BEG/NOTE_INSN_BLOCK_END note. */
+ if (!cfun->x_whole_function_mode_p)
+ identify_blocks ();
+
+ /* In function-at-a-time mode, we do not attempt to keep the BLOCK
+ tree in sensible shape. So, we just recalculate it here. */
+ if (cfun->x_whole_function_mode_p)
+ reorder_blocks ();
+
+ init_flow ();
+
+ if (rest_of_handle_inlining (decl))
+ goto exit_rest_of_compilation;
+
+ /* If we're emitting a nested function, make sure its parent gets
+ emitted as well. Doing otherwise confuses debug info. */
+ {
+ tree parent;
+ for (parent = DECL_CONTEXT (current_function_decl);
+ parent != NULL_TREE;
+ parent = get_containing_scope (parent))
+ if (TREE_CODE (parent) == FUNCTION_DECL)
+ TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (parent)) = 1;
+ }
+
+ /* We are now committed to emitting code for this function. Do any
+ preparation, such as emitting abstract debug info for the inline
+ before it gets mangled by optimization. */
+ if (cgraph_function_possibly_inlined_p (decl))
+ (*debug_hooks->outlining_inline_function) (decl);
+
+ /* Remove any notes we don't need. That will make iterating
+ over the instruction sequence faster, and allow the garbage
+ collector to reclaim the memory used by the notes. */
+ remove_unnecessary_notes ();
+ reorder_blocks ();
+
+ ggc_collect ();
+
+ /* Initialize some variables used by the optimizers. */
+ init_function_for_compilation ();
+
+ if (! DECL_DEFER_OUTPUT (decl))
+ TREE_ASM_WRITTEN (decl) = 1;
+
+ /* Now that integrate will no longer see our rtl, we need not
+ distinguish between the return value of this function and the
+ return value of called functions. Also, we can remove all SETs
+ of subregs of hard registers; they are only here because of
+ integrate. Also, we can now initialize pseudos intended to
+ carry magic hard reg data throughout the function. */
+ rtx_equal_function_value_matters = 0;
+ purge_hard_subreg_sets (get_insns ());
+
+ /* Early return if there were errors. We can run afoul of our
+ consistency checks, and there's not really much point in fixing them.
+ Don't return yet if -Wreturn-type; we need to do cleanup_cfg. */
+ if (((rtl_dump_and_exit || flag_syntax_only) && !warn_return_type)
+ || errorcount || sorrycount)
+ goto exit_rest_of_compilation;
+
+ timevar_push (TV_JUMP);
+ open_dump_file (DFI_sibling, decl);
+ insns = get_insns ();
+ rebuild_jump_labels (insns);
+ find_exception_handler_labels ();
+ find_basic_blocks (insns, max_reg_num (), dump_file);
+
+ delete_unreachable_blocks ();
+
+ /* Turn NOTE_INSN_PREDICTIONs into branch predictions. */
+ if (flag_guess_branch_prob)
+ {
+ timevar_push (TV_BRANCH_PROB);
+ note_prediction_to_br_prob ();
+ timevar_pop (TV_BRANCH_PROB);
+ }
+
+ if (flag_optimize_sibling_calls)
+ rest_of_handle_sibling_calls (insns);
+
+ /* We have to issue these warnings now already, because CFG cleanups
+ further down may destroy the required information. However, this
+ must be done after the sibcall optimization pass because the barrier
+ emitted for noreturn calls that are candidate for the optimization
+ is folded into the CALL_PLACEHOLDER until after this pass, so the
+ CFG is inaccurate. */
+ check_function_return_warnings ();
+
+ timevar_pop (TV_JUMP);
+
+ insn_locators_initialize ();
+ /* Complete generation of exception handling code. */
+ if (doing_eh (0))
+ {
+ timevar_push (TV_JUMP);
+ open_dump_file (DFI_eh, decl);
+
+ finish_eh_generation ();
+
+ close_dump_file (DFI_eh, print_rtl, get_insns ());
+ timevar_pop (TV_JUMP);
+ }
+
+ /* Delay emitting hard_reg_initial_value sets until after EH landing pad
+ generation, which might create new sets. */
+ emit_initial_value_sets ();
+
+#ifdef FINALIZE_PIC
+ /* If we are doing position-independent code generation, now
+ is the time to output special prologues and epilogues.
+ We do not want to do this earlier, because it just clutters
+ up inline functions with meaningless insns. */
+ if (flag_pic)
+ FINALIZE_PIC;
+#endif
+
+ insns = get_insns ();
+
+ /* Copy any shared structure that should not be shared. */
+ unshare_all_rtl (current_function_decl, insns);
+
+#ifdef SETJMP_VIA_SAVE_AREA
+ /* This must be performed before virtual register instantiation.
+ Please be aware the everything in the compiler that can look
+ at the RTL up to this point must understand that REG_SAVE_AREA
+ is just like a use of the REG contained inside. */
+ if (current_function_calls_alloca)
+ optimize_save_area_alloca (insns);
+#endif
+
+ /* Instantiate all virtual registers. */
+ instantiate_virtual_regs (current_function_decl, insns);
+
+ open_dump_file (DFI_jump, decl);
+
+ /* Always do one jump optimization pass to ensure that JUMP_LABEL fields
+ are initialized and to compute whether control can drop off the end
+ of the function. */
+
+ timevar_push (TV_JUMP);
+ /* Turn NOTE_INSN_EXPECTED_VALUE into REG_BR_PROB. Do this
+ before jump optimization switches branch directions. */
+ if (flag_guess_branch_prob)
+ expected_value_to_br_prob ();
+
+ reg_scan (insns, max_reg_num (), 0);
+ rebuild_jump_labels (insns);
+ find_basic_blocks (insns, max_reg_num (), dump_file);
+ delete_trivially_dead_insns (insns, max_reg_num ());
+ if (dump_file)
+ dump_flow_info (dump_file);
+ cleanup_cfg ((optimize ? CLEANUP_EXPENSIVE : 0) | CLEANUP_PRE_LOOP
+ | (flag_thread_jumps ? CLEANUP_THREADING : 0));
+
+ if (optimize)
+ {
+ free_bb_for_insn ();
+ copy_loop_headers (insns);
+ find_basic_blocks (insns, max_reg_num (), dump_file);
+ }
+ purge_line_number_notes (insns);
+
+ timevar_pop (TV_JUMP);
+ close_dump_file (DFI_jump, print_rtl, insns);
+
+ /* Now is when we stop if -fsyntax-only and -Wreturn-type. */
+ if (rtl_dump_and_exit || flag_syntax_only || DECL_DEFER_OUTPUT (decl))
+ goto exit_rest_of_compilation;
+
+ timevar_push (TV_JUMP);
+
+ if (optimize)
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP);
+
+ if (flag_delete_null_pointer_checks)
+ rest_of_handle_null_pointer (decl, insns);
+
+ /* Jump optimization, and the removal of NULL pointer checks, may
+ have reduced the number of instructions substantially. CSE, and
+ future passes, allocate arrays whose dimensions involve the
+ maximum instruction UID, so if we can reduce the maximum UID
+ we'll save big on memory. */
+ renumber_insns (dump_file);
+ timevar_pop (TV_JUMP);
+
+ close_dump_file (DFI_jump, print_rtl_with_bb, insns);
+
+ ggc_collect ();
+
+ if (optimize > 0)
+ rest_of_handle_cse (decl, insns);
+
+ rest_of_handle_addressof (decl, insns);
+
+ ggc_collect ();
+
+ if (optimize > 0)
+ {
+ if (flag_gcse)
+ rest_of_handle_gcse (decl, insns);
+
+ if (flag_loop_optimize)
+ rest_of_handle_loop_optimize (decl, insns);
+
+ if (flag_gcse)
+ rest_of_handle_jump_bypass (decl, insns);
+ }
+
+ timevar_push (TV_FLOW);
+
+ rest_of_handle_cfg (decl, insns);
+
+ if (optimize > 0
+ || profile_arc_flag || flag_test_coverage || flag_branch_probabilities)
+ {
+ rest_of_handle_branch_prob (decl, insns);
+
+ if (flag_branch_probabilities
+ && flag_profile_values
+ && flag_value_profile_transformations)
+ rest_of_handle_value_profile_transformations (decl, insns);
+
+ /* Remove the death notes created for vpt. */
+ if (flag_profile_values)
+ count_or_remove_death_notes (NULL, 1);
+ }
+
+ if (optimize > 0)
+ rest_of_handle_if_conversion (decl, insns);
+
+ if (flag_tracer)
+ rest_of_handle_tracer (decl, insns);
+
+ if (optimize > 0
+ && (flag_unswitch_loops
+ || flag_peel_loops
+ || flag_unroll_loops))
+ rest_of_handle_loop2 (decl, insns);
+
+ if (flag_web)
+ rest_of_handle_web (decl, insns);
+
+ if (flag_rerun_cse_after_loop)
+ rest_of_handle_cse2 (decl, insns);
+
+ cse_not_expected = 1;
+
+ rest_of_handle_life (decl, insns);
+
+ if (optimize > 0)
+ rest_of_handle_combine (decl, insns);
+
+ if (flag_if_conversion)
+ rest_of_handle_if_after_combine (decl, insns);
+
+ if (optimize > 0 && (flag_regmove || flag_expensive_optimizations))
+ rest_of_handle_regmove (decl, insns);
+
+ /* Do unconditional splitting before register allocation to allow machine
+ description to add extra information not needed previously. */
+ split_all_insns (1);
+
+#ifdef OPTIMIZE_MODE_SWITCHING
+ timevar_push (TV_MODE_SWITCH);
+
+ no_new_pseudos = 0;
+ optimize_mode_switching (NULL);
+ no_new_pseudos = 1;
+
+ timevar_pop (TV_MODE_SWITCH);
+#endif
+
+ /* Any of the several passes since flow1 will have munged register
+ lifetime data a bit. We need it to be up to date for scheduling
+ (see handling of reg_known_equiv in init_alias_analysis). */
+ recompute_reg_usage (insns, !optimize_size);
+
+#ifdef INSN_SCHEDULING
+ rest_of_handle_sched (decl, insns);
+#endif
+
+ /* Determine if the current function is a leaf before running reload
+ since this can impact optimizations done by the prologue and
+ epilogue thus changing register elimination offsets. */
+ current_function_is_leaf = leaf_function_p ();
+
+ timevar_push (TV_LOCAL_ALLOC);
+ open_dump_file (DFI_lreg, decl);
+
+ if (flag_new_regalloc)
+ {
+ if (rest_of_handle_new_regalloc (decl, insns))
+ goto exit_rest_of_compilation;
+ }
+ else
+ {
+ if (rest_of_handle_old_regalloc (decl, insns))
+ goto exit_rest_of_compilation;
+ }
+
+ ggc_collect ();
+
+ open_dump_file (DFI_postreload, decl);
+
+ /* Do a very simple CSE pass over just the hard registers. */
+ if (optimize > 0)
+ {
+ timevar_push (TV_RELOAD_CSE_REGS);
+ reload_cse_regs (insns);
+ /* reload_cse_regs can eliminate potentially-trapping MEMs.
+ Remove any EH edges associated with them. */
+ if (flag_non_call_exceptions)
+ purge_all_dead_edges (0);
+ timevar_pop (TV_RELOAD_CSE_REGS);
+ }
+
+ close_dump_file (DFI_postreload, print_rtl_with_bb, insns);
+
+ /* Re-create the death notes which were deleted during reload. */
+ timevar_push (TV_FLOW2);
+ open_dump_file (DFI_flow2, decl);
+
+#ifdef ENABLE_CHECKING
+ verify_flow_info ();
+#endif
+
+ /* If optimizing, then go ahead and split insns now. */
+#ifndef STACK_REGS
+ if (optimize > 0)
+#endif
+ split_all_insns (0);
+
+ if (flag_branch_target_load_optimize)
+ {
+ open_dump_file (DFI_branch_target_load, decl);
+
+ branch_target_load_optimize (insns, false);
+
+ close_dump_file (DFI_branch_target_load, print_rtl_with_bb, insns);
+
+ ggc_collect ();
+ }
+
+ if (optimize)
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+
+ /* On some machines, the prologue and epilogue code, or parts thereof,
+ can be represented as RTL. Doing so lets us schedule insns between
+ it and the rest of the code and also allows delayed branch
+ scheduling to operate in the epilogue. */
+ thread_prologue_and_epilogue_insns (insns);
+ epilogue_completed = 1;
+
+ if (optimize)
+ {
+ life_analysis (insns, dump_file, PROP_POSTRELOAD);
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_UPDATE_LIFE
+ | (flag_crossjumping ? CLEANUP_CROSSJUMP : 0));
+
+ /* This is kind of a heuristic. We need to run combine_stack_adjustments
+ even for machines with possibly nonzero RETURN_POPS_ARGS
+ and ACCUMULATE_OUTGOING_ARGS. We expect that only ports having
+ push instructions will have popping returns. */
+#ifndef PUSH_ROUNDING
+ if (!ACCUMULATE_OUTGOING_ARGS)
+#endif
+ combine_stack_adjustments ();
+
+ ggc_collect ();
+ }
+
+ flow2_completed = 1;
+
+ close_dump_file (DFI_flow2, print_rtl_with_bb, insns);
+ timevar_pop (TV_FLOW2);
+
+#ifdef HAVE_peephole2
+ if (optimize > 0 && flag_peephole2)
+ {
+ timevar_push (TV_PEEPHOLE2);
+ open_dump_file (DFI_peephole2, decl);
+
+ peephole2_optimize (dump_file);
+
+ close_dump_file (DFI_peephole2, print_rtl_with_bb, insns);
+ timevar_pop (TV_PEEPHOLE2);
+ }
+#endif
+
+ open_dump_file (DFI_ce3, decl);
+ if (optimize)
+ /* Last attempt to optimize CFG, as scheduling, peepholing and insn
+ splitting possibly introduced more crossjumping opportunities. */
+ cleanup_cfg (CLEANUP_EXPENSIVE
+ | CLEANUP_UPDATE_LIFE
+ | (flag_crossjumping ? CLEANUP_CROSSJUMP : 0));
+ if (flag_if_conversion2)
+ {
+ timevar_push (TV_IFCVT2);
+
+ if_convert (1);
+
+ timevar_pop (TV_IFCVT2);
+ }
+ close_dump_file (DFI_ce3, print_rtl_with_bb, insns);
+
+ if (optimize > 0)
+ {
+ if (flag_rename_registers || flag_cprop_registers)
+ rest_of_handle_regrename (decl, insns);
+
+ rest_of_handle_reorder_blocks (decl, insns);
+ }
+
+ if (flag_branch_target_load_optimize2)
+ {
+ /* Leave this a warning for now so that it is possible to experiment
+ with running this pass twice. In 3.6, we should either make this
+ an error, or use separate dump files. */
+ if (flag_branch_target_load_optimize)
+ warning ("branch target register load optimization is not intended "
+ "to be run twice");
+
+ open_dump_file (DFI_branch_target_load, decl);
+
+ branch_target_load_optimize (insns, true);
+
+ close_dump_file (DFI_branch_target_load, print_rtl_with_bb, insns);
+
+ ggc_collect ();
+ }
+
+#ifdef INSN_SCHEDULING
+ if (optimize > 0 && flag_schedule_insns_after_reload)
+ rest_of_handle_sched2 (decl, insns);
+#endif
+
+#ifdef LEAF_REGISTERS
+ current_function_uses_only_leaf_regs
+ = optimize > 0 && only_leaf_regs_used () && leaf_function_p ();
+#endif
+
+#ifdef STACK_REGS
+ rest_of_handle_stack_regs (decl, insns);
+#endif
+
+ compute_alignments ();
+
+ if (flag_var_tracking)
+ rest_of_handle_variable_tracking (decl, insns);
+
+ /* CFG is no longer maintained up-to-date. */
+ free_bb_for_insn ();
+
+ if (targetm.machine_dependent_reorg != 0)
+ rest_of_handle_machine_reorg (decl, insns);
+
+ purge_line_number_notes (insns);
+ cleanup_barriers ();
+
+#ifdef DELAY_SLOTS
+ if (optimize > 0 && flag_delayed_branch)
+ rest_of_handle_delay_slots (decl, insns);
+#endif
+
+#if defined (HAVE_ATTR_length) && !defined (STACK_REGS)
+ timevar_push (TV_SHORTEN_BRANCH);
+ split_all_insns_noflow ();
+ timevar_pop (TV_SHORTEN_BRANCH);
+#endif
+
+ convert_to_eh_region_ranges ();
+
+ /* Shorten branches. */
+ timevar_push (TV_SHORTEN_BRANCH);
+ shorten_branches (get_insns ());
+ timevar_pop (TV_SHORTEN_BRANCH);
+
+ set_nothrow_function_flags ();
+ if (current_function_nothrow)
+ /* Now we know that this can't throw; set the flag for the benefit
+ of other functions later in this translation unit. */
+ TREE_NOTHROW (current_function_decl) = 1;
+
+ rest_of_handle_final (decl, insns);
+
+ /* Write DBX symbols if requested. */
+
+ /* Note that for those inline functions where we don't initially
+ know for certain that we will be generating an out-of-line copy,
+ the first invocation of this routine (rest_of_compilation) will
+ skip over this code by doing a `goto exit_rest_of_compilation;'.
+ Later on, wrapup_global_declarations will (indirectly) call
+ rest_of_compilation again for those inline functions that need
+ to have out-of-line copies generated. During that call, we
+ *will* be routed past here. */
+
+ timevar_push (TV_SYMOUT);
+ (*debug_hooks->function_decl) (decl);
+ timevar_pop (TV_SYMOUT);
+
+ exit_rest_of_compilation:
+
+ coverage_end_function ();
+
+ /* In case the function was not output,
+ don't leave any temporary anonymous types
+ queued up for sdb output. */
+#ifdef SDB_DEBUGGING_INFO
+ if (write_symbols == SDB_DEBUG)
+ sdbout_types (NULL_TREE);
+#endif
+
+ reload_completed = 0;
+ epilogue_completed = 0;
+ flow2_completed = 0;
+ no_new_pseudos = 0;
+
+ timevar_push (TV_FINAL);
+
+ /* Clear out the insn_length contents now that they are no
+ longer valid. */
+ init_insn_lengths ();
+
+ /* Show no temporary slots allocated. */
+ init_temp_slots ();
+
+ free_basic_block_vars (0);
+ free_bb_for_insn ();
+
+ timevar_pop (TV_FINAL);
+
+ if ((*targetm.binds_local_p) (current_function_decl))
+ {
+ int pref = cfun->preferred_stack_boundary;
+ if (cfun->recursive_call_emit
+ && cfun->stack_alignment_needed > cfun->preferred_stack_boundary)
+ pref = cfun->stack_alignment_needed;
+ cgraph_rtl_info (current_function_decl)->preferred_incoming_stack_boundary
+ = pref;
+ }
+
+ /* Make sure volatile mem refs aren't considered valid operands for
+ arithmetic insns. We must call this here if this is a nested inline
+ function, since the above code leaves us in the init_recog state
+ (from final.c), and the function context push/pop code does not
+ save/restore volatile_ok.
+
+ ??? Maybe it isn't necessary for expand_start_function to call this
+ anymore if we do it here? */
+
+ init_recog_no_volatile ();
+
+ /* We're done with this function. Free up memory if we can. */
+ free_after_parsing (cfun);
+ if (! DECL_DEFER_OUTPUT (decl))
+ {
+ free_after_compilation (cfun);
+ DECL_STRUCT_FUNCTION (decl) = 0;
+ }
+ cfun = 0;
+
+ ggc_collect ();
+
+ timevar_pop (TV_REST_OF_COMPILATION);
+}
+
+void
+init_optimization_passes (void)
+{
+ if (flag_unit_at_a_time)
+ {
+ open_dump_file (DFI_cgraph, NULL);
+ cgraph_dump_file = dump_file;
+ dump_file = NULL;
+ }
+}
+
+void
+finish_optimization_passes (void)
+{
+ if (profile_arc_flag || flag_test_coverage || flag_branch_probabilities)
+ {
+ timevar_push (TV_DUMP);
+ open_dump_file (DFI_bp, NULL);
+
+ end_branch_prob ();
+
+ close_dump_file (DFI_bp, NULL, NULL_RTX);
+ timevar_pop (TV_DUMP);
+ }
+
+ if (optimize > 0 && open_dump_file (DFI_combine, NULL))
+ {
+ timevar_push (TV_DUMP);
+ dump_combine_total_stats (dump_file);
+ close_dump_file (DFI_combine, NULL, NULL_RTX);
+ timevar_pop (TV_DUMP);
+ }
+
+ if (flag_unit_at_a_time)
+ {
+ dump_file = cgraph_dump_file;
+ cgraph_dump_file = NULL;
+ close_dump_file (DFI_cgraph, NULL, NULL_RTX);
+ }
+
+ /* Do whatever is necessary to finish printing the graphs. */
+ if (graph_dump_format != no_graph)
+ {
+ int i;
+
+ for (i = 0; i < (int) DFI_MAX; ++i)
+ if (dump_file_tbl[i].initialized && dump_file_tbl[i].graph_dump_p)
+ {
+ char seq[16];
+ char *suffix;
+
+ sprintf (seq, DUMPFILE_FORMAT, i);
+ suffix = concat (seq, dump_file_tbl[i].extension, NULL);
+ finish_graph_dump_file (dump_base_name, suffix);
+ free (suffix);
+ }
+ }
+
+}
+
+bool
+enable_rtl_dump_file (int letter)
+{
+ bool matched = false;
+ int i;
+
+ if (letter == 'a')
+ {
+ for (i = 0; i < (int) DFI_MAX; ++i)
+ dump_file_tbl[i].enabled = 1;
+ matched = true;
+ }
+ else
+ {
+ for (i = 0; i < (int) DFI_MAX; ++i)
+ if (letter == dump_file_tbl[i].debug_switch)
+ {
+ dump_file_tbl[i].enabled = 1;
+ matched = true;
+ }
+ }
+
+ return matched;
+}
diff --git a/gcc/testsuite/g++.dg/abi/mangle21.C b/gcc/testsuite/g++.dg/abi/mangle21.C
new file mode 100644
index 00000000000..f457d600cd8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle21.C
@@ -0,0 +1,13 @@
+// PR c++/14324
+// { dg-do assemble }
+
+extern "C" {
+
+void fun1(void)
+{
+ do { static int xyz __attribute__((unused)) = 1; } while (0);
+ do { static int xyz __attribute__((unused)) = 2; } while (0);
+ do { static int xyz __attribute__((unused)) = 3; } while (0);
+}
+
+}
diff --git a/gcc/testsuite/g++.dg/charset/asm1.c b/gcc/testsuite/g++.dg/charset/asm1.c
new file mode 100644
index 00000000000..9c0ff2866b0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/charset/asm1.c
@@ -0,0 +1,14 @@
+/* { dg-do compile { target *-*-* } }
+ { dg-require-iconv "IBM-1047" }
+ { dg-final { scan-assembler ".ascii bar" } }
+ { dg-final { scan-assembler ".ascii foo" } }
+ */
+extern int x, y;
+
+asm (".ascii bar");
+
+int foo (void)
+{
+ __asm__ (".ascii foo");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/charset/asm2.c b/gcc/testsuite/g++.dg/charset/asm2.c
new file mode 100644
index 00000000000..8d8dbbb524d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/charset/asm2.c
@@ -0,0 +1,33 @@
+/* Test for complex asm statements. Make sure it compiles
+ then test for some of the asm statements not being translated. */
+/* { dg-do compile { target i?86-*-* } }
+ { dg-require-iconv "IBM-1047" }
+ { dg-final { scan-assembler "std" } }
+ { dg-final { scan-assembler "cld" } }
+ { dg-final { scan-assembler "rep" } }
+ { dg-final { scan-assembler "movsb" } } */
+#define size_t int
+void *
+memmove (void *__dest, __const void *__src, size_t __n)
+{
+ register unsigned long int __d0, __d1, __d2;
+ if (__dest < __src)
+ __asm__ __volatile__
+ ("cld\n\t"
+ "rep\n\t"
+ "movsb"
+ : "=&c" (__d0), "=&S" (__d1), "=&D" (__d2)
+ : "0" (__n), "1" (__src), "2" (__dest)
+ : "memory");
+ else
+ __asm__ __volatile__
+ ("std\n\t"
+ "rep\n\t"
+ "movsb\n\t"
+ "cld"
+ : "=&c" (__d0), "=&S" (__d1), "=&D" (__d2)
+ : "0" (__n), "1" (__n - 1 + (const char *) __src),
+ "2" (__n - 1 + (char *) __dest)
+ : "memory");
+ return __dest;
+}
diff --git a/gcc/testsuite/g++.dg/charset/asm3.c b/gcc/testsuite/g++.dg/charset/asm3.c
new file mode 100644
index 00000000000..cd850c3e81f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/charset/asm3.c
@@ -0,0 +1,10 @@
+/* Simple asm test. */
+/* { dg-do compile }
+ { dg-require-iconv "IBM-1047" }
+ { dg-final { scan-assembler "foo" } } */
+extern int bar;
+
+int main (void)
+{
+ asm ("foo %0" : "=r" (bar));
+}
diff --git a/gcc/testsuite/g++.dg/charset/asm4.c b/gcc/testsuite/g++.dg/charset/asm4.c
new file mode 100644
index 00000000000..fa93f40fdaf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/charset/asm4.c
@@ -0,0 +1,8 @@
+/* Test for string translation. */
+/* { dg-do compile }
+ { dg-require-iconv "IBM-1047" }
+ { dg-final { scan-assembler-not "translate" } } */
+void foo (void)
+{
+ asm ("xx" : : "r"("translate") : "cc");
+}
diff --git a/gcc/testsuite/g++.dg/charset/attribute1.c b/gcc/testsuite/g++.dg/charset/attribute1.c
new file mode 100644
index 00000000000..993c7934c80
--- /dev/null
+++ b/gcc/testsuite/g++.dg/charset/attribute1.c
@@ -0,0 +1,10 @@
+/* Test for attribute non-translation. */
+/* { dg-do compile }
+ { dg-require-iconv "IBM-1047" }
+ { dg-final { scan-assembler "foo" } } */
+int walrus __attribute__ ((section (".foo")));
+
+int main (void)
+{
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/charset/attribute2.c b/gcc/testsuite/g++.dg/charset/attribute2.c
new file mode 100644
index 00000000000..3cb766aa63a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/charset/attribute2.c
@@ -0,0 +1,8 @@
+/* Test to make sure that invalid attributes aren't translated.
+ If error recovery is ever testable then "foobar" should be
+ translated. */
+/* { dg-do compile }
+ { dg-require-iconv "IBM-1047" }
+*/
+int foo __attribute__ ((walrus)); /* { dg-error "walrus" "ignored" } */
+char x[] = "foobar";
diff --git a/gcc/testsuite/g++.dg/charset/extern1.cc b/gcc/testsuite/g++.dg/charset/extern1.cc
new file mode 100644
index 00000000000..2a68ab7cf78
--- /dev/null
+++ b/gcc/testsuite/g++.dg/charset/extern1.cc
@@ -0,0 +1,15 @@
+/* Test extern statments not being translated. */
+/* { dg-do compile }
+ { dg-require-iconv "IBM-1047" }
+*/
+
+extern "C" {
+
+
+int testbug (void) {
+
+ return 0;
+
+}
+
+} //extern block
diff --git a/gcc/testsuite/g++.dg/charset/extern2.cc b/gcc/testsuite/g++.dg/charset/extern2.cc
new file mode 100644
index 00000000000..82157a6fea4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/charset/extern2.cc
@@ -0,0 +1,5 @@
+/* Check that we push the declaration and then continue translation. */
+/* { dg-do compile }
+ { dg-require-iconv "IBM-1047" }
+ { dg-final { scan-assembler-not "foobar" } } */
+extern "C" { char *foo = "foobar"; }
diff --git a/gcc/testsuite/g++.dg/charset/string.c b/gcc/testsuite/g++.dg/charset/string.c
new file mode 100644
index 00000000000..375e28a2ed6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/charset/string.c
@@ -0,0 +1,5 @@
+/* Simple character translation test. */
+/* { dg-do compile }
+ { dg-require-iconv "IBM-1047" }
+ { dg-final { scan-assembler-not "string foobar" } } */
+char *foo = "string foobar";
diff --git a/gcc/testsuite/g++.dg/debug/crash1.C b/gcc/testsuite/g++.dg/debug/crash1.C
new file mode 100644
index 00000000000..4fba256b7cc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/crash1.C
@@ -0,0 +1,17 @@
+template <typename T>
+class foo
+{
+ T t;
+};
+
+class bar;
+typedef foo<bar> foobar;
+
+class obj
+{
+ virtual foobar* yeah() = 0;
+};
+
+class bar : virtual public obj
+{
+};
diff --git a/gcc/testsuite/g++.dg/expr/crash2.C b/gcc/testsuite/g++.dg/expr/crash2.C
new file mode 100644
index 00000000000..5379bb159d1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/expr/crash2.C
@@ -0,0 +1,14 @@
+// PR c++/14267
+
+class foo {
+public: static int& x;
+};
+int temp;
+int& foo::x=temp;
+
+int main() {
+ int x = 3;
+ &foo::x = x; // { dg-error "" }
+ return 0;
+}
+
diff --git a/gcc/testsuite/g++.dg/ext/altivec-3.C b/gcc/testsuite/g++.dg/ext/altivec-3.C
new file mode 100644
index 00000000000..a98d4a73041
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/altivec-3.C
@@ -0,0 +1,128 @@
+/* { dg-do run { target powerpc*-*-* } } */
+/* { dg-options "-maltivec" } */
+
+/* Test for correct handling of AltiVec constants passed
+ through '...' (va_arg). */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <altivec.h>
+#include "altivec_check.h"
+
+#define CHECK_INVARIANT(expr) \
+ if (!(expr)) { \
+ printf ("ASSERT FAILED: %d: %s\n", __LINE__, #expr); \
+ abort (); \
+ }
+
+struct foo { int x; int y; };
+struct vfoo { int x; __vector signed int v; int y; };
+union u { __vector signed int v; signed int i[4]; };
+
+struct foo x_g = { 3, 4};
+struct vfoo vx_g = { 10, (vector signed int) {11, 12, 13, 14}, 15 };
+__vector signed int v_g = (vector signed int) {22, 23, 24, 25};
+struct vfoo vx2_g = { 30, (vector signed int) {31, 32, 33, 34}, 35 };
+__vector signed int v2_g = (vector signed int) {40, 41, 42, 43};
+int i_1 = 99, i_2 = 33;
+double d_2 = 1.5, d_3 = 1.75;
+double ld_1 = 1.25;
+
+void bar (int i, ... )
+{
+ struct foo xi;
+ double d;
+ double ld;
+ float f;
+ char c;
+ short s;
+ va_list ap;
+ va_start(ap, i);
+ xi = va_arg(ap, struct foo);
+ s = (short)va_arg(ap, int);
+ f = (float)va_arg(ap, double);
+ ld = va_arg(ap, double);
+ c = (char)va_arg(ap, int);
+ d = va_arg(ap, double);
+ va_end(ap);
+
+ CHECK_INVARIANT (xi.x == x_g.x && xi.y == x_g.y);
+ CHECK_INVARIANT (s == (short)i_2);
+ CHECK_INVARIANT (f == (float)d_2);
+ CHECK_INVARIANT (ld == ld_1);
+ CHECK_INVARIANT (c == (char)i_1);
+ CHECK_INVARIANT (d == d_3);
+}
+
+void baz (int i, ... )
+{
+ struct vfoo vx, vx2;
+ __vector signed int v_i, v2_i;
+ int j, k, l;
+ va_list ap;
+ va_start(ap, i);
+ v_i = va_arg(ap, __vector signed int);
+ j = va_arg(ap, int);
+ vx = va_arg(ap, struct vfoo);
+ k = va_arg(ap, int);
+ v2_i = va_arg(ap, __vector signed int);
+ l = va_arg(ap, int);
+ vx2 = va_arg(ap, struct vfoo);
+ va_end(ap);
+
+ CHECK_INVARIANT (vec_all_eq (v_i, v_g));
+ CHECK_INVARIANT (j == i_1);
+ CHECK_INVARIANT (vx.x == vx_g.x && vec_all_eq(vx.v, vx_g.v) && vx.y == vx_g.y);
+ CHECK_INVARIANT (k == i_1);
+ CHECK_INVARIANT (vec_all_eq (v2_i, v2_g));
+ CHECK_INVARIANT (l == i_1);
+ CHECK_INVARIANT (vx2.x == vx2_g.x && vec_all_eq(vx2.v, vx2_g.v) && vx2.y == vx2_g.y);
+}
+
+void quux (int i, ... )
+{
+ __vector signed int v_i, v2_i;
+ union u vi, v2i;
+ va_list ap;
+ va_start(ap, i);
+ v_i = va_arg(ap, __vector signed int);
+ v2_i = va_arg(ap, __vector signed int);
+ va_end(ap);
+ vi.v = v_i;
+ v2i.v = v2_i;
+
+ CHECK_INVARIANT (vec_all_eq (v_i, v_g));
+ CHECK_INVARIANT (vec_all_eq (v2_i, v_g));
+ CHECK_INVARIANT (vec_all_eq (vi.v, v_g));
+ CHECK_INVARIANT (vec_all_eq (v2i.v, v_g));
+}
+
+void baz2 (int i, ... )
+{
+ struct vfoo vx;
+ union u vxi;
+ va_list ap;
+ va_start(ap, i);
+ vx = va_arg(ap, struct vfoo);
+ va_end(ap);
+ vxi.v = vx.v;
+
+ CHECK_INVARIANT (vx.x == vx_g.x && vec_all_eq(vx.v, vx_g.v) && vx.y == vx_g.y);
+ CHECK_INVARIANT (vec_all_eq (vxi.v, vx_g.v));
+}
+
+int main(void)
+{
+ CHECK_INVARIANT (sizeof(struct foo) == 8 && sizeof(struct vfoo) == 48);
+
+ altivec_check();
+
+ bar(i_1, x_g, (short)i_2, (float)d_2, ld_1, (char)i_1, d_3);
+ baz(i_1, v_g, i_1, vx_g, i_1, v2_g, i_1, vx2_g);
+ quux(i_1, v_g, v_g);
+ baz2(i_1, vx_g);
+
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/parse/comma1.C b/gcc/testsuite/g++.dg/parse/comma1.C
new file mode 100644
index 00000000000..33d222cbc04
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/comma1.C
@@ -0,0 +1,13 @@
+// PR c++/14278
+
+struct X {
+ X (int p);
+};
+
+struct A {
+ A(X);
+};
+
+void *p_fun;
+
+A a(X ((*(int (*)(int, int)) p_fun)(0, 0)));
diff --git a/gcc/testsuite/g++.dg/parse/constructor2.C b/gcc/testsuite/g++.dg/parse/constructor2.C
new file mode 100644
index 00000000000..e514e9397e9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/constructor2.C
@@ -0,0 +1,11 @@
+// PR c++/14260
+
+template <class TClass>
+class T
+{
+public:
+ T(short,short f=0) {}
+ T<TClass>(int f) {}
+ T<TClass>(int f=0,const char* b=0) {}
+};
+
diff --git a/gcc/testsuite/g++.dg/parse/defarg7.C b/gcc/testsuite/g++.dg/parse/defarg7.C
new file mode 100644
index 00000000000..c1f75ce8f18
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/defarg7.C
@@ -0,0 +1,5 @@
+// PR c++/14361
+
+class A {
+ A ( int n=0 int n ); // { dg-error "" }
+};
diff --git a/gcc/testsuite/g++.dg/template/cond4.C b/gcc/testsuite/g++.dg/template/cond4.C
new file mode 100644
index 00000000000..35416ba798f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/cond4.C
@@ -0,0 +1,20 @@
+// PR c++/14369
+
+struct A { };
+
+template<class T>
+struct X : A {
+ const A* bar() const
+ { return this; }
+
+ const A& foo() const;
+};
+
+template<class T>
+const A& X<T>::foo() const
+{
+ const A* t = bar();
+ return *(t ? t : throw 0);
+}
+
+
diff --git a/gcc/testsuite/g++.dg/template/friend26.C b/gcc/testsuite/g++.dg/template/friend26.C
new file mode 100644
index 00000000000..3cf659df402
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/friend26.C
@@ -0,0 +1,15 @@
+// PR c++/14359
+
+template<typename> struct A {};
+
+template<typename> struct B
+{
+ template<typename T> friend void foo(const A<T>& a, const B&) { a; }
+};
+
+void bar()
+{
+ A<void> a;
+ B<void> b;
+ foo(a,b);
+}
diff --git a/gcc/testsuite/g++.dg/template/sfinae1.C b/gcc/testsuite/g++.dg/template/sfinae1.C
new file mode 100644
index 00000000000..47db4115452
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/sfinae1.C
@@ -0,0 +1,21 @@
+// PR c++/14337
+
+template <bool> struct Constraint;
+template <> struct Constraint<true> { typedef int Result; };
+
+template <typename T>
+struct IsInt { static const bool value = false; };
+
+template <>
+struct IsInt<int> { static const bool value = true; };
+
+template <typename T>
+typename Constraint<IsInt<T>::value>::Result foo(T);
+
+template <typename T>
+typename Constraint<!IsInt<T>::value>::Result foo(T);
+
+template <typename>
+void bar() {
+ foo(1);
+}
diff --git a/gcc/testsuite/g++.dg/template/ttp8.C b/gcc/testsuite/g++.dg/template/ttp8.C
new file mode 100644
index 00000000000..99f99b965a1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/ttp8.C
@@ -0,0 +1,16 @@
+// { dg-do compile }
+// Contributed by: Niall Douglas <s_gccbugzilla at netprod dot com>
+// PR c++/14284: Failure to select specialization
+
+template<typename> struct S;
+template<template<class> class> struct I {};
+
+template<class, int> struct Match;
+
+template<template<class> class C>
+struct Match<I<C>, 0> {};
+
+template<template<class> class C, int i>
+struct Match<I<C>, i>;
+
+Match<I<S>, 0> v;
diff --git a/gcc/testsuite/g++.dg/warn/Wunused-6.C b/gcc/testsuite/g++.dg/warn/Wunused-6.C
new file mode 100644
index 00000000000..58a3f642bc3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wunused-6.C
@@ -0,0 +1,11 @@
+/* PR middle-end/14203 */
+/* { dg-do compile } */
+/* { dg-options "-Wall" } */
+
+void foo()
+{
+ if (false)
+ if (int i=0) // { dg-warning "unused" "" }
+ int j=0; // { dg-warning "unused" "" }
+}
+
diff --git a/gcc/testsuite/gcc.dg/always_inline2.c b/gcc/testsuite/gcc.dg/always_inline2.c
new file mode 100644
index 00000000000..fa6528d1fac
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/always_inline2.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-options "-Winline -O2" } */
+inline __attribute__ ((always_inline)) void t(void); /* { dg-error "body not available" "" } */
+void
+q(void)
+{
+ t(); /* { dg-error "called from here" "" } */
+}
diff --git a/gcc/testsuite/gcc.dg/always_inline3.c b/gcc/testsuite/gcc.dg/always_inline3.c
new file mode 100644
index 00000000000..b183770adc0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/always_inline3.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-options "-Winline -O2" } */
+inline __attribute__ ((always_inline)) void
+q2(void)
+{ /* { dg-error "recursive" "" } */
+ q2(); /* { dg-error "called from here" "" } */
+ q2(); /* { dg-error "called from here" "" } */
+}
diff --git a/gcc/testsuite/gcc.dg/attr-alias-1.c b/gcc/testsuite/gcc.dg/attr-alias-1.c
new file mode 100644
index 00000000000..8f530a84ce0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-alias-1.c
@@ -0,0 +1,8 @@
+/* PR c++/12795 */
+/* { dg-require-alias "" } */
+
+void foo()
+{
+ extern void bar () __attribute__ ((__alias__ ("BAR"))); /* { dg-warning "ignored" } */
+ bar ();
+}
diff --git a/gcc/testsuite/gcc.dg/charset/asm1.c b/gcc/testsuite/gcc.dg/charset/asm1.c
new file mode 100644
index 00000000000..d7578d418f6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/charset/asm1.c
@@ -0,0 +1,14 @@
+/* { dg-do compile }
+ { dg-require-iconv "IBM-1047" }
+ { dg-final { scan-assembler ".ascii bar" } }
+ { dg-final { scan-assembler ".ascii foo" } }
+ */
+extern int x, y;
+
+asm (".ascii bar");
+
+int foo (void)
+{
+ __asm__ (".ascii foo");
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/charset/asm3.c b/gcc/testsuite/gcc.dg/charset/asm3.c
new file mode 100644
index 00000000000..8d8dbbb524d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/charset/asm3.c
@@ -0,0 +1,33 @@
+/* Test for complex asm statements. Make sure it compiles
+ then test for some of the asm statements not being translated. */
+/* { dg-do compile { target i?86-*-* } }
+ { dg-require-iconv "IBM-1047" }
+ { dg-final { scan-assembler "std" } }
+ { dg-final { scan-assembler "cld" } }
+ { dg-final { scan-assembler "rep" } }
+ { dg-final { scan-assembler "movsb" } } */
+#define size_t int
+void *
+memmove (void *__dest, __const void *__src, size_t __n)
+{
+ register unsigned long int __d0, __d1, __d2;
+ if (__dest < __src)
+ __asm__ __volatile__
+ ("cld\n\t"
+ "rep\n\t"
+ "movsb"
+ : "=&c" (__d0), "=&S" (__d1), "=&D" (__d2)
+ : "0" (__n), "1" (__src), "2" (__dest)
+ : "memory");
+ else
+ __asm__ __volatile__
+ ("std\n\t"
+ "rep\n\t"
+ "movsb\n\t"
+ "cld"
+ : "=&c" (__d0), "=&S" (__d1), "=&D" (__d2)
+ : "0" (__n), "1" (__n - 1 + (const char *) __src),
+ "2" (__n - 1 + (char *) __dest)
+ : "memory");
+ return __dest;
+}
diff --git a/gcc/testsuite/gcc.dg/charset/asm4.c b/gcc/testsuite/gcc.dg/charset/asm4.c
new file mode 100644
index 00000000000..cd850c3e81f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/charset/asm4.c
@@ -0,0 +1,10 @@
+/* Simple asm test. */
+/* { dg-do compile }
+ { dg-require-iconv "IBM-1047" }
+ { dg-final { scan-assembler "foo" } } */
+extern int bar;
+
+int main (void)
+{
+ asm ("foo %0" : "=r" (bar));
+}
diff --git a/gcc/testsuite/gcc.dg/charset/asm5.c b/gcc/testsuite/gcc.dg/charset/asm5.c
new file mode 100644
index 00000000000..fa93f40fdaf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/charset/asm5.c
@@ -0,0 +1,8 @@
+/* Test for string translation. */
+/* { dg-do compile }
+ { dg-require-iconv "IBM-1047" }
+ { dg-final { scan-assembler-not "translate" } } */
+void foo (void)
+{
+ asm ("xx" : : "r"("translate") : "cc");
+}
diff --git a/gcc/testsuite/gcc.dg/charset/attribute1.c b/gcc/testsuite/gcc.dg/charset/attribute1.c
new file mode 100644
index 00000000000..993c7934c80
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/charset/attribute1.c
@@ -0,0 +1,10 @@
+/* Test for attribute non-translation. */
+/* { dg-do compile }
+ { dg-require-iconv "IBM-1047" }
+ { dg-final { scan-assembler "foo" } } */
+int walrus __attribute__ ((section (".foo")));
+
+int main (void)
+{
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/charset/attribute2.c b/gcc/testsuite/gcc.dg/charset/attribute2.c
new file mode 100644
index 00000000000..4ce95a51f84
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/charset/attribute2.c
@@ -0,0 +1,8 @@
+/* Test to make sure that invalid attributes aren't translated.
+ If error recovery is ever testable then "foobar" should be
+ translated. */
+/* { dg-do compile }
+ { dg-require-iconv "IBM-1047" }
+ */
+int foo __attribute__ ((walrus)); /* { dg-error "walrus" "ignored" } */
+char x[] = "foobar";
diff --git a/gcc/testsuite/gcc.dg/charset/charset.exp b/gcc/testsuite/gcc.dg/charset/charset.exp
new file mode 100644
index 00000000000..ad75cb55af8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/charset/charset.exp
@@ -0,0 +1,44 @@
+# Copyright (C) 2004 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+# GCC testsuite that uses the 'dg.exp' driver.
+
+# There's a bunch of headers we need.
+if [is_remote host] {
+ foreach header [glob -nocomplain $srcdir/$subdir/*.{h,def} ] {
+ remote_download host $header
+ }
+}
+
+# Load support procs.
+load_lib gcc-dg.exp
+load_lib target-supports.exp
+
+# If a testcase doesn't have special options, use these.
+global DEFAULT_CHARSETCFLAGS
+if ![info exists DEFAULT_CHARSETCFLAGS] then {
+ set DEFAULT_CHARSETCFLAGS "-fexec-charset=IBM-1047"
+}
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.{c,S} ]] \
+ "" $DEFAULT_CHARSETCFLAGS
+
+# All done.
+dg-finish
diff --git a/gcc/testsuite/gcc.dg/charset/string.c b/gcc/testsuite/gcc.dg/charset/string.c
new file mode 100644
index 00000000000..375e28a2ed6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/charset/string.c
@@ -0,0 +1,5 @@
+/* Simple character translation test. */
+/* { dg-do compile }
+ { dg-require-iconv "IBM-1047" }
+ { dg-final { scan-assembler-not "string foobar" } } */
+char *foo = "string foobar";
diff --git a/gcc/testsuite/gcc.dg/fixuns-trunc-1.c b/gcc/testsuite/gcc.dg/fixuns-trunc-1.c
new file mode 100644
index 00000000000..0d094295249
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/fixuns-trunc-1.c
@@ -0,0 +1,31 @@
+/* { dg-do run } */
+/* { dg-options "-std=c99" } */
+
+unsigned long foo(double d)
+{
+ return (unsigned long) d;
+}
+
+extern void abort(void);
+
+int main(void)
+{
+ double d;
+ unsigned long l;
+
+#ifdef __LP64__
+ d = 9223372036854775808.7;
+ l = 1LL << 63;
+
+ if (foo(d) != l)
+ abort();
+#endif
+
+ d = 122485.2;
+ l = 122485;
+
+ if (foo(d) != l)
+ abort();
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/inline-5.c b/gcc/testsuite/gcc.dg/inline-5.c
new file mode 100644
index 00000000000..d72fad65610
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/inline-5.c
@@ -0,0 +1,13 @@
+/* PR middle-end/13448 */
+
+/* { dg-options "-O3" } */
+
+void funct (const int n)
+{
+ n++; /* { dg-error "" } */
+}
+
+int main () {
+ funct (1);
+ return 0;
+}
diff --git a/intl/configure.ac b/intl/configure.ac
new file mode 100644
index 00000000000..0f54336ba86
--- /dev/null
+++ b/intl/configure.ac
@@ -0,0 +1,33 @@
+AC_PREREQ(2.57)
+AC_INIT
+AC_CONFIG_SRCDIR(gettext.c)
+AC_CONFIG_HEADER(config.h)
+AM_GNU_GETTEXT_VERSION(0.12.1)
+AM_GNU_GETTEXT
+
+# This replaces the extensive use of DEFS in the original Makefile.in.
+AC_DEFINE(IN_LIBINTL, 1, [Define because this is libintl.])
+AC_DEFINE(IN_LIBRARY, 1, [Define because this is a library.])
+AC_DEFINE(DEPENDS_ON_LIBICONV, 1, [Define because we depend on libiconv.])
+AC_DEFINE(ENABLE_RELOCATABLE, 1, [Define to enable relocation.])
+AC_DEFINE(NO_XMALLOC, 1, [Define if there is no xmalloc.])
+AC_DEFINE(set_relocation_prefix, libintl_set_relocation_prefix,
+[Define this entry point correctly.])
+AC_DEFINE(relocate, libintl_relocate,
+[Define this entry point correctly.])
+
+# Additional info for config.intl.
+AC_SUBST(LIBINTL_DEP)
+AC_SUBST(INCINTL)
+
+LIBINTL_DEP=
+INCINTL=
+case $USE_INCLUDED_LIBINTL in
+ yes)
+ LIBINTL_DEP='${top_builddir}/intl/libintl.a'
+ INCINTL='-I${top_builddir}/intl'
+ ;;
+esac
+
+AC_CONFIG_FILES(Makefile config.intl)
+AC_OUTPUT
diff --git a/libjava/gnu/java/nio/channels/FileChannelImpl.java b/libjava/gnu/java/nio/channels/FileChannelImpl.java
new file mode 100644
index 00000000000..b5e5ffe6b67
--- /dev/null
+++ b/libjava/gnu/java/nio/channels/FileChannelImpl.java
@@ -0,0 +1,420 @@
+/* FileChannelImpl.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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 Classpath 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 Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.nio.channels;
+
+import gnu.classpath.Configuration;
+import gnu.gcj.RawData;
+import gnu.java.nio.FileLockImpl;
+import java.io.*;
+import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.*;
+
+/**
+ * This file is not user visible !
+ * But alas, Java does not have a concept of friendly packages
+ * so this class is public.
+ * Instances of this class are created by invoking getChannel
+ * Upon a Input/Output/RandomAccessFile object.
+ */
+
+public class FileChannelImpl extends FileChannel
+{
+ int mode;
+ // These are WHENCE values for seek.
+ static final int SET = 0;
+ static final int CUR = 1;
+
+ // These are mode values for open().
+ static final int READ = 1;
+ static final int WRITE = 2;
+ static final int APPEND = 4;
+
+ // EXCL is used only when making a temp file.
+ static final int EXCL = 8;
+ static final int SYNC = 16;
+ static final int DSYNC = 32;
+
+ /**
+ * This is the actual native file descriptor value
+ */
+ // System's notion of file descriptor. It might seem redundant to
+ // initialize this given that it is reassigned in the constructors.
+ // However, this is necessary because if open() throws an exception
+ // we want to make sure this has the value -1. This is the most
+ // efficient way to accomplish that.
+ private int fd = -1;
+
+ int length;
+ private long pos;
+
+ public FileChannelImpl ()
+ {
+ }
+
+ /* Open a file. MODE is a combination of the above mode flags. */
+ public FileChannelImpl (String path, int mode) throws FileNotFoundException
+ {
+ fd = open (path, mode);
+ this.mode = mode;
+ }
+
+ private static native void init();
+ static { init (); }
+
+ public static FileChannelImpl in;
+ public static FileChannelImpl out;
+ public static FileChannelImpl err;
+
+ private native int open (String path, int mode) throws FileNotFoundException;
+
+ /** Attach to an already-opened file. */
+ public FileChannelImpl (int desc, int mode)
+ {
+ fd = desc;
+ this.mode = mode;
+ }
+
+ native int available () throws IOException;
+ private native long implPosition ();
+ private native void seek (long newPosition);
+ private native void implTruncate (long size);
+
+ public native void unlock (long pos, long len);
+
+ public native long size () throws IOException;
+
+ protected native void implCloseChannel() throws IOException;
+
+ public int read (ByteBuffer dst) throws IOException
+ {
+ return implRead (dst);
+ }
+
+ public int read (ByteBuffer dst, long position)
+ throws IOException
+ {
+ if (position < 0)
+ throw new IllegalArgumentException ();
+ long oldPosition = implPosition ();
+ position (position);
+ int result = implRead (dst);
+ position (oldPosition);
+
+ return result;
+ }
+
+ private int implRead (ByteBuffer dst) throws IOException
+ {
+ int result;
+ byte[] buffer = new byte [dst.remaining ()];
+
+ result = read (buffer, 0, buffer.length);
+
+ if (result > 0)
+ dst.put (buffer, 0, result);
+
+ return result;
+ }
+
+ public native int read ()
+ throws IOException;
+
+ public native int read (byte[] buffer, int offset, int length)
+ throws IOException;
+
+ public long read (ByteBuffer[] dsts, int offset, int length)
+ throws IOException
+ {
+ long result = 0;
+
+ for (int i = offset; i < offset + length; i++)
+ {
+ result += read (dsts [i]);
+ }
+
+ return result;
+ }
+
+ public int write (ByteBuffer src) throws IOException
+ {
+ return implWrite (src);
+ }
+
+ public int write (ByteBuffer src, long position)
+ throws IOException
+ {
+ if (position < 0)
+ throw new IllegalArgumentException ();
+
+ if (!isOpen ())
+ throw new ClosedChannelException ();
+
+ if ((mode & WRITE) == 0)
+ throw new NonWritableChannelException ();
+
+ int result;
+ long oldPosition;
+
+ oldPosition = implPosition ();
+ seek (position);
+ result = implWrite (src);
+ seek (oldPosition);
+
+ return result;
+ }
+
+ private int implWrite (ByteBuffer src) throws IOException
+ {
+ int len = src.remaining ();
+ if (src.hasArray())
+ {
+ byte[] buffer = src.array();
+ write(buffer, src.arrayOffset() + src.position(), len);
+ }
+ else
+ {
+ // Use a more efficient native method! FIXME!
+ byte[] buffer = new byte [len];
+ src.get (buffer, 0, len);
+ write (buffer, 0, len);
+ }
+ return len;
+ }
+
+ public native void write (byte[] buffer, int offset, int length)
+ throws IOException;
+
+ public native void write (int b) throws IOException;
+
+ public long write(ByteBuffer[] srcs, int offset, int length)
+ throws IOException
+ {
+ long result = 0;
+
+ for (int i = offset;i < offset + length;i++)
+ {
+ result += write (srcs[i]);
+ }
+
+ return result;
+ }
+
+ public native MappedByteBuffer mapImpl (char mode, long position, int size)
+ throws IOException;
+
+ public MappedByteBuffer map (FileChannel.MapMode mode,
+ long position, long size)
+ throws IOException
+ {
+ char nmode = 0;
+ if (mode == MapMode.READ_ONLY)
+ {
+ nmode = 'r';
+ if ((this.mode & READ) == 0)
+ throw new NonReadableChannelException();
+ }
+ else if (mode == MapMode.READ_WRITE || mode == MapMode.PRIVATE)
+ {
+ nmode = mode == MapMode.READ_WRITE ? '+' : 'c';
+ if ((this.mode & (READ|WRITE)) != (READ|WRITE))
+ throw new NonWritableChannelException();
+ }
+ else
+ throw new IllegalArgumentException ();
+
+ if (position < 0 || size < 0 || size > Integer.MAX_VALUE)
+ throw new IllegalArgumentException ();
+ return mapImpl(nmode, position, (int) size);
+ }
+
+ /**
+ * msync with the disk
+ */
+ public void force (boolean metaData) throws IOException
+ {
+ if (!isOpen ())
+ throw new ClosedChannelException ();
+ }
+
+ public long transferTo (long position, long count, WritableByteChannel target)
+ throws IOException
+ {
+ if (position < 0
+ || count < 0)
+ throw new IllegalArgumentException ();
+
+ if (!isOpen ())
+ throw new ClosedChannelException ();
+
+ if ((mode & READ) == 0)
+ throw new NonReadableChannelException ();
+
+ // XXX: count needs to be casted from long to int. Dataloss ?
+ ByteBuffer buffer = ByteBuffer.allocate ((int) count);
+ read (buffer, position);
+ buffer.flip();
+ return target.write (buffer);
+ }
+
+ public long transferFrom (ReadableByteChannel src, long position, long count)
+ throws IOException
+ {
+ if (position < 0
+ || count < 0)
+ throw new IllegalArgumentException ();
+
+ if (!isOpen ())
+ throw new ClosedChannelException ();
+
+ if ((mode & WRITE) == 0)
+ throw new NonWritableChannelException ();
+
+ // XXX: count needs to be casted from long to int. Dataloss ?
+ ByteBuffer buffer = ByteBuffer.allocate ((int) count);
+ src.read (buffer);
+ buffer.flip();
+ return write (buffer, position);
+ }
+
+ public FileLock tryLock (long position, long size, boolean shared)
+ throws IOException
+ {
+ if (position < 0
+ || size < 0)
+ throw new IllegalArgumentException ();
+
+ if (!isOpen ())
+ throw new ClosedChannelException ();
+
+ if (shared && (mode & READ) == 0)
+ throw new NonReadableChannelException ();
+
+ if (!shared && (mode & WRITE) == 0)
+ throw new NonWritableChannelException ();
+
+ boolean completed = false;
+
+ try
+ {
+ begin();
+ lock(position, size, shared, true);
+ completed = true;
+ return new FileLockImpl(this, position, size, shared);
+ }
+ finally
+ {
+ end(completed);
+ }
+ }
+
+ /** Try to acquire a lock at the given position and size.
+ * On success return true.
+ * If wait as specified, block until we can get it.
+ * Otherwise return false.
+ */
+ private native boolean lock(long position, long size,
+ boolean shared, boolean wait);
+
+ public FileLock lock (long position, long size, boolean shared)
+ throws IOException
+ {
+ if (position < 0
+ || size < 0)
+ throw new IllegalArgumentException ();
+
+ if (!isOpen ())
+ throw new ClosedChannelException ();
+
+ boolean completed = false;
+
+ try
+ {
+ boolean lockable = lock(position, size, shared, false);
+ completed = true;
+ return (lockable
+ ? new FileLockImpl(this, position, size, shared)
+ : null);
+ }
+ finally
+ {
+ end(completed);
+ }
+ }
+
+ public long position ()
+ throws IOException
+ {
+ if (!isOpen ())
+ throw new ClosedChannelException ();
+
+ return implPosition ();
+ }
+
+ public FileChannel position (long newPosition)
+ throws IOException
+ {
+ if (newPosition < 0)
+ throw new IllegalArgumentException ();
+
+ if (!isOpen ())
+ throw new ClosedChannelException ();
+
+ // FIXME note semantics if seeking beyond eof.
+ // We should seek lazily - only on a write.
+ seek (newPosition);
+ return this;
+ }
+
+ public FileChannel truncate (long size)
+ throws IOException
+ {
+ if (size < 0)
+ throw new IllegalArgumentException ();
+
+ if (!isOpen ())
+ throw new ClosedChannelException ();
+
+ if ((mode & WRITE) == 0)
+ throw new NonWritableChannelException ();
+
+ implTruncate (size);
+ return this;
+ }
+}
diff --git a/libjava/gnu/java/nio/channels/natFileChannelEcos.cc b/libjava/gnu/java/nio/channels/natFileChannelEcos.cc
new file mode 100644
index 00000000000..66713079a86
--- /dev/null
+++ b/libjava/gnu/java/nio/channels/natFileChannelEcos.cc
@@ -0,0 +1,159 @@
+// natFileDescriptor.cc - Native part of FileDescriptor class.
+
+/* Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation
+
+ This file is part of libgcj.
+
+This software is copyrighted work licensed under the terms of the
+Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
+details. */
+
+#include <config.h>
+
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include <gcj/cni.h>
+#include <jvm.h>
+#include <java/io/FileDescriptor.h>
+#include <java/io/SyncFailedException.h>
+#include <java/io/IOException.h>
+#include <java/io/EOFException.h>
+#include <java/lang/ArrayIndexOutOfBoundsException.h>
+#include <java/lang/NullPointerException.h>
+#include <java/lang/String.h>
+#include <java/io/FileNotFoundException.h>
+
+extern "C" void diag_write_char (char c);
+
+static void
+diag_write (char *data, int len)
+{
+ while (len > 0)
+ {
+ diag_write_char (*data++);
+ len--;
+ }
+}
+
+#define NO_FSYNC_MESSAGE "sync unsupported"
+
+void
+java::io::FileDescriptor::init(void)
+{
+ in = new java::io::FileDescriptor(0);
+ out = new java::io::FileDescriptor(1);
+ err = new java::io::FileDescriptor(2);
+}
+
+jboolean
+java::io::FileDescriptor::valid (void)
+{
+ return true;
+}
+
+void
+java::io::FileDescriptor::sync (void)
+{
+ // Some files don't support fsync. We don't bother reporting these
+ // as errors.
+#ifdef HAVE_FSYNC
+#else
+ throw new SyncFailedException (JvNewStringLatin1 (NO_FSYNC_MESSAGE));
+#endif
+}
+
+jint
+java::io::FileDescriptor::open (jstring path, jint jflags)
+{
+ return fd;
+}
+
+void
+java::io::FileDescriptor::write (jint b)
+{
+ char d = (char) b;
+ ::diag_write (&d, 1);
+}
+
+void
+java::io::FileDescriptor::write (jbyteArray b, jint offset, jint len)
+{
+ if (! b)
+ throw new java::lang::NullPointerException;
+ if (offset < 0 || len < 0 || offset + len > JvGetArrayLength (b))
+ throw new java::lang::ArrayIndexOutOfBoundsException;
+ char *bytes = (char *)elements (b) + offset;
+ ::diag_write (bytes, len);
+}
+
+void
+java::io::FileDescriptor::close (void)
+{
+}
+
+void
+java::io::FileDescriptor::setLength (long)
+{
+}
+
+jint
+java::io::FileDescriptor::seek (jlong pos, jint whence, jboolean)
+{
+ JvAssert (whence == SET || whence == CUR);
+ return 0;
+}
+
+jlong
+java::io::FileDescriptor::getLength (void)
+{
+ return 0;
+}
+
+jlong
+java::io::FileDescriptor::getFilePointer (void)
+{
+ return 0;
+}
+
+jint
+java::io::FileDescriptor::read (void)
+{
+ return 0;
+}
+
+jint
+java::io::FileDescriptor::read (jbyteArray buffer, jint offset, jint count)
+{
+ return 0;
+}
+
+jint
+java::io::FileDescriptor::available (void)
+{
+ return 0;
+}
+
+void
+java::io::FileDescriptor::lock (jlong pos, jint len, jboolean shared)
+{
+ throw new IOException (JvNewStringLatin1
+ ("java.io.FileDescriptor.lock() not implemented"));
+}
+
+jboolean
+java::io::FileDescriptor::tryLock (jlong pos, jint len, jboolean shared)
+{
+ throw new IOException (JvNewStringLatin1
+ ("java.io.FileDescriptor.tryLock() not implemented"));
+}
+
+void
+java::io::FileDescriptor::unlock (jlong pos, jint len)
+{
+ throw new IOException (JvNewStringLatin1
+ ("java.io.FileDescriptor.unlock() not implemented"));
+}
diff --git a/libjava/gnu/java/nio/channels/natFileChannelPosix.cc b/libjava/gnu/java/nio/channels/natFileChannelPosix.cc
new file mode 100644
index 00000000000..86537872821
--- /dev/null
+++ b/libjava/gnu/java/nio/channels/natFileChannelPosix.cc
@@ -0,0 +1,522 @@
+
+// natFileChannelImplPosix.cc - Native part of FileChannelImpl class.
+
+/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation
+
+ This file is part of libgcj.
+
+This software is copyrighted work licensed under the terms of the
+Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
+details. */
+
+#include <config.h>
+#include <platform.h>
+
+#include <gcj/cni.h>
+#include <gcj/javaprims.h>
+#include <jvm.h>
+
+#include "posix.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include <gnu/gcj/RawData.h>
+#include <gnu/java/nio/FileLockImpl.h>
+#include <gnu/java/nio/channels/FileChannelImpl.h>
+#include <java/io/FileNotFoundException.h>
+#include <java/io/IOException.h>
+#include <java/io/SyncFailedException.h>
+#include <java/io/InterruptedIOException.h>
+#include <java/io/EOFException.h>
+#include <java/lang/ArrayIndexOutOfBoundsException.h>
+#include <java/lang/NullPointerException.h>
+#include <java/lang/System.h>
+#include <java/lang/String.h>
+#include <java/lang/Thread.h>
+#include <java/nio/ByteBuffer.h>
+#include <java/nio/MappedByteBufferImpl.h>
+#include <java/nio/channels/FileChannel.h>
+#include <java/nio/channels/FileLock.h>
+#include <gnu/java/nio/channels/FileChannelImpl.h>
+
+#ifdef HAVE_SYS_IOCTL_H
+#define BSD_COMP /* Get FIONREAD on Solaris2. */
+#include <sys/ioctl.h>
+#endif
+
+// Pick up FIONREAD on Solaris 2.5.
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#endif
+
+using gnu::gcj::RawData;
+using java::io::IOException;
+using java::nio::MappedByteBufferImpl;
+using java::io::InterruptedIOException;
+using java::io::FileNotFoundException;
+using java::lang::ArrayIndexOutOfBoundsException;
+using gnu::java::nio::channels::FileChannelImpl;
+
+#define NO_FSYNC_MESSAGE "sync unsupported"
+
+void
+FileChannelImpl::init(void)
+{
+ in = new FileChannelImpl((jint) 0, FileChannelImpl::READ);
+ out = new FileChannelImpl((jint) 1, FileChannelImpl::WRITE);
+ err = new FileChannelImpl((jint) 2, FileChannelImpl::WRITE);
+}
+
+#if 0
+jboolean
+FileChannelImpl::valid (void)
+{
+ struct stat sb;
+ return fd >= 0 && ::fstat (fd, &sb) == 0;
+}
+
+void
+FileChannelImpl::sync (void)
+{
+ // Some files don't support fsync. We don't bother reporting these
+ // as errors.
+#ifdef HAVE_FSYNC
+ if (::fsync (fd) && errno != EROFS && errno != EINVAL)
+ throw new SyncFailedException (JvNewStringLatin1 (strerror (errno)));
+#else
+ throw new SyncFailedException (JvNewStringLatin1 (NO_FSYNC_MESSAGE));
+#endif
+}
+#endif
+
+jint
+FileChannelImpl::open (jstring path, jint jflags)
+{
+ fd = -1;
+ char *buf = (char *) _Jv_AllocBytes (_Jv_GetStringUTFLength (path) + 1);
+ jsize total = JvGetStringUTFRegion (path, 0, path->length(), buf);
+ buf[total] = '\0';
+ int flags = 0;
+#ifdef O_BINARY
+ flags |= O_BINARY;
+#endif
+
+ JvAssert ((jflags & READ) || (jflags & WRITE));
+ int mode = 0666;
+ if ((jflags & READ) && (jflags & WRITE))
+ flags |= O_RDWR | O_CREAT;
+ else if ((jflags & READ))
+ flags |= O_RDONLY;
+ else
+ {
+ flags |= O_WRONLY | O_CREAT;
+ if ((jflags & APPEND))
+ flags |= O_APPEND;
+ else
+ flags |= O_TRUNC;
+
+ if ((jflags & EXCL))
+ {
+ flags |= O_EXCL;
+ // In this case we are making a temp file.
+ mode = 0600;
+ }
+ }
+
+ if ((jflags & SYNC))
+ flags |= O_SYNC;
+
+ if ((jflags & DSYNC))
+ flags |= O_DSYNC;
+
+ int fd = ::open (buf, flags, mode);
+ if (fd == -1 && errno == EMFILE)
+ {
+ // Because finalize () calls close () we might be able to continue.
+ ::java::lang::System::gc ();
+ ::java::lang::System::runFinalization ();
+ fd = ::open (buf, flags, mode);
+ }
+ if (fd == -1)
+ {
+ char msg[MAXPATHLEN + 200];
+ // We choose the formatting here for JDK compatibility, believe
+ // it or not.
+ sprintf (msg, "%.*s (%.*s)",
+ MAXPATHLEN + 150, buf,
+ 40, strerror (errno));
+ throw new ::java::io::FileNotFoundException (JvNewStringLatin1 (msg));
+ }
+
+ _Jv_platform_close_on_exec (fd);
+
+ return fd;
+}
+
+void
+FileChannelImpl::write (jint b)
+{
+ jbyte d = (jbyte) b;
+ int r = 0;
+ while (r != 1)
+ {
+ r = ::write (fd, &d, 1);
+ if (r == -1)
+ {
+ if (::java::lang::Thread::interrupted())
+ {
+ ::java::io::InterruptedIOException *iioe
+ = new ::java::io::InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
+ iioe->bytesTransferred = r == -1 ? 0 : r;
+ throw iioe;
+ }
+ if (errno != EINTR)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ }
+ }
+ pos++;
+}
+
+void
+FileChannelImpl::write (jbyteArray b, jint offset, jint len)
+{
+ if (! b)
+ throw new ::java::lang::NullPointerException;
+ if (offset < 0 || len < 0 || offset + len > JvGetArrayLength (b))
+ throw new ArrayIndexOutOfBoundsException;
+ jbyte *bytes = elements (b) + offset;
+
+ int written = 0;
+ while (len > 0)
+ {
+ int r = ::write (fd, bytes, len);
+ if (r == -1)
+ {
+ if (::java::lang::Thread::interrupted())
+ {
+ InterruptedIOException *iioe
+ = new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
+ iioe->bytesTransferred = written;
+ throw iioe;
+ }
+ if (errno != EINTR)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ }
+
+ written += r;
+ len -= r;
+ bytes += r;
+ pos += r;
+ }
+}
+
+void
+FileChannelImpl::implCloseChannel (void)
+{
+ jint save = fd;
+ fd = -1;
+ if (::close (save))
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+}
+
+void
+FileChannelImpl::implTruncate (jlong size)
+{
+ struct stat sb;
+
+#ifdef HAVE_FTRUNCATE
+ if (::fstat (fd, &sb))
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+
+ if ((jlong) sb.st_size == size)
+ return;
+
+ // If the file is too short, we extend it. We can't rely on
+ // ftruncate() extending the file. So we lseek() to 1 byte less
+ // than we want, and then we write a single byte at the end.
+ if ((jlong) sb.st_size < size)
+ {
+ if (::lseek (fd, (off_t) (size - 1), SEEK_SET) == -1)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ char out = '\0';
+ int r = ::write (fd, &out, 1);
+ if (r <= 0 || ::lseek (fd, pos, SEEK_SET) == -1)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ }
+ else
+ {
+ if (::ftruncate (fd, (off_t) pos))
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ pos = size;
+ }
+#else /* HAVE_FTRUNCATE */
+ throw new IOException (JvNewStringLatin1 ("FileDescriptor.setLength not implemented"));
+#endif /* HAVE_FTRUNCATE */
+}
+
+void
+FileChannelImpl::seek (jlong newPos)
+{
+ off_t r = ::lseek (fd, (off_t) newPos, SEEK_SET);
+ if (r == -1)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ pos = r;
+}
+
+jlong
+FileChannelImpl::size (void)
+{
+ struct stat sb;
+ if (::fstat (fd, &sb))
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ return sb.st_size;
+}
+
+jlong
+FileChannelImpl::implPosition (void)
+{
+ return pos;
+}
+
+jint
+FileChannelImpl::read (void)
+{
+ jbyte b;
+ int r;
+ do
+ {
+ r = ::read (fd, &b, 1);
+ if (r == 0)
+ return -1;
+ if (r == -1)
+ {
+ if (::java::lang::Thread::interrupted())
+ {
+ InterruptedIOException *iioe
+ = new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
+ iioe->bytesTransferred = r == -1 ? 0 : r;
+ throw iioe;
+ }
+ if (errno != EINTR)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ }
+ }
+ while (r != 1);
+ pos++;
+ return b & 0xFF;
+}
+
+jint
+FileChannelImpl::read (jbyteArray buffer, jint offset, jint count)
+{
+ if (! buffer)
+ throw new ::java::lang::NullPointerException;
+ jsize bsize = JvGetArrayLength (buffer);
+ if (offset < 0 || count < 0 || offset + count > bsize)
+ throw new ::java::lang::ArrayIndexOutOfBoundsException;
+
+ // Must return 0 if an attempt is made to read 0 bytes.
+ if (count == 0)
+ return 0;
+
+ jbyte *bytes = elements (buffer) + offset;
+ int r;
+ do
+ {
+ r = ::read (fd, bytes, count);
+ if (r == 0)
+ return -1;
+ if (r == -1)
+ {
+ if (::java::lang::Thread::interrupted())
+ {
+ InterruptedIOException *iioe
+ = new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
+ iioe->bytesTransferred = r == -1 ? 0 : r;
+ throw iioe;
+ }
+ if (errno != EINTR)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ }
+ }
+ while (r <= 0);
+ pos += r;
+ return r;
+}
+
+jint
+FileChannelImpl::available (void)
+{
+#if defined (FIONREAD) || defined (HAVE_SELECT) || defined (HAVE_FSTAT)
+ long num = 0;
+ int r = 0;
+ bool num_set = false;
+
+#if defined (FIONREAD)
+ r = ::ioctl (fd, FIONREAD, &num);
+ if (r == -1 && errno == ENOTTY)
+ {
+ // If the ioctl doesn't work, we don't care.
+ r = 0;
+ num = 0;
+ }
+ else
+ num_set = true;
+#elif defined (HAVE_SELECT)
+ if (fd < 0)
+ {
+ errno = EBADF;
+ r = -1;
+ }
+#endif
+
+ if (r == -1)
+ {
+ posix_error:
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ }
+
+ // If we didn't get anything, and we have fstat, then see if see if
+ // we're reading a regular file. On many systems, FIONREAD does not
+ // work on regular files; select() likewise returns a useless
+ // result. This is run incorrectly when FIONREAD does work on
+ // regular files and we are at the end of the file. However, this
+ // case probably isn't very important.
+#if defined (HAVE_FSTAT)
+ if (! num_set)
+ {
+ struct stat sb;
+ off_t where = 0;
+ if (fstat (fd, &sb) != -1
+ && S_ISREG (sb.st_mode)
+ && (where = lseek (fd, 0, SEEK_CUR)) != (off_t) -1)
+ {
+ num = (long) (sb.st_size - where);
+ num_set = true;
+ }
+ }
+#endif /* HAVE_FSTAT */
+
+#if defined (HAVE_SELECT)
+ if (! num_set)
+ {
+ fd_set rd;
+ FD_ZERO (&rd);
+ FD_SET (fd, &rd);
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ r = _Jv_select (fd + 1, &rd, NULL, NULL, &tv);
+ if (r == -1)
+ goto posix_error;
+ num = r == 0 ? 0 : 1;
+ }
+#endif /* HAVE_SELECT */
+
+ return (jint) num;
+#else
+ return 0;
+#endif
+}
+
+jboolean
+FileChannelImpl::lock
+(jlong pos, jlong len, jboolean shared, jboolean wait)
+{
+ struct flock lockdata;
+
+ lockdata.l_type = shared ? F_WRLCK : F_RDLCK;
+ lockdata.l_whence = SEEK_SET;
+ lockdata.l_start = pos;
+ lockdata.l_len = len;
+
+ if (::fcntl (fd, wait ? F_SETLKW : F_SETLK, &lockdata) == -1)
+ {
+ if (! wait && (errno == EACCES || errno == EAGAIN))
+ return false;
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ }
+ return true;
+}
+
+void
+FileChannelImpl::unlock (jlong pos, jlong len)
+{
+ struct flock lockdata;
+
+ lockdata.l_type = F_UNLCK;
+ lockdata.l_whence = SEEK_SET;
+ lockdata.l_start = pos;
+ lockdata.l_len = len;
+
+ if (::fcntl (fd, F_SETLK, &lockdata) == -1)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+}
+
+java::nio::MappedByteBuffer *
+FileChannelImpl::mapImpl (jchar mmode, jlong position, jint size)
+{
+#if defined(HAVE_MMAP)
+ int prot, flags;
+ if (mmode == 'r')
+ {
+ prot = PROT_READ;
+ flags = MAP_PRIVATE;
+ }
+ else
+ {
+ prot = PROT_READ|PROT_WRITE;
+ flags = mmode == '+' ? MAP_SHARED : MAP_PRIVATE;
+ }
+ jint page_size = ::getpagesize();
+ jint offset = position & ~(page_size-1);
+ jint align = position - offset;
+ void* ptr = ::mmap(NULL, size + align, prot, flags, fd, offset);
+ MappedByteBufferImpl *buf
+ = new MappedByteBufferImpl ((RawData *) ((char *) ptr + align),
+ size, mmode == 'r');
+ if (ptr == MAP_FAILED)
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ buf->implPtr = reinterpret_cast<RawData*> (ptr);
+ buf->implLen = size+align;
+ return buf;
+#else
+ throw new IOException (JvNewStringUTF ("mmap not implemented"));
+#endif
+}
+
+void
+MappedByteBufferImpl::unmapImpl ()
+{
+#if defined(HAVE_MMAP)
+ munmap((void*) implPtr, implLen);
+#endif
+}
+
+void
+MappedByteBufferImpl::loadImpl ()
+{
+}
+
+jboolean
+MappedByteBufferImpl::isLoadedImpl ()
+{
+ return true;
+}
+
+void
+MappedByteBufferImpl::forceImpl ()
+{
+#if defined(HAVE_MMAP)
+ ::msync((void*) implPtr, implLen, MS_SYNC);
+#endif
+}
diff --git a/libjava/gnu/java/nio/channels/natFileChannelWin32.cc b/libjava/gnu/java/nio/channels/natFileChannelWin32.cc
new file mode 100644
index 00000000000..ca6387df011
--- /dev/null
+++ b/libjava/gnu/java/nio/channels/natFileChannelWin32.cc
@@ -0,0 +1,432 @@
+// natFileChannelImplWin32.cc - Native part of FileChannelImpl class.
+
+/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software
+ Foundation, Inc.
+
+ This file is part of libgcj.
+
+This software is copyrighted work licensed under the terms of the
+Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
+details. */
+
+// FIXME: In order to support interrupting of IO operations, we
+// need to change to use the windows asynchronous IO functions
+
+#include <config.h>
+#include <platform.h>
+
+#include <gcj/cni.h>
+#include <gcj/javaprims.h>
+#include <jvm.h>
+
+#include <stdio.h>
+
+#include <gnu/gcj/RawData.h>
+#include <gnu/java/nio/FileLockImpl.h>
+#include <gnu/java/nio/channels/FileChannelImpl.h>
+#include <java/io/FileNotFoundException.h>
+#include <java/io/IOException.h>
+#include <java/io/SyncFailedException.h>
+#include <java/io/InterruptedIOException.h>
+#include <java/io/EOFException.h>
+#include <java/lang/ArrayIndexOutOfBoundsException.h>
+#include <java/lang/NullPointerException.h>
+#include <java/lang/System.h>
+#include <java/lang/String.h>
+#include <java/lang/Thread.h>
+#include <java/nio/ByteBuffer.h>
+#include <java/nio/MappedByteBufferImpl.h>
+#include <java/nio/channels/FileChannel.h>
+#include <java/nio/channels/FileLock.h>
+#include <gnu/java/nio/channels/FileChannelImpl.h>
+
+using gnu::gcj::RawData;
+using java::io::IOException;
+using java::nio::MappedByteBufferImpl;
+using java::io::InterruptedIOException;
+using java::io::FileNotFoundException;
+using java::lang::ArrayIndexOutOfBoundsException;
+using gnu::java::nio::channels::FileChannelImpl;
+
+#undef STRICT
+
+static bool testCanUseGetHandleInfo()
+{
+ /* Test to see whether GetHandleInformation can be used
+ for console input or screen buffers. This is better
+ a kludgy OS version check. */
+ DWORD dwFlags;
+ return GetHandleInformation (GetStdHandle (STD_INPUT_HANDLE),
+ &dwFlags) != 0;
+}
+
+// FIXME: casting a FILE (pointer) to a jint will not work on Win64 --
+// we should be using gnu.gcj.RawData's.
+
+void
+FileChannelImpl::init(void)
+{
+ in = new FileChannelImpl((jint)(GetStdHandle (STD_INPUT_HANDLE)),
+ FileChannelImpl::READ);
+ out = new FileChannelImpl((jint)(GetStdHandle (STD_OUTPUT_HANDLE)),
+ FileChannelImpl::WRITE);
+ err = new FileChannelImpl((jint)(GetStdHandle (STD_ERROR_HANDLE)),
+ FileChannelImpl::WRITE);
+}
+
+#if 0
+FileChannelImpl::sync (void) {
+ if (! FlushFileBuffers ((HANDLE)fd))
+ {
+ DWORD dwErrorCode = GetLastError ();
+ throw new SyncFailedException (_Jv_WinStrError (dwErrorCode));
+ }
+}
+#endif
+
+jint
+FileChannelImpl::open (jstring path, jint jflags) {
+
+ HANDLE handle = NULL;
+ DWORD access = 0;
+ DWORD create = OPEN_EXISTING;
+
+ JV_TEMP_STRING_WIN32(cpath, path)
+
+ JvAssert((jflags & READ) || (jflags & WRITE));
+
+ if ((jflags & READ) && (jflags & WRITE))
+ {
+ access = GENERIC_READ | GENERIC_WRITE;
+ if (jflags & EXCL)
+ create = CREATE_NEW; // this will raise error if file exists.
+ else
+ create = OPEN_ALWAYS; // equivalent to O_CREAT
+ }
+ else if (jflags & READ)
+ {
+ access = GENERIC_READ;
+ create = OPEN_EXISTING; // ignore EXCL
+ }
+ else
+ {
+ access = GENERIC_WRITE;
+ if (jflags & EXCL)
+ create = CREATE_NEW;
+ else if (jflags & APPEND)
+ create = OPEN_ALWAYS;
+ else
+ create = CREATE_ALWAYS;
+ }
+
+ handle = CreateFile(cpath, access, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, create, 0, NULL);
+
+ if (handle == INVALID_HANDLE_VALUE)
+ {
+ DWORD dwErrorCode = GetLastError ();
+ throw new FileNotFoundException (_Jv_WinStrError (cpath, dwErrorCode));
+ }
+
+ // For APPEND mode, move the file pointer to the end of the file.
+ if (jflags & APPEND)
+ {
+ DWORD low = SetFilePointer (handle, 0, NULL, FILE_END);
+ if ((low == (DWORD) 0xffffffff) && (GetLastError () != NO_ERROR))
+ {
+ DWORD dwErrorCode = GetLastError ();
+ throw new FileNotFoundException (_Jv_WinStrError (cpath, dwErrorCode));
+ }
+ }
+
+ // Make this handle non-inheritable so that child
+ // processes don't inadvertently prevent us from
+ // closing this file.
+ _Jv_platform_close_on_exec (handle);
+
+ return (jint) handle;
+}
+
+void
+FileChannelImpl::write (jint b)
+{
+ DWORD bytesWritten;
+ jbyte buf = (jbyte)b;
+
+ if (WriteFile ((HANDLE)fd, &buf, 1, &bytesWritten, NULL))
+ {
+ if (::java::lang::Thread::interrupted())
+ {
+ InterruptedIOException *iioe = new InterruptedIOException (JvNewStringLatin1 ("write interrupted"));
+ iioe->bytesTransferred = bytesWritten;
+ throw iioe;
+ }
+ if (bytesWritten != 1)
+ _Jv_ThrowIOException ();
+ }
+ else
+ _Jv_ThrowIOException ();
+ // FIXME: loop until bytesWritten == 1
+}
+
+void
+FileChannelImpl::write(jbyteArray b, jint offset, jint len)
+{
+ if (! b)
+ throw new ::java::lang::NullPointerException;
+ if(offset < 0 || len < 0 || offset + len > JvGetArrayLength (b))
+ throw new ArrayIndexOutOfBoundsException;
+
+ jbyte *buf = elements (b) + offset;
+ DWORD bytesWritten;
+
+ if (WriteFile ((HANDLE)fd, buf, len, &bytesWritten, NULL))
+ {
+ if (::java::lang::Thread::interrupted())
+ {
+ InterruptedIOException *iioe = new InterruptedIOException (JvNewStringLatin1 ("write interrupted"));
+ iioe->bytesTransferred = bytesWritten;
+ throw iioe;
+ }
+ }
+ else
+ _Jv_ThrowIOException ();
+ // FIXME: loop until bytesWritten == len
+}
+
+void
+FileChannelImpl::implCloseChannel (void)
+{
+ HANDLE save = (HANDLE)fd;
+ fd = (jint)INVALID_HANDLE_VALUE;
+ if (! CloseHandle (save))
+ _Jv_ThrowIOException ();
+}
+
+void
+FileChannelImpl::implTruncate (jlong size)
+{
+ LONG liOrigFilePointer;
+ LONG liNewFilePointer;
+ LONG liEndFilePointer;
+
+ // Get the original file pointer.
+ if (SetFilePointer((HANDLE) fd, (LONG) 0, &liOrigFilePointer,
+ FILE_CURRENT) != (BOOL) 0
+ && (GetLastError() != NO_ERROR))
+ _Jv_ThrowIOException ();
+
+ // Get the length of the file.
+ if (SetFilePointer((HANDLE) fd, (LONG) 0, &liEndFilePointer,
+ FILE_END) != (BOOL) 0
+ && (GetLastError() != NO_ERROR))
+ _Jv_ThrowIOException ();
+
+ if ((jlong)liEndFilePointer == size)
+ {
+ // Restore the file pointer.
+ if (liOrigFilePointer != liEndFilePointer)
+ {
+ if (SetFilePointer((HANDLE) fd, liOrigFilePointer, &liNewFilePointer,
+ FILE_BEGIN) != (BOOL) 0
+ && (GetLastError() != NO_ERROR))
+ _Jv_ThrowIOException ();
+ }
+ return;
+ }
+
+ // Seek to the new end of file.
+ if (SetFilePointer((HANDLE) fd, (LONG) size, &liNewFilePointer,
+ FILE_BEGIN) != (BOOL) 0
+ && (GetLastError() != NO_ERROR))
+ _Jv_ThrowIOException ();
+
+ // Truncate the file at this point.
+ if (SetEndOfFile((HANDLE) fd) != (BOOL) 0 && (GetLastError() != NO_ERROR))
+ _Jv_ThrowIOException ();
+
+ if (liOrigFilePointer < liNewFilePointer)
+ {
+ // Restore the file pointer.
+ if (SetFilePointer((HANDLE) fd, liOrigFilePointer, &liNewFilePointer,
+ FILE_BEGIN) != (BOOL) 0
+ && (GetLastError() != NO_ERROR))
+ _Jv_ThrowIOException ();
+ }
+}
+
+void
+FileChannelImpl::seek (jlong newPos)
+{
+ LONG high = pos >> 32;
+ DWORD low = SetFilePointer ((HANDLE)fd, (DWORD)(0xffffffff & newPos), &high, FILE_BEGIN);
+ if ((low == 0xffffffff) && (GetLastError () != NO_ERROR))
+ _Jv_ThrowIOException ();
+}
+
+jlong
+FileChannelImpl::implPosition (void)
+{
+ LONG high = 0;
+ DWORD low = SetFilePointer ((HANDLE)fd, 0, &high, FILE_CURRENT);
+ if ((low == 0xffffffff) && (GetLastError() != NO_ERROR))
+ _Jv_ThrowIOException ();
+ return (((jlong)high) << 32L) | (jlong)low;
+}
+
+jlong
+FileChannelImpl::size (void)
+{
+ DWORD high;
+ DWORD low;
+
+ low = GetFileSize ((HANDLE)fd, &high);
+ // FIXME: Error checking
+ return (((jlong)high) << 32L) | (jlong)low;
+}
+
+jint
+FileChannelImpl::read (void)
+{
+ CHAR buf;
+ DWORD read;
+
+ if (! ReadFile ((HANDLE)fd, &buf, 1, &read, NULL))
+ {
+ if (GetLastError () == ERROR_BROKEN_PIPE)
+ return -1;
+ else
+ _Jv_ThrowIOException ();
+ }
+
+ if (! read)
+ return -1;
+ else
+ return (jint)(buf & 0xff);
+}
+
+jint
+FileChannelImpl::read (jbyteArray buffer, jint offset, jint count)
+{
+ if (! buffer)
+ throw new ::java::lang::NullPointerException;
+
+ jsize bsize = JvGetArrayLength (buffer);
+ if (offset < 0 || count < 0 || offset + count > bsize)
+ throw new ArrayIndexOutOfBoundsException;
+
+ // Must return 0 if an attempt is made to read 0 bytes.
+ if (count == 0)
+ return 0;
+
+ jbyte *bytes = elements (buffer) + offset;
+
+ DWORD read;
+ if (! ReadFile((HANDLE)fd, bytes, count, &read, NULL))
+ {
+ if (GetLastError () == ERROR_BROKEN_PIPE)
+ return -1;
+ else
+ _Jv_ThrowIOException ();
+ }
+
+ if (read == 0) return -1;
+
+ return (jint)read;
+}
+
+jint
+FileChannelImpl::available (void)
+{
+ // FIXME:
+ return size() - position();
+}
+
+jboolean
+FileChannelImpl::lock
+(jlong /*pos*/, jlong /*len*/, jboolean /*shared*/, jboolean /*wait*/)
+{
+ throw new IOException (JvNewStringLatin1
+ ("FileChannel.lock() not implemented"));
+}
+
+void
+FileChannelImpl::unlock (jlong /*pos*/, jlong /*len*/)
+{
+ throw new IOException (JvNewStringLatin1
+ ("FileChannel.unlock() not implemented"));
+}
+
+java::nio::MappedByteBuffer *
+FileChannelImpl::mapImpl (jchar mmode, jlong position, jint size)
+{
+ SYSTEM_INFO siSysInfo;
+ GetSystemInfo(&siSysInfo);
+ DWORD page_size = siSysInfo.dwPageSize;
+ jlong offset = position & ~(page_size-1);
+ jint align = position - offset;
+ jlong high = position + size;
+ jlong max_size;
+ if (mmode == '+')
+ max_size = high - offset;
+ else
+ max_size = 0;
+ DWORD access, protect;
+ if (mmode == 'r')
+ {
+ access = FILE_MAP_READ;
+ protect = PAGE_READONLY;
+ }
+ else if (mmode == '+')
+ {
+ access = FILE_MAP_WRITE;
+ protect = PAGE_READWRITE;
+ }
+ else
+ {
+ access = FILE_MAP_COPY;
+ protect = PAGE_WRITECOPY;
+ }
+ HANDLE hFileMapping = CreateFileMapping((HANDLE) fd,
+ (LPSECURITY_ATTRIBUTES) NULL,
+ protect,
+ (DWORD) (max_size >> 32),
+ (DWORD) max_size,
+ (LPCTSTR) NULL);
+ if (hFileMapping == NULL)
+ throw new IOException();
+ void *ptr = MapViewOfFile(hFileMapping, access,
+ (DWORD) (offset >> 32), (DWORD) offset,
+ (SIZE_T) (high - offset));
+ if (ptr == NULL)
+ throw new IOException();
+ MappedByteBufferImpl *buf
+ = new MappedByteBufferImpl((RawData *) ((char *) ptr + align),
+ size, mode == 'r');
+ buf->implPtr = reinterpret_cast<RawData*> (ptr);
+ buf->implLen = (jlong) (size_t) hFileMapping;
+ return buf;
+}
+
+void
+MappedByteBufferImpl::unmapImpl ()
+{
+ UnmapViewOfFile((void*)implPtr);
+ CloseHandle((HANDLE) (size_t) implLen);
+}
+
+void
+MappedByteBufferImpl::loadImpl ()
+{
+}
+
+jboolean
+MappedByteBufferImpl::isLoadedImpl ()
+{
+ return true;
+}
+
+void
+MappedByteBufferImpl::forceImpl ()
+{
+}
diff --git a/libstdc++-v3/config/cpu/cris/atomic_word.h b/libstdc++-v3/config/cpu/cris/atomic_word.h
new file mode 100644
index 00000000000..dd2cf6b834b
--- /dev/null
+++ b/libstdc++-v3/config/cpu/cris/atomic_word.h
@@ -0,0 +1,36 @@
+// Low-level type for atomic operations -*- C++ -*-
+
+// Copyright (C) 2004 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING. If not, write to the Free
+// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+// USA.
+
+// As a special exception, you may use this file as part of a free software
+// library without restriction. Specifically, if other files instantiate
+// templates or use macros or inline functions from this file, or you compile
+// this file and link it with other files to produce an executable, this
+// file does not by itself cause the resulting executable to be covered by
+// the GNU General Public License. This exception does not however
+// invalidate any other reasons why the executable file might be covered by
+// the GNU General Public License.
+
+#ifndef _GLIBCXX_ATOMIC_WORD_H
+#define _GLIBCXX_ATOMIC_WORD_H 1
+
+// This entity must not cross a page boundary.
+typedef int _Atomic_word __attribute__ ((__aligned__ (4)));
+
+#endif
diff --git a/libstdc++-v3/config/cpu/generic/atomic_word.h b/libstdc++-v3/config/cpu/generic/atomic_word.h
new file mode 100644
index 00000000000..b46adc2a474
--- /dev/null
+++ b/libstdc++-v3/config/cpu/generic/atomic_word.h
@@ -0,0 +1,35 @@
+// Low-level type for atomic operations -*- C++ -*-
+
+// Copyright (C) 2004 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING. If not, write to the Free
+// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+// USA.
+
+// As a special exception, you may use this file as part of a free software
+// library without restriction. Specifically, if other files instantiate
+// templates or use macros or inline functions from this file, or you compile
+// this file and link it with other files to produce an executable, this
+// file does not by itself cause the resulting executable to be covered by
+// the GNU General Public License. This exception does not however
+// invalidate any other reasons why the executable file might be covered by
+// the GNU General Public License.
+
+#ifndef _GLIBCXX_ATOMIC_WORD_H
+#define _GLIBCXX_ATOMIC_WORD_H 1
+
+typedef int _Atomic_word;
+
+#endif
diff --git a/libstdc++-v3/config/cpu/sparc/atomic_word.h b/libstdc++-v3/config/cpu/sparc/atomic_word.h
new file mode 100644
index 00000000000..941fddd2b0e
--- /dev/null
+++ b/libstdc++-v3/config/cpu/sparc/atomic_word.h
@@ -0,0 +1,39 @@
+// Low-level type for atomic operations -*- C++ -*-
+
+// Copyright (C) 2004 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING. If not, write to the Free
+// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+// USA.
+
+// As a special exception, you may use this file as part of a free software
+// library without restriction. Specifically, if other files instantiate
+// templates or use macros or inline functions from this file, or you compile
+// this file and link it with other files to produce an executable, this
+// file does not by itself cause the resulting executable to be covered by
+// the GNU General Public License. This exception does not however
+// invalidate any other reasons why the executable file might be covered by
+// the GNU General Public License.
+
+#ifndef _GLIBCXX_ATOMIC_WORD_H
+#define _GLIBCXX_ATOMIC_WORD_H 1
+
+#ifdef __arch64__
+ typedef long _Atomic_word;
+#else
+ typedef int _Atomic_word;
+#endif
+
+#endif
diff --git a/libstdc++-v3/config/os/irix/atomic_word.h b/libstdc++-v3/config/os/irix/atomic_word.h
new file mode 100644
index 00000000000..68b8101c737
--- /dev/null
+++ b/libstdc++-v3/config/os/irix/atomic_word.h
@@ -0,0 +1,35 @@
+// Low-level type for atomic operations -*- C++ -*-
+
+// Copyright (C) 2004 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING. If not, write to the Free
+// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+// USA.
+
+// As a special exception, you may use this file as part of a free software
+// library without restriction. Specifically, if other files instantiate
+// templates or use macros or inline functions from this file, or you compile
+// this file and link it with other files to produce an executable, this
+// file does not by itself cause the resulting executable to be covered by
+// the GNU General Public License. This exception does not however
+// invalidate any other reasons why the executable file might be covered by
+// the GNU General Public License.
+
+#ifndef _GLIBCXX_ATOMIC_WORD_H
+#define _GLIBCXX_ATOMIC_WORD_H 1
+
+typedef long _Atomic_word;
+
+#endif
diff --git a/libstdc++-v3/config/os/irix/atomicity.h b/libstdc++-v3/config/os/irix/atomicity.h
new file mode 100644
index 00000000000..83e9e029338
--- /dev/null
+++ b/libstdc++-v3/config/os/irix/atomicity.h
@@ -0,0 +1,42 @@
+// Low-level functions for atomic operations: IRIX version -*- C++ -*-
+
+// Copyright (C) 2001, 2004 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING. If not, write to the Free
+// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+// USA.
+
+// As a special exception, you may use this file as part of a free software
+// library without restriction. Specifically, if other files instantiate
+// templates or use macros or inline functions from this file, or you compile
+// this file and link it with other files to produce an executable, this
+// file does not by itself cause the resulting executable to be covered by
+// the GNU General Public License. This exception does not however
+// invalidate any other reasons why the executable file might be covered by
+// the GNU General Public License.
+
+#include <mutex.h>
+#include <bits/atomicity.h>
+
+namespace __gnu_cxx
+{
+ _Atomic_word
+ __exchange_and_add(volatile _Atomic_word* __mem, int __val)
+ { return (_Atomic_word) test_then_add((unsigned long*) const_cast<_Atomic_word*>(__mem), __val); }
+
+ void
+ __atomic_add(volatile _Atomic_word* __mem, int __val)
+ { __exchange_and_add(__mem, __val); }
+} // namespace __gnu_cxx
diff --git a/libstdc++-v3/docs/html/20_util/allocator.html b/libstdc++-v3/docs/html/20_util/allocator.html
new file mode 100644
index 00000000000..07e8e2dd909
--- /dev/null
+++ b/libstdc++-v3/docs/html/20_util/allocator.html
@@ -0,0 +1,472 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta name="AUTHOR" content="pme@gcc.gnu.org (Phil Edwards) and bkoz@gcc.gnu.org (Benjamin Kosnik)" />
+ <meta name="KEYWORDS" content="c++, libstdc++, g++, allocator, memory" />
+ <meta name="DESCRIPTION" content="Allocators and allocation" />
+ <meta name="GENERATOR" content="emacs and ten fingers" />
+ <title>Allocators and allocation</title>
+<link rel="StyleSheet" href="../lib3styles.css" type="text/css" />
+<link rel="Start" href="../documentation.html" type="text/html"
+ title="GNU C++ Standard Library" />
+<link rel="Bookmark" href="howto.html" type="text/html"
+ title="General Utilities" />
+<link rel="Copyright" href="../17_intro/license.html" type="text/html" />
+</head>
+<body>
+
+<h1 class="centered"><a name="top">Allocators and allocation</a></h1>
+
+<p class="fineprint"><em>
+ The latest version of this document is always available at
+ <a href="http://gcc.gnu.org/onlinedocs/libstdc++/20_util/allocator.html">
+ http://gcc.gnu.org/onlinedocs/libstdc++/20_util/allocator.html</a>.
+</em></p>
+
+<p><em>
+ To the <a href="http://gcc.gnu.org/libstdc++/">libstdc++-v3 homepage</a>.
+</em></p>
+
+<!-- ####################################################### -->
+<hr />
+<p> The C++ Standard encapsulates memory management characteristics
+ for strings, container classes, and parts of iostreams in a
+ template class called <code>std::allocator</code>.
+</p>
+
+<h3 class="left">
+ <a name="standard_requirements">Standard requirements</a>
+</h3>
+ <p>The C++ standard only gives a few directives in this area:
+ </p>
+ <ul>
+ <li>When you add elements to a container, and the container must allocate
+ more memory to hold them, the container makes the request via its
+ <code>Allocator</code> template parameter. This includes adding
+ chars to the string class, which acts as a regular STL container
+ in this respect.
+ </li>
+ <li>The default <code>Allocator</code> of every container-of-T is
+ <code>std::allocator&lt;T&gt;</code>.
+ </li>
+ <li>The interface of the <code>allocator&lt;T&gt;</code> class is
+ extremely simple. It has about 20 public declarations (nested
+ typedefs, member functions, etc), but the two which concern us most
+ are:
+ <pre>
+ T* allocate (size_type n, const void* hint = 0);
+ void deallocate (T* p, size_type n);</pre>
+ (This is a simplification; the real signatures use nested typedefs.)
+ The <code>&quot;n&quot;</code> arguments in both those functions is a
+ <em>count</em> of the number of T's to allocate space for,
+ <em>not their total size</em>.
+ </li>
+ <li>&quot;The storage is obtained by calling
+ <code>::operator new(size_t)</code>, but it is unspecified when or
+ how often this function is called. The use of <code>hint</code>
+ is unspecified, but intended as an aid to locality if an
+ implementation so desires.&quot; [20.4.1.1]/6
+ </li>
+ </ul>
+
+ <p> Complete details cam be found in the C++ standard, look in
+ [20.4 Memory].
+ </p>
+
+<h3 class="left">
+ <a name="probs_possibilities">Problems and Possibilities</a>
+</h3>
+ <p>The easiest way of fulfilling the requirements is to call operator new
+ each time a container needs memory, and to call operator delete each
+ time the container releases memory. <strong>BUT</strong>
+ <a href="http://gcc.gnu.org/ml/libstdc++/2001-05/msg00105.html">this
+ method is horribly slow</a>.
+ </p>
+ <p>Or we can keep old memory around, and reuse it in a pool to save time.
+ The old libstdc++-v2 used a memory pool, and so do we. As of 3.0,
+ <a href="http://gcc.gnu.org/ml/libstdc++/2001-05/msg00136.html">it's
+ on by default</a>. The pool is shared among all the containers in the
+ program: when your program's std::vector&lt;int&gt; gets cut in half
+ and frees a bunch of its storage, that memory can be reused by the
+ private std::list&lt;WonkyWidget&gt; brought in from a KDE library
+ that you linked against. And we don't have to call operators new and
+ delete to pass the memory on, either, which is a speed bonus.
+ <strong>BUT</strong>...
+ </p>
+ <p>What about threads? No problem: in a threadsafe environment, the
+ memory pool is manipulated atomically, so you can grow a container in
+ one thread and shrink it in another, etc. <strong>BUT</strong> what
+ if threads in libstdc++-v3 aren't set up properly?
+ <a href="../faq/index.html#5_6">That's been answered already</a>.
+ </p>
+ <p><strong>BUT</strong> what if you want to use your own allocator? What
+ if you plan on using a runtime-loadable version of malloc() which uses
+ shared telepathic anonymous mmap'd sections serializable over a
+ network, so that memory requests <em>should</em> go through malloc?
+ And what if you need to debug it?
+ </p>
+
+<h3 class="left">
+ <a name="stdallocator">Implementation details of <code>std::allocator</code></a>
+</h3>
+ <p> The implementation of <code> std::allocator</code> has continued
+ to evolve through successive releases. Here's a brief history.
+ </p>
+
+<h5 class="left">
+ <a name="30allocator"> 3.0, 3.1, 3.2, 3.3 </a>
+</h5>
+ <p> During this period, all allocators were written to the SGI
+ style, and all STL containers expected this interface. This
+ interface had a traits class called <code>_Alloc_traits</code> that
+ attempted to provide more information for compile-time allocation
+ selection and optimization. This traits class had another allocator
+ wrapper, <code>__simple_alloc&lt;T,A&gt;</code>, which was a
+ wrapper around another allocator, A, which itself is an allocator
+ for instances of T. But wait, there's more:
+ <code>__allocator&lt;T,A&gt;</code> is another adapter. Many of
+ the provided allocator classes were SGI style: such classes can be
+ changed to a conforming interface with this wrapper:
+ <code>__allocator&lt;T, __alloc&gt;</code> is thus the same as
+ <code>allocator&lt;T&gt;</code>.
+ </p>
+
+ <p> The class <code>std::allocator</code> use the typedef
+ <code>__alloc</code> to select an underlying allocator that
+ satisfied memory allocation requests. The selection of this
+ underlying allocator was not user-configurable.
+ </p>
+
+<h5 class="left">
+ <a name="34allocator"> 3.4 </a>
+</h5>
+ <p> For this and later releases, the only allocator interface that
+ is support is the standard C++ interface. As such, all STL
+ containers have been adjusted, and all external allocators have
+ been modified to support this change. Because of this,
+ <code>__simple_alloc, __allocator, __alloc, </code> and <code>
+ _Alloc_traits</code> have all been removed.
+ </p>
+
+ <p> The class <code>std::allocator</code> just has typedef,
+ constructor, and rebind members. It inherits from one of the
+ high-speed extension allocators, covered below. Thus, all
+ allocation and deallocation depends on the base class.
+ </p>
+
+ <p> The base class that <code>std::allocator</code> is derived from
+ is not user-configurable.
+ </p>
+
+<h5 class="left">
+ <a name="benchmarks"> How the default allocation strategy is selected.</a>
+</h5>
+ <p> It's difficult to pick an allocation strategy that will provide
+ maximum utility, without excessively penalizing some behavior. In
+ fact, it's difficult just deciding which typical actions to measure
+ for speed.
+ </p>
+
+ <p> Three synthetic benchmarks have been created that provide data
+ that is used to compare different C++ allocators. These tests are:
+ </p>
+
+ <ul>
+ <li>Insertion. Over multiple iterations, various STL container
+ objects have elements inserted to some maximum amount. A variety
+ of allocators are tested.
+ Test source <a
+ href="http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc%2b%2b-v3/testsuite/performance/20_util/allocator/insert.cc?only_with_tag=MAIN">here.</a>
+ </li>
+
+ <li>Insertion, clear, and re-insertion in a multi-threaded
+ environment. Over multiple iterations, several threads are
+ started that insert elements into a STL container, then assign a
+ null instance of the same type to clear memory, and then
+ re-insert the same number of elements. Several STL containers and
+ multiple allocators are tested. This test shows the ability of
+ the allocator to reclaim memory on a pre-thread basis, as well as
+ measuring thread contention for memory resources.
+ Test source
+ <a href="http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc%2b%2b-v3/testsuite/performance/20_util/allocator/insert_insert.cc">
+ here.</a>
+ </li>
+
+ <li>A threaded producer/consumer model.
+ Test source
+ <a href="http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc%2b%2b-v3/testsuite/performance/20_util/allocator/producer_consumer.cc">
+ here.</a>
+ </li>
+ </ul>
+
+<h5 class="left">
+ <a name="forcenew"> Disabling memory caching.</a>
+</h5>
+ <p> In use, <code>std::allocator</code> may allocate and deallocate
+ using implementation-specified strategies and heuristics. Because of
+ this, every call to an allocator object's <code> allocate</code>
+ member function may not actually call the global operator new. This
+ situation is also duplicated for calls to the <code>
+ deallocate</code> member function.
+ </p>
+
+ <p> This can be confusing.
+ </p>
+
+ <p> In particular, this can make debugging memory errors more
+ difficult, especially when using third party tools like valgrind or
+ debug versions of <code> new</code>.
+ </p>
+
+ <p> There are various ways to solve this problem. One would be to
+ use a custom allocator that just called operators <code> new
+ </code> and <code> delete</code> directly, for every
+ allocation. (See include/ext/new_allocator.h, for instance.)
+ However, that option would involve changing source code to use the a
+ non-default allocator. Another option is to force the default
+ allocator to remove caching and pools, and to directly allocate
+ with every call of <code> allocate</code> and directly deallocate
+ with every call of <code> deallocate</code>, regardless of
+ efficiency. As it turns out, this last option is available,
+ although the exact mechanism has evolved with time.
+ </p>
+
+ <p> For GCC releases from 2.95 through the 3.1 series, defining
+ <code>__USE_MALLOC</code> on the gcc command line would change the
+ default allocation strategy to instead use <code> malloc</code> and
+ <code> free</code>. See
+ <a href="../23_containers/howto.html#3">this note</a>
+ for details as to why this was something needing improvement.
+ </p>
+
+ <p>Starting with GCC 3.2, and continued in the 3.3 series, to
+ globally disable memory caching within the library for the
+ default allocator, merely set GLIBCPP_FORCE_NEW (at this time,
+ with any value) in the system's environment before running the
+ program. If your program crashes with GLIBCPP_FORCE_NEW in the
+ environment, it likely means that you linked against objects
+ built against the older library. Code to support this extension
+ is fully compatible with 3.2 code if GLIBCPP_FORCE_NEW is not in
+ the environment.
+ </p>
+
+ <p> As it turns out, the 3.4 code base continues to use this
+ mechanism, only the environment variable has been changed to
+ GLIBCXX_FORCE_NEW.
+ </p>
+
+<h3 class="left">
+ <a name="ext_allocators">Other allocators</a>
+</h3>
+ <p> Several other allocators are provided as part of this
+ implementation. The location of the extension allocators and their
+ names have changed, but in all cases, functionality is
+ equivalent. Starting with gcc-3.4, all extension allocators are
+ standard style. Before this point, SGI style was the norm. Because of
+ this, the number of template arguments also changed. Here's a simple
+ chart to track the changes.
+ </p>
+
+<table title="extension allocators" border="1">
+ <tr>
+ <th>Allocator (3.4)</th>
+ <th>Header (3.4)</th>
+ <th>Allocator (3.[0-3])</th>
+ <th>Header (3.[0-3])</th>
+ </tr>
+ <tr>
+ <td>__gnu_cxx::new_allocator&lt;T&gt;</td>
+ <td>&lt;ext/new_allocator.h&gt;</td>
+ <td>std::__new_alloc</td>
+ <td>&lt;memory&gt;</td>
+ </tr>
+ <tr>
+ <td>__gnu_cxx::malloc_allocator&lt;T&gt;</td>
+ <td>&lt;ext/malloc_allocator.h&gt;</td>
+ <td>std::__malloc_alloc_template&lt;int&gt;</td>
+ <td>&lt;memory&gt;</td>
+ </tr>
+ <tr>
+ <td>__gnu_cxx::debug_allocator&lt;T&gt;</td>
+ <td>&lt;ext/debug_allocator.h&gt;</td>
+ <td>std::debug_alloc&lt;T&gt;</td>
+ <td>&lt;memory&gt;</td>
+ </tr>
+ <tr>
+ <td>__gnu_cxx::__pool_alloc&lt;bool, int&gt;</td>
+ <td>&lt;ext/pool_allocator.h&gt;</td>
+ <td>std::__default_alloc_template&lt;bool,int&gt;</td>
+ <td>&lt;memory&gt;</td>
+ </tr>
+ <tr>
+ <td>__gnu_cxx::__mt_alloc&lt;T&gt;</td>
+ <td>&lt;ext/mt_allocator.h&gt;</td>
+ <td></td>
+ <td></td>
+ </tr>
+</table>
+
+ <p>More details on each of these allocators follows. </p>
+ <ul>
+ <li><code>new_allocator</code>
+ <p>Simply wraps <code>::operator new</code>
+ and <code>::operator delete</code>.
+ </p>
+ </li>
+ <li><code>malloc_allocator</code>
+ <p>Simply wraps
+ <code>malloc</code> and <code>free</code>. There is also a hook
+ for an out-of-memory handler (for new/delete this is taken care of
+ elsewhere).
+ </p>
+ </li>
+ <li><code>debug_allocator</code>
+ <p> A wrapper around an
+ arbitrary allocator A. It passes on slightly increased size
+ requests to A, and uses the extra memory to store size information.
+ When a pointer is passed to <code>deallocate()</code>, the stored
+ size is checked, and assert() is used to guarantee they match.
+ </p>
+ </li>
+ <li><code>__pool_alloc</code>
+ <p> A high-performance, single pool allocator. The reusable
+ memory is shared among identical instantiations of this type.
+ It calls through <code>::operator new</code> to obtain new memory
+ when its lists run out. If a client container requests a block
+ larger than a certain threshold size, then the pool is bypassed,
+ and the allocate/deallocate request is passed to
+ <code>::operator new</code> directly. </p>
+
+ <p> This class take a boolean template parameter, called
+ <code>thr</code>, and an integer template parameter, called
+ <code>inst</code>.
+ </p>
+ <p>The <code>inst</code> number is used to track additional memory
+ pools. The point of the number is to allow multiple
+ instantiations of the classes without changing the semantics at
+ all. All three of
+ </p>
+
+ <pre>
+ typedef __pool_alloc&lt;true,0&gt; normal;
+ typedef __pool_alloc&lt;true,1&gt; private;
+ typedef __pool_alloc&lt;true,42&gt; also_private;</pre>
+ <p>behave exactly the same way. However, the memory pool for each type
+ (and remember that different instantiations result in different types)
+ remains separate.
+ </p>
+ <p>The library uses <strong>0</strong> in all its instantiations. If you
+ wish to keep separate free lists for a particular purpose, use a
+ different number.
+ </p>
+ <p>The <code>thr</code> boolean determines whether the pool should
+ be manipulated atomically or not. When thr=true, the allocator
+ is is threadsafe, while thr=false, and is slightly faster but
+ unsafe for multiple threads.
+ </p>
+ <p>(Note that the GCC thread abstraction layer allows us to provide safe
+ zero-overhead stubs for the threading routines, if threads were
+ disabled at configuration time.)
+ </p>
+
+ </li>
+
+ <li><code>__mt_alloc</code>
+ <p>A high-performance
+ fixed-size allocator. It has its own documentation, found <a
+ href="../ext/mt_allocator.html">here</a>.
+ </p>
+ </li>
+ </ul>
+
+
+<h3 class="left">
+ <a name="using_custom_allocators">Using a specific allocator</a>
+</h3>
+ <p>You can specify different memory management schemes on a
+ per-container basis, by overriding the default
+ <code>Allocator</code> template parameter. For example, an easy
+ (but non-portable) method of specifying that only malloc/free
+ should be used instead of the default node allocator is:
+ </p>
+ <pre>
+ std::list &lt;int, __gnu_cxx::malloc_allocator&lt;int&gt; &gt; malloc_list;</pre>
+ Likewise, a debugging form of whichever allocator is currently in use:
+ <pre>
+ std::deque &lt;int, __gnu_cxx::debug_allocator&lt;std::allocator&lt;int&gt; &gt; &gt; debug_deque;</pre>
+
+
+<h3 class="left">
+ <a name="custom_allocators">Writing custom allocators</a>
+</h3>
+ <p> Writing a portable C++ allocator would dictate that the
+ interface would look much like the one specified for <code>
+ std::allocator</code>. Additional member functions, but not
+ subtractions, would be permissible.
+ </p>
+
+ <p> Probably the best place to start would be to copy one of the
+ extension allocators already shipped with libstdc++: say, <code>
+ new_allocator </code>.
+ </p>
+
+
+<h3 class="left">
+ <a name="biblio">Bibliography / Further Reading</a>
+</h3>
+ <p>
+ ISO/IEC 14882:1998 Programming languages - C++ [20.4 Memory]
+ </p>
+
+ <p>
+ Austern, Matt, C/C++ Users Journal.
+ <a href="http://www.cuj.com/documents/s=8000/cujcexp1812austern/">The Standard Librarian: What Are Allocators Good
+ For?</a>
+ </p>
+
+ <p>
+ Berger, Emery,
+ <a href="http://www.cs.umass.edu/~emery/hoard/"> The Hoard memory allocator </a>
+ </p>
+
+ <p>
+ Berger, Emery with Ben Zorn & Kathryn McKinley, OOPSLA 2002
+ <a href="http://www.cs.umass.edu/~emery/pubs/berger-oopsla2002.pdf">Reconsidering Custom Memory Allocation</a>
+ </p>
+
+ <p>
+ Kreft, Klaus and Angelika Langer, C++ Report, June 1998
+ <a href="http://www.langer.camelot.de/Articles/C++Report/Allocators/Allocators.html">Allocator Types</a>
+ </p>
+
+ <p>
+ Stroustrup, Bjarne, 19.4 Allocators, The C++ Programming
+ Language, Special Edition, Addison Wesley, Inc. 2000
+ </p>
+
+ <p>
+ Yen, Felix, <a href="http://home.earthlink.net/~brimar/yalloc/">Yalloc: A Recycling C++ Allocator</a>
+ </p>
+
+<hr />
+<p>Return <a href="#top">to the top of the page</a> or
+ <a href="http://gcc.gnu.org/libstdc++/">to the libstdc++ homepage</a>.
+</p>
+
+
+<!-- ####################################################### -->
+
+<hr />
+<p class="fineprint"><em>
+See <a href="../17_intro/license.html">license.html</a> for copying conditions.
+Comments and suggestions are welcome, and may be sent to
+<a href="mailto:libstdc++@gcc.gnu.org">the libstdc++ mailing list</a>.
+</em></p>
+
+
+</body>
+</html>
diff --git a/libstdc++-v3/docs/html/ext/mt_allocator.html b/libstdc++-v3/docs/html/ext/mt_allocator.html
new file mode 100644
index 00000000000..72727114ac6
--- /dev/null
+++ b/libstdc++-v3/docs/html/ext/mt_allocator.html
@@ -0,0 +1,420 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta name="AUTHOR" content="Stefan Olsson &lt;stefan@xapa.se&gt;" />
+ <meta name="KEYWORDS" content="c++, libstdc++, g++, allocator, memory" />
+ <meta name="DESCRIPTION" content="Allocators and allocation" />
+ <meta name="GENERATOR" content="emacs and ten fingers" />
+ <title>A fixed-size, multi-thread optimized allocator</title>
+<link rel="StyleSheet" href="../lib3styles.css" type="text/css" />
+<link rel="Start" href="../documentation.html" type="text/html"
+ title="GNU C++ Standard Library" />
+<link rel="Bookmark" href="howto.html" type="text/html" title="Extensions" />
+<link rel="Copyright" href="../17_intro/license.html" type="text/html" />
+</head>
+<body>
+
+<h1 class="centered"><a name="top">A fixed-size, multi-thread optimized allocator</a></h1>
+
+<p class="fineprint"><em>
+ The latest version of this document is always available at
+ <a href="http://gcc.gnu.org/onlinedocs/libstdc++/ext/mt_allocator.html">
+ http://gcc.gnu.org/onlinedocs/libstdc++/ext/mt_allocator.html</a>.
+</em></p>
+
+<p><em>
+ To the <a href="http://gcc.gnu.org/libstdc++/">libstdc++-v3 homepage</a>.
+</em></p>
+
+<!-- ####################################################### -->
+<hr />
+<h3 class="left">
+ <a name="intro">Introduction</a>
+</h3>
+
+<p> The mt allocator [hereinafter referred to simply as "the
+allocator"] is a fixed size (power of two) allocator that was
+initially developed specifically to suit the needs of multi threaded
+applications [hereinafter referred to as an MT application]. Over time
+the allocator has evolved and been improved in many ways, one of the
+being that it now also does a good job in single threaded applications
+[hereinafter referred to as a ST application]. (Note: In this
+document, when referring to single threaded applications this also
+includes applications that are compiled with gcc without thread
+support enabled. This is accomplished using ifdef's on __GTHREADS)
+</p>
+
+<p>
+The aim of this document is to describe - from a application point of
+view - the "inner workings" of the allocator.
+</p>
+
+
+<h3 class="left">
+ <a name="init">Initialization</a>
+</h3>
+
+<p>
+The static variables (pointers to freelists, tuning parameters etc)
+are initialized to their default values at file scope, i.e.:
+</p>
+
+<pre>
+ template&lt;typename _Tp&gt; size_t
+ __mt_alloc&lt;_Tp&gt;::_S_freelist_headroom = 10;
+</pre>
+
+<p>
+The very first allocate() call will always call the _S_init() function.
+In order to make sure that this function is called exactly once we make use
+of a __gthread_once (with _S_once_mt and _S_init as arguments) call in MT
+applications and check a static bool (_S_initialized) in ST applications.
+</p>
+
+<p>
+The _S_init() function:
+- If the GLIBCXX_FORCE_NEW environment variable is set, it sets the bool
+ _S_force_new to true and then returns. This will cause subsequent calls to
+ allocate() to return memory directly from a new() call, and deallocate will
+ only do a delete() call.
+</p>
+
+<p>
+- If the GLIBCXX_FORCE_NEW environment variable is not set, both ST and MT
+ applications will:
+ - Calculate the number of bins needed. A bin is a specific power of two size
+ of bytes. I.e., by default the allocator will deal with requests of up to
+ 128 bytes (or whatever the value of _S_max_bytes is when _S_init() is
+ called). This means that there will be bins of the following sizes
+ (in bytes): 1, 2, 4, 8, 16, 32, 64, 128.
+
+ - Create the _S_binmap array. All requests are rounded up to the next
+ "large enough" bin. I.e., a request for 29 bytes will cause a block from
+ the "32 byte bin" to be returned to the application. The purpose of
+ _S_binmap is to speed up the process of finding out which bin to use.
+ I.e., the value of _S_binmap[ 29 ] is initialized to 5 (bin 5 = 32 bytes).
+</p>
+<p>
+ - Create the _S_bin array. This array consists of bin_records. There will be
+ as many bin_records in this array as the number of bins that we calculated
+ earlier. I.e., if _S_max_bytes = 128 there will be 8 entries.
+ Each bin_record is then initialized:
+ - bin_record-&gt;first = An array of pointers to block_records. There will be
+ as many block_records pointers as there are maximum number of threads
+ (in a ST application there is only 1 thread, in a MT application there
+ are _S_max_threads).
+ This holds the pointer to the first free block for each thread in this
+ bin. I.e., if we would like to know where the first free block of size 32
+ for thread number 3 is we would look this up by: _S_bin[ 5 ].first[ 3 ]
+
+ The above created block_record pointers members are now initialized to
+ their initial values. I.e. _S_bin[ n ].first[ n ] = NULL;
+</p>
+
+<p>
+- Additionally a MT application will:
+ - Create a list of free thread id's. The pointer to the first entry
+ is stored in _S_thread_freelist_first. The reason for this approach is
+ that the __gthread_self() call will not return a value that corresponds to
+ the maximum number of threads allowed but rather a process id number or
+ something else. So what we do is that we create a list of thread_records.
+ This list is _S_max_threads long and each entry holds a size_t thread_id
+ which is initialized to 1, 2, 3, 4, 5 and so on up to _S_max_threads.
+ Each time a thread calls allocate() or deallocate() we call
+ _S_get_thread_id() which looks at the value of _S_thread_key which is a
+ thread local storage pointer. If this is NULL we know that this is a newly
+ created thread and we pop the first entry from this list and saves the
+ pointer to this record in the _S_thread_key variable. The next time
+ we will get the pointer to the thread_record back and we use the
+ thread_record-&gt;thread_id as identification. I.e., the first thread that
+ calls allocate will get the first record in this list and thus be thread
+ number 1 and will then find the pointer to its first free 32 byte block
+ in _S_bin[ 5 ].first[ 1 ]
+ When we create the _S_thread_key we also define a destructor
+ (_S_thread_key_destr) which means that when the thread dies, this
+ thread_record is returned to the front of this list and the thread id
+ can then be reused if a new thread is created.
+ This list is protected by a mutex (_S_thread_freelist_mutex) which is only
+ locked when records are removed/added to the list.
+</p>
+<p>
+ - Initialize the free and used counters of each bin_record:
+ - bin_record-&gt;free = An array of size_t. This keeps track of the number
+ of blocks on a specific thread's freelist in each bin. I.e., if a thread
+ has 12 32-byte blocks on it's freelists and allocates one of these, this
+ counter would be decreased to 11.
+
+ - bin_record-&gt;used = An array of size_t. This keeps track of the number
+ of blocks currently in use of this size by this thread. I.e., if a thread
+ has made 678 requests (and no deallocations...) of 32-byte blocks this
+ counter will read 678.
+
+ The above created arrays are now initialized with their initial values.
+ I.e. _S_bin[ n ].free[ n ] = 0;
+</p>
+<p>
+ - Initialize the mutex of each bin_record:
+ The bin_record-&gt;mutex is used to protect the global freelist. This concept
+ of a global freelist is explained in more detail in the section
+ "A multi threaded example", but basically this mutex is locked whenever
+ a block of memory is retrieved or returned to the global freelist for this
+ specific bin. This only occurs when a number of blocks are grabbed from the
+ global list to a thread specific list or when a thread decides to return
+ some blocks to the global freelist.
+</p>
+
+<h3 class="left">
+ <a name="st_example">A single threaded example (and a primer for the multi threaded example!)</a>
+</h3>
+
+<p>
+Let's start by describing how the data on a freelist is laid out in memory.
+This is the first two blocks in freelist for thread id 3 in bin 3 (8 bytes):
+</p>
+<pre>
++----------------+
+| next* ---------|--+ (_S_bin[ 3 ].first[ 3 ] points here)
+| | |
+| | |
+| | |
++----------------+ |
+| thread_id = 3 | |
+| | |
+| | |
+| | |
++----------------+ |
+| DATA | | (A pointer to here is what is returned to the
+| | | the application when needed)
+| | |
+| | |
+| | |
+| | |
+| | |
+| | |
++----------------+ |
++----------------+ |
+| next* |&lt;-+ (If next == NULL it's the last one on the list)
+| |
+| |
+| |
++----------------+
+| thread_id = 3 |
+| |
+| |
+| |
++----------------+
+| DATA |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
++----------------+
+</pre>
+
+<p>
+With this in mind we simplify things a bit for a while and say that there is
+only one thread (a ST application). In this case all operations are made to
+what is referred to as the global pool - thread id 0 (No thread may be
+assigned this id since they span from 1 to _S_max_threads in a MT application).
+</p>
+<p>
+When the application requests memory (calling allocate()) we first look at the
+requested size and if this is &gt; _S_max_bytes we call new() directly and return.
+</p>
+<p>
+If the requested size is within limits we start by finding out from which
+bin we should serve this request by looking in _S_binmap.
+</p>
+<p>
+A quick look at _S_bin[ bin ].first[ 0 ] tells us if there are any blocks of
+this size on the freelist (0). If this is not NULL - fine, just remove the
+block that _S_bin[ bin ].first[ 0 ] points to from the list,
+update _S_bin[ bin ].first[ 0 ] and return a pointer to that blocks data.
+</p>
+<p>
+If the freelist is empty (the pointer is NULL) we must get memory from the
+system and build us a freelist within this memory. All requests for new memory
+is made in chunks of _S_chunk_size. Knowing the size of a block_record and
+the bytes that this bin stores we then calculate how many blocks we can create
+within this chunk, build the list, remove the first block, update the pointer
+(_S_bin[ bin ].first[ 0 ]) and return a pointer to that blocks data.
+</p>
+
+<p>
+Deallocation is equally simple; the pointer is casted back to a block_record
+pointer, lookup which bin to use based on the size, add the block to the front
+of the global freelist and update the pointer as needed
+(_S_bin[ bin ].first[ 0 ]).
+</p>
+
+<p>
+The decision to add deallocated blocks to the front of the freelist was made
+after a set of performance measurements that showed that this is roughly 10%
+faster than maintaining a set of "last pointers" as well.
+</p>
+
+<h3 class="left">
+ <a name="mt_example">A multi threaded example</a>
+</h3>
+
+<p>
+In the ST example we never used the thread_id variable present in each block.
+Let's start by explaining the purpose of this in a MT application.
+</p>
+
+<p>
+The concept of "ownership" was introduced since many MT applications
+allocate and deallocate memory to shared containers from different
+threads (such as a cache shared amongst all threads). This introduces
+a problem if the allocator only returns memory to the current threads
+freelist (I.e., there might be one thread doing all the allocation and
+thus obtaining ever more memory from the system and another thread
+that is getting a longer and longer freelist - this will in the end
+consume all available memory).
+</p>
+
+<p>
+Each time a block is moved from the global list (where ownership is
+irrelevant), to a threads freelist (or when a new freelist is built
+from a chunk directly onto a threads freelist or when a deallocation
+occurs on a block which was not allocated by the same thread id as the
+one doing the deallocation) the thread id is set to the current one.
+</p>
+
+<p>
+What's the use? Well, when a deallocation occurs we can now look at
+the thread id and find out if it was allocated by another thread id
+and decrease the used counter of that thread instead, thus keeping the
+free and used counters correct. And keeping the free and used counters
+corrects is very important since the relationship between these two
+variables decides if memory should be returned to the global pool or
+not when a deallocation occurs.
+</p>
+
+<p>
+When the application requests memory (calling allocate()) we first
+look at the requested size and if this is &gt; _S_max_bytes we call new()
+directly and return.
+</p>
+
+<p>
+If the requested size is within limits we start by finding out from which
+bin we should serve this request by looking in _S_binmap.
+</p>
+
+<p>
+A call to _S_get_thread_id() returns the thread id for the calling thread
+(and if no value has been set in _S_thread_key, a new id is assigned and
+returned).
+</p>
+
+<p>
+A quick look at _S_bin[ bin ].first[ thread_id ] tells us if there are
+any blocks of this size on the current threads freelist. If this is
+not NULL - fine, just remove the block that _S_bin[ bin ].first[
+thread_id ] points to from the list, update _S_bin[ bin ].first[
+thread_id ], update the free and used counters and return a pointer to
+that blocks data.
+</p>
+
+<p>
+If the freelist is empty (the pointer is NULL) we start by looking at
+the global freelist (0). If there are blocks available on the global
+freelist we lock this bins mutex and move up to block_count (the
+number of blocks of this bins size that will fit into a _S_chunk_size)
+or until end of list - whatever comes first - to the current threads
+freelist and at the same time change the thread_id ownership and
+update the counters and pointers. When the bins mutex has been
+unlocked, we remove the block that _S_bin[ bin ].first[ thread_id ]
+points to from the list, update _S_bin[ bin ].first[ thread_id ],
+update the free and used counters, and return a pointer to that blocks
+data.
+</p>
+
+<p>
+The reason that the number of blocks moved to the current threads
+freelist is limited to block_count is to minimize the chance that a
+subsequent deallocate() call will return the excess blocks to the
+global freelist (based on the _S_freelist_headroom calculation, see
+below).
+</p>
+
+<p>
+However if there isn't any memory on the global pool we need to get
+memory from the system - this is done in exactly the same way as in a
+single threaded application with one major difference; the list built
+in the newly allocated memory (of _S_chunk_size size) is added to the
+current threads freelist instead of to the global.
+</p>
+
+<p>
+The basic process of a deallocation call is simple: always add the
+block to the front of the current threads freelist and update the
+counters and pointers (as described earlier with the specific check of
+ownership that causes the used counter of the thread that originally
+allocated the block to be decreased instead of the current threads
+counter).
+</p>
+
+<p>
+And here comes the free and used counters to service. Each time a
+deallocation() call is made, the length of the current threads
+freelist is compared to the amount memory in use by this thread.
+</p>
+
+<p>
+Let's go back to the example of an application that has one thread
+that does all the allocations and one that deallocates. Both these
+threads use say 516 32-byte blocks that was allocated during thread
+creation for example. Their used counters will both say 516 at this
+point. The allocation thread now grabs 1000 32-byte blocks and puts
+them in a shared container. The used counter for this thread is now
+1516.
+</p>
+
+<p>
+The deallocation thread now deallocates 500 of these blocks. For each
+deallocation made the used counter of the allocating thread is
+decreased and the freelist of the deallocation thread gets longer and
+longer. But the calculation made in deallocate() will limit the length
+of the freelist in the deallocation thread to _S_freelist_headroom %
+of it's used counter. In this case, when the freelist (given that the
+_S_freelist_headroom is at it's default value of 10%) exceeds 52
+(516/10) blocks will be returned to the global pool where the
+allocating thread may pick them up and reuse them.
+</p>
+
+<p>
+In order to reduce lock contention (since this requires this bins
+mutex to be locked) this operation is also made in chunks of blocks
+(just like when chunks of blocks are moved from the global freelist to
+a threads freelist mentioned above). The "formula" used can probably
+be improved to further reduce the risk of blocks being "bounced back
+and forth" between freelists.
+</p>
+
+<hr />
+<p>Return <a href="#top">to the top of the page</a> or
+ <a href="http://gcc.gnu.org/libstdc++/">to the libstdc++ homepage</a>.
+</p>
+
+
+<!-- ####################################################### -->
+
+<hr />
+<p class="fineprint"><em>
+See <a href="../17_intro/license.html">license.html</a> for copying conditions.
+Comments and suggestions are welcome, and may be sent to
+<a href="mailto:libstdc++@gcc.gnu.org">the libstdc++ mailing list</a>.
+</em></p>
+
+
+</body>
+</html>
diff --git a/libstdc++-v3/include/bits/atomicity.h b/libstdc++-v3/include/bits/atomicity.h
new file mode 100644
index 00000000000..d2620b08e5d
--- /dev/null
+++ b/libstdc++-v3/include/bits/atomicity.h
@@ -0,0 +1,46 @@
+// Low-level functions for atomic operations -*- C++ -*-
+
+// Copyright (C) 2004 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING. If not, write to the Free
+// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+// USA.
+
+// As a special exception, you may use this file as part of a free software
+// library without restriction. Specifically, if other files instantiate
+// templates or use macros or inline functions from this file, or you compile
+// this file and link it with other files to produce an executable, this
+// file does not by itself cause the resulting executable to be covered by
+// the GNU General Public License. This exception does not however
+// invalidate any other reasons why the executable file might be covered by
+// the GNU General Public License.
+
+#ifndef _GLIBCXX_ATOMICITY_H
+#define _GLIBCXX_ATOMICITY_H 1
+
+#include <bits/atomic_word.h>
+
+namespace __gnu_cxx
+{
+ _Atomic_word
+ __attribute__ ((__unused__))
+ __exchange_and_add(volatile _Atomic_word* __mem, int __val);
+
+ void
+ __attribute__ ((__unused__))
+ __atomic_add(volatile _Atomic_word* __mem, int __val);
+} // namespace __gnu_cxx
+
+#endif
diff --git a/libstdc++-v3/include/ext/demangle.h b/libstdc++-v3/include/ext/demangle.h
new file mode 100644
index 00000000000..5de4f04a224
--- /dev/null
+++ b/libstdc++-v3/include/ext/demangle.h
@@ -0,0 +1,2789 @@
+// C++ IA64 / g++ v3 demangler -*- C++ -*-
+
+// Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+// Written by Carlo Wood <carlo@alinoe.com>
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING. If not, write to the Free
+// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+// USA.
+
+// As a special exception, you may use this file as part of a free software
+// library without restriction. Specifically, if other files instantiate
+// templates or use macros or inline functions from this file, or you compile
+// this file and link it with other files to produce an executable, this
+// file does not by itself cause the resulting executable to be covered by
+// the GNU General Public License. This exception does not however
+// invalidate any other reasons why the executable file might be covered by
+// the GNU General Public License.
+
+// This file implements demangling of "C++ ABI for Itanium"-mangled symbol
+// and type names as described in Revision 1.73 of the C++ ABI as can be found
+// at http://www.codesourcery.com/cxx-abi/abi.html#mangling
+
+#ifndef _DEMANGLER_H
+#define _DEMANGLER_H 1
+
+#include <vector>
+#include <string>
+#include <ext/new_allocator.h>
+
+#ifndef _GLIBCXX_DEMANGLER_DEBUG
+#define _GLIBCXX_DEMANGLER_CWDEBUG 0
+#define _GLIBCXX_DEMANGLER_DEBUG(x)
+#define _GLIBCXX_DEMANGLER_DOUT(cntrl, data)
+#define _GLIBCXX_DEMANGLER_DOUT_ENTERING(x)
+#define _GLIBCXX_DEMANGLER_DOUT_ENTERING2(x)
+#define _GLIBCXX_DEMANGLER_DOUT_ENTERING3(x)
+#define _GLIBCXX_DEMANGLER_RETURN return M_result
+#define _GLIBCXX_DEMANGLER_RETURN2 return M_result
+#define _GLIBCXX_DEMANGLER_RETURN3
+#define _GLIBCXX_DEMANGLER_FAILURE \
+ do { M_result = false; return false; } while(0)
+#else
+#define _GLIBCXX_DEMANGLER_CWDEBUG 1
+#endif
+
+namespace __gnu_cxx
+{
+ namespace demangler
+ {
+ enum substitution_nt
+ {
+ type,
+ template_template_param,
+ nested_name_prefix,
+ nested_name_template_prefix,
+ unscoped_template_name
+ };
+
+ struct substitution_st
+ {
+ int M_start_pos;
+ substitution_nt M_type;
+ int M_number_of_prefixes;
+
+ substitution_st(int start_pos,
+ substitution_nt type,
+ int number_of_prefixes)
+ : M_start_pos(start_pos), M_type(type),
+ M_number_of_prefixes(number_of_prefixes)
+ { }
+ };
+
+ enum simple_qualifier_nt
+ {
+ complex_or_imaginary = 'G',
+ pointer = 'P',
+ reference = 'R'
+ };
+
+ enum cv_qualifier_nt
+ {
+ cv_qualifier = 'K'
+ };
+
+ enum param_qualifier_nt
+ {
+ vendor_extension = 'U',
+ array = 'A',
+ pointer_to_member = 'M'
+ };
+
+ template<typename Tp, typename Allocator = __gnu_cxx::new_allocator<Tp> >
+ class qualifier;
+
+ template<typename Tp, typename Allocator = __gnu_cxx::new_allocator<Tp> >
+ class qualifier_list;
+
+ template<typename Tp, typename Allocator = __gnu_cxx::new_allocator<Tp> >
+ class session;
+
+ template<typename Tp, typename Allocator>
+ class qualifier
+ {
+ typedef typename Allocator::template rebind<char>::other
+ char_Allocator;
+ typedef std::basic_string<char, std::char_traits<char>, char_Allocator>
+ string_type;
+
+ private:
+ char M_qualifier1;
+ char M_qualifier2;
+ char M_qualifier3;
+ mutable unsigned char M_cnt;
+ string_type M_optional_type;
+ int M_start_pos;
+ bool M_part_of_substitution;
+
+ public:
+ qualifier(int start_pos,
+ simple_qualifier_nt simple_qualifier,
+ int inside_substitution)
+ : M_qualifier1(simple_qualifier),
+ M_start_pos(start_pos),
+ M_part_of_substitution(inside_substitution)
+ { }
+
+ qualifier(int start_pos,
+ cv_qualifier_nt,
+ char const* start,
+ int count,
+ int inside_substitution)
+ : M_qualifier1(start[0]),
+ M_qualifier2((count > 1) ? start[1] : '\0'),
+ M_qualifier3((count > 2) ? start[2] : '\0'),
+ M_start_pos(start_pos),
+ M_part_of_substitution(inside_substitution)
+ { }
+
+ qualifier(int start_pos,
+ param_qualifier_nt param_qualifier,
+ string_type optional_type,
+ int inside_substitution)
+ : M_qualifier1(param_qualifier),
+ M_optional_type(optional_type),
+ M_start_pos(start_pos),
+ M_part_of_substitution(inside_substitution)
+ { }
+
+ int
+ get_start_pos(void) const
+ { return M_start_pos; }
+
+ char
+ first_qualifier(void) const
+ { M_cnt = 1; return M_qualifier1; }
+
+ char
+ next_qualifier(void) const
+ {
+ return (++M_cnt == 2) ? M_qualifier2
+ : ((M_cnt == 3) ? M_qualifier3 : 0);
+ }
+
+ string_type const&
+ get_optional_type(void) const
+ { return M_optional_type; }
+
+ bool
+ part_of_substitution(void) const
+ { return M_part_of_substitution; }
+
+#if _GLIBCXX_DEMANGLER_CWDEBUG
+ friend std::ostream& operator<<(std::ostream& os, qualifier const& qual)
+ {
+ os << (char)qual.M_qualifier1;
+ if (qual.M_qualifier1 == vendor_extension ||
+ qual.M_qualifier1 == array ||
+ qual.M_qualifier1 == pointer_to_member)
+ os << " [" << qual.M_optional_type << ']';
+ else if (qual.M_qualifier1 == 'K' ||
+ qual.M_qualifier1 == 'V' ||
+ qual.M_qualifier1 == 'r')
+ {
+ if (qual.M_qualifier2)
+ {
+ os << (char)qual.M_qualifier2;
+ if (qual.M_qualifier3)
+ os << (char)qual.M_qualifier3;
+ }
+ }
+ return os;
+ }
+#endif
+ };
+
+ template<typename Tp, typename Allocator>
+ class qualifier_list
+ {
+ typedef typename Allocator::template rebind<char>::other
+ char_Allocator;
+ typedef std::basic_string<char, std::char_traits<char>, char_Allocator>
+ string_type;
+
+ private:
+ mutable bool M_printing_suppressed;
+ typedef qualifier<Tp, Allocator> qual;
+ typedef typename Allocator::template rebind<qual>::other qual_Allocator;
+ typedef std::vector<qual, qual_Allocator> qual_vector;
+ qual_vector M_qualifier_starts;
+ session<Tp, Allocator>& M_demangler;
+
+ void decode_KVrA(string_type& prefix, string_type& postfix, int cvq,
+ typename qual_vector::
+ const_reverse_iterator const& iter_array) const;
+
+ public:
+ qualifier_list(session<Tp, Allocator>& demangler_obj)
+ : M_printing_suppressed(false), M_demangler(demangler_obj)
+ { }
+
+ void
+ add_qualifier_start(simple_qualifier_nt simple_qualifier,
+ int start_pos,
+ int inside_substitution)
+ { M_qualifier_starts.
+ push_back(qualifier<Tp, Allocator>(start_pos,
+ simple_qualifier, inside_substitution)); }
+
+ void
+ add_qualifier_start(cv_qualifier_nt cv_qualifier,
+ int start_pos,
+ int count,
+ int inside_substitution)
+ { M_qualifier_starts.
+ push_back(qualifier<Tp, Allocator>(start_pos,
+ cv_qualifier, &M_demangler.M_str[start_pos],
+ count, inside_substitution)); }
+
+ void
+ add_qualifier_start(param_qualifier_nt param_qualifier,
+ int start_pos,
+ string_type optional_type,
+ int inside_substitution)
+ { M_qualifier_starts.
+ push_back(qualifier<Tp, Allocator>(start_pos,
+ param_qualifier, optional_type, inside_substitution)); }
+
+ void
+ decode_qualifiers(string_type& prefix,
+ string_type& postfix,
+ bool member_function_pointer_qualifiers) const;
+
+ bool
+ suppressed(void) const
+ { return M_printing_suppressed; }
+
+ void
+ printing_suppressed(void)
+ { M_printing_suppressed = true; }
+
+ size_t
+ size(void) const
+ { return M_qualifier_starts.size(); }
+
+#if _GLIBCXX_DEMANGLER_CWDEBUG
+ friend std::ostream& operator<<(std::ostream& os, qualifier_list const& list)
+ {
+ typename qual_vector::const_iterator
+ iter = list.M_qualifier_starts.begin();
+ if (iter != list.M_qualifier_starts.end())
+ {
+ os << "{ " << *iter;
+ while (++iter != list.M_qualifier_starts.end())
+ os << ", " << *iter;
+ os << " }";
+ }
+ else
+ os << "{ }";
+ return os;
+ }
+#endif
+ };
+
+ struct implementation_details
+ {
+ private:
+ unsigned int M_style;
+
+ public:
+ // The following flags change the behaviour of the demangler. The
+ // default behaviour is that none of these flags is set.
+
+ static unsigned int const style_void = 1;
+ // Default behaviour: int f()
+ // Use (void) instead of (): int f(void)
+
+ static unsigned int const style_literal = 2;
+ // Default behaviour: (long)13,
+ // (unsigned long long)19
+ // Use extensions 'u', 'l' and 'll' for integral
+ // literals (as in template arguments): 13l, 19ull
+
+ static unsigned int const style_literal_int = 4;
+ // Default behaviour: 4
+ // Use also an explicit
+ // cast for int in literals: (int)4
+
+ static unsigned int const style_compact_expr_ops = 8;
+ // Default behaviour: (i) < (3), sizeof (int)
+ // Don't output spaces around
+ // operators in expressions: (i)<(3), sizeof(int)
+
+ static unsigned int const style_sizeof_typename = 16;
+ // Default behaviour: sizeof (X::t)
+ // Put 'typename' infront of <nested-name>
+ // types inside a 'sizeof': sizeof (typename X::t)
+
+ public:
+ implementation_details(unsigned int style_flags = 0) :
+ M_style(style_flags) { }
+ virtual ~implementation_details() { }
+ bool get_style_void(void) const
+ { return (M_style & style_void); }
+ bool get_style_literal(void) const
+ { return (M_style & style_literal); }
+ bool get_style_literal_int(void) const
+ { return (M_style & style_literal_int); }
+ bool get_style_compact_expr_ops(void) const
+ { return (M_style & style_compact_expr_ops); }
+ bool get_style_sizeof_typename(void) const
+ { return (M_style & style_sizeof_typename); }
+ // This can be overridden by user implementations.
+ virtual bool decode_real(char* /* output */, unsigned long* /* input */,
+ size_t /* size_of_real */) const
+ { return false; }
+ };
+
+ template<typename Tp, typename Allocator>
+ class session
+ {
+ public:
+ friend class qualifier_list<Tp, Allocator>;
+ typedef typename Allocator::template rebind<char>::other
+ char_Allocator;
+ typedef std::basic_string<char, std::char_traits<char>, char_Allocator>
+ string_type;
+
+ private:
+ char const* M_str;
+ int M_pos;
+ int M_maxpos;
+ bool M_result;
+ int M_inside_template_args;
+ int M_inside_type;
+ int M_inside_substitution;
+ bool M_saw_destructor;
+ bool M_name_is_cdtor;
+ bool M_name_is_template;
+ bool M_name_is_conversion_operator;
+ bool M_template_args_need_space;
+ string_type M_function_name;
+ typedef typename Allocator::template rebind<int>::other
+ int_Allocator;
+ typedef typename Allocator::template rebind<substitution_st>::other
+ subst_Allocator;
+ std::vector<int, int_Allocator> M_template_arg_pos;
+ int M_template_arg_pos_offset;
+ std::vector<substitution_st, subst_Allocator> M_substitutions_pos;
+ implementation_details const& M_implementation_details;
+ typedef typename Allocator::template
+ rebind<qualifier_list<Allocator> >::other qualifier_list_Allocator;
+ qualifier_list_Allocator M_qualifier_list_alloc;
+#if _GLIBCXX_DEMANGLER_CWDEBUG
+ bool M_inside_add_substitution;
+#endif
+
+ public:
+ explicit session(char const* in, int len,
+ implementation_details const& id = implementation_details())
+ : M_str(in), M_pos(0), M_maxpos(len - 1), M_result(true),
+ M_inside_template_args(0), M_inside_type(0),
+ M_inside_substitution(0), M_saw_destructor(false),
+ M_name_is_cdtor(false), M_name_is_template(false),
+ M_name_is_conversion_operator(false),
+ M_template_args_need_space(false), M_template_arg_pos_offset(0),
+ M_implementation_details(id)
+#if _GLIBCXX_DEMANGLER_CWDEBUG
+ , M_inside_add_substitution(false)
+#endif
+ { }
+
+ static int
+ decode_encoding(string_type& output, char const* input, int len,
+ implementation_details const& id = implementation_details());
+
+ bool
+ decode_type(string_type& output,
+ qualifier_list<Tp, Allocator>* qualifiers = NULL)
+ {
+ string_type postfix;
+ bool res = decode_type_with_postfix(output, postfix, qualifiers);
+ output += postfix;
+ return res;
+ }
+
+ bool
+ remaining_input_characters(void) const
+ { return current() != 0; }
+
+ private:
+ char
+ current(void) const
+ { return (M_pos > M_maxpos) ? 0 : M_str[M_pos]; }
+
+ char
+ next_peek(void) const
+ { return (M_pos >= M_maxpos) ? 0 : M_str[M_pos + 1]; }
+
+ char
+ next(void)
+ { return (M_pos >= M_maxpos) ? 0 : M_str[++M_pos]; }
+
+ char
+ eat_current(void)
+ { return (M_pos > M_maxpos) ? 0 : M_str[M_pos++]; }
+
+ void
+ store(int& saved_pos)
+ { saved_pos = M_pos; }
+
+ void
+ restore(int saved_pos)
+ { M_pos = saved_pos; M_result = true; }
+
+ void
+ add_substitution(int start_pos,
+ substitution_nt sub_type,
+ int number_of_prefixes);
+
+ bool decode_type_with_postfix(string_type& prefix,
+ string_type& postfix, qualifier_list<Tp, Allocator>* qualifiers = NULL);
+ bool decode_bare_function_type(string_type& output);
+ bool decode_builtin_type(string_type& output);
+ bool decode_call_offset(string_type& output);
+ bool decode_class_enum_type(string_type& output);
+ bool decode_expression(string_type& output);
+ bool decode_literal(string_type& output);
+ bool decode_local_name(string_type& output);
+ bool decode_name(string_type& output,
+ string_type& nested_name_qualifiers);
+ bool decode_nested_name(string_type& output,
+ string_type& qualifiers);
+ bool decode_number(string_type& output);
+ bool decode_operator_name(string_type& output);
+ bool decode_source_name(string_type& output);
+ bool decode_substitution(string_type& output,
+ qualifier_list<Tp, Allocator>* qualifiers = NULL);
+ bool decode_template_args(string_type& output);
+ bool decode_template_param(string_type& output,
+ qualifier_list<Tp, Allocator>* qualifiers = NULL);
+ bool decode_unqualified_name(string_type& output);
+ bool decode_unscoped_name(string_type& output);
+ bool decode_non_negative_decimal_integer(string_type& output);
+ bool decode_special_name(string_type& output);
+ bool decode_real(string_type& output, size_t size_of_real);
+ };
+
+ template<typename Tp, typename Allocator>
+#if !_GLIBCXX_DEMANGLER_CWDEBUG
+ inline
+#endif
+ void
+ session<Tp, Allocator>::add_substitution(int start_pos,
+ substitution_nt sub_type,
+ int number_of_prefixes = 0)
+ {
+ if (!M_inside_substitution)
+ {
+#if _GLIBCXX_DEMANGLER_CWDEBUG
+ if (M_inside_add_substitution)
+ return;
+#endif
+ M_substitutions_pos.
+ push_back(substitution_st(start_pos,
+ sub_type, number_of_prefixes));
+#if _GLIBCXX_DEMANGLER_CWDEBUG
+ if (!DEBUGCHANNELS::dc::demangler.is_on())
+ return;
+ string_type substitution_name("S");
+ int n = M_substitutions_pos.size() - 1;
+ if (n > 0)
+ substitution_name += (n <= 10) ? (char)(n + '0' - 1)
+ : (char)(n + 'A' - 11);
+ substitution_name += '_';
+ string_type subst;
+ int saved_pos = M_pos;
+ M_pos = start_pos;
+ M_inside_add_substitution = true;
+ _GLIBCXX_DEMANGLER_DEBUG( dc::demangler.off() );
+ switch(sub_type)
+ {
+ case type:
+ decode_type(subst);
+ break;
+ case template_template_param:
+ decode_template_param(subst);
+ break;
+ case nested_name_prefix:
+ case nested_name_template_prefix:
+ for (int cnt = number_of_prefixes; cnt > 0; --cnt)
+ {
+ if (current() == 'I')
+ {
+ subst += ' ';
+ decode_template_args(subst);
+ }
+ else
+ {
+ if (cnt < number_of_prefixes)
+ subst += "::";
+ if (current() == 'S')
+ decode_substitution(subst);
+ else if (current() == 'T')
+ decode_template_param(subst);
+ else
+ decode_unqualified_name(subst);
+ }
+ }
+ break;
+ case unscoped_template_name:
+ decode_unscoped_name(subst);
+ break;
+ }
+ M_pos = saved_pos;
+ _GLIBCXX_DEMANGLER_DEBUG( dc::demangler.on() );
+ _GLIBCXX_DEMANGLER_DOUT(dc::demangler,
+ "Adding substitution " << substitution_name
+ << " : " << subst
+ << " (from " << location_ct((char*)__builtin_return_address(0)
+ + builtin_return_address_offset)
+ << " <- " << location_ct((char*)__builtin_return_address(1)
+ + builtin_return_address_offset)
+ << " <- " << location_ct((char*)__builtin_return_address(2)
+ + builtin_return_address_offset)
+ << ").");
+ M_inside_add_substitution = false;
+#endif
+ }
+ }
+
+ // We don't want to depend on locale (or include <cctype> for that matter).
+ // We also don't want to use "safe-ctype.h" because that headerfile is not
+ // available to the users.
+ inline bool isdigit(char c) { return c >= '0' && c <= '9'; }
+ inline bool islower(char c) { return c >= 'a' && c <= 'z'; }
+ inline bool isupper(char c) { return c >= 'A' && c <= 'Z'; }
+ inline char tolower(char c) { return isupper(c) ? c - 'A' + 'a' : c; }
+
+ //
+ // <non-negative decimal integer> ::= 0
+ // ::= 1|2|3|4|5|6|7|8|9 [<digit>+]
+ // <digit> ::= 0|1|2|3|4|5|6|7|8|9
+ //
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::
+ decode_non_negative_decimal_integer(string_type& output)
+ {
+ char c = current();
+ if (c == '0')
+ {
+ output += '0';
+ eat_current();
+ }
+ else if (!isdigit(c))
+ M_result = false;
+ else
+ {
+ do
+ {
+ output += c;
+ }
+ while (isdigit((c = next())));
+ }
+ return M_result;
+ }
+
+ // <number> ::= [n] <non-negative decimal integer>
+ //
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::decode_number(string_type& output)
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_number");
+ if (current() != 'n')
+ decode_non_negative_decimal_integer(output);
+ else
+ {
+ output += '-';
+ eat_current();
+ decode_non_negative_decimal_integer(output);
+ }
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+
+ // <builtin-type> ::= v # void
+ // ::= w # wchar_t
+ // ::= b # bool
+ // ::= c # char
+ // ::= a # signed char
+ // ::= h # unsigned char
+ // ::= s # short
+ // ::= t # unsigned short
+ // ::= i # int
+ // ::= j # unsigned int
+ // ::= l # long
+ // ::= m # unsigned long
+ // ::= x # long long, __int64
+ // ::= y # unsigned long long, __int64
+ // ::= n # __int128
+ // ::= o # unsigned __int128
+ // ::= f # float
+ // ::= d # double
+ // ::= e # long double, __float80
+ // ::= g # __float128
+ // ::= z # ellipsis
+ // ::= u <source-name> # vendor extended type
+ //
+ char const* const builtin_type_c[26] =
+ {
+ "signed char", // a
+ "bool", // b
+ "char", // c
+ "double", // d
+ "long double", // e
+ "float", // f
+ "__float128", // g
+ "unsigned char", // h
+ "int", // i
+ "unsigned int", // j
+ NULL, // k
+ "long", // l
+ "unsigned long", // m
+ "__int128", // n
+ "unsigned __int128", // o
+ NULL, // p
+ NULL, // q
+ NULL, // r
+ "short", // s
+ "unsigned short", // t
+ NULL, // u
+ "void", // v
+ "wchar_t", // w
+ "long long", // x
+ "unsigned long long", // y
+ "..." // z
+ };
+
+ //
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::decode_builtin_type(string_type& output)
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_builtin_type");
+ char const* bt;
+ if (!islower(current()) || !(bt = builtin_type_c[current() - 'a']))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ output += bt;
+ eat_current();
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+
+ // <class-enum-type> ::= <name>
+ //
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::decode_class_enum_type(string_type& output)
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_class_enum_type");
+ string_type nested_name_qualifiers;
+ if (!decode_name(output, nested_name_qualifiers))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ output += nested_name_qualifiers;
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+
+ // <substitution> ::=
+ // S <seq-id> _
+ // S_
+ // St # ::std::
+ // Sa # ::std::allocator
+ // Sb # ::std::basic_string
+ // Ss # ::std::basic_string<char, std::char_traits<char>,
+ // std::allocator<char> >
+ // Si # ::std::basic_istream<char, std::char_traits<char> >
+ // So # ::std::basic_ostream<char, std::char_traits<char> >
+ // Sd # ::std::basic_iostream<char, std::char_traits<char> >
+ //
+ // <seq-id> ::=
+ // 0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z
+ // [<seq-id>] # Base 36 number
+ //
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::decode_substitution(string_type& output,
+ qualifier_list<Tp, Allocator>* qualifiers)
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_substitution");
+ unsigned int value = 0;
+ char c = next();
+ if (c != '_')
+ {
+ switch(c)
+ {
+ case 'a':
+ {
+ output += "std::allocator";
+ if (!M_inside_template_args)
+ {
+ M_function_name = "allocator";
+ M_name_is_template = true;
+ M_name_is_cdtor = false;
+ M_name_is_conversion_operator = false;
+ }
+ eat_current();
+ if (qualifiers)
+ qualifiers->printing_suppressed();
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ case 'b':
+ {
+ output += "std::basic_string";
+ if (!M_inside_template_args)
+ {
+ M_function_name = "basic_string";
+ M_name_is_template = true;
+ M_name_is_cdtor = false;
+ M_name_is_conversion_operator = false;
+ }
+ eat_current();
+ if (qualifiers)
+ qualifiers->printing_suppressed();
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ case 'd':
+ output += "std::iostream";
+ if (!M_inside_template_args)
+ {
+ M_function_name = "iostream";
+ M_name_is_template = true;
+ M_name_is_cdtor = false;
+ M_name_is_conversion_operator = false;
+ }
+ eat_current();
+ if (qualifiers)
+ qualifiers->printing_suppressed();
+ _GLIBCXX_DEMANGLER_RETURN;
+ case 'i':
+ output += "std::istream";
+ if (!M_inside_template_args)
+ {
+ M_function_name = "istream";
+ M_name_is_template = true;
+ M_name_is_cdtor = false;
+ M_name_is_conversion_operator = false;
+ }
+ eat_current();
+ if (qualifiers)
+ qualifiers->printing_suppressed();
+ _GLIBCXX_DEMANGLER_RETURN;
+ case 'o':
+ output += "std::ostream";
+ if (!M_inside_template_args)
+ {
+ M_function_name = "ostream";
+ M_name_is_template = true;
+ M_name_is_cdtor = false;
+ M_name_is_conversion_operator = false;
+ }
+ eat_current();
+ if (qualifiers)
+ qualifiers->printing_suppressed();
+ _GLIBCXX_DEMANGLER_RETURN;
+ case 's':
+ output += "std::string";
+ if (!M_inside_template_args)
+ {
+ M_function_name = "string";
+ M_name_is_template = true;
+ M_name_is_cdtor = false;
+ M_name_is_conversion_operator = false;
+ }
+ eat_current();
+ if (qualifiers)
+ qualifiers->printing_suppressed();
+ _GLIBCXX_DEMANGLER_RETURN;
+ case 't':
+ output += "std";
+ eat_current();
+ if (qualifiers)
+ qualifiers->printing_suppressed();
+ _GLIBCXX_DEMANGLER_RETURN;
+ default:
+ for(;; c = next())
+ {
+ if (isdigit(c))
+ value = value * 36 + c - '0';
+ else if (isupper(c))
+ value = value * 36 + c - 'A' + 10;
+ else if (c == '_')
+ break;
+ else
+ _GLIBCXX_DEMANGLER_FAILURE;
+ }
+ ++value;
+ break;
+ }
+ }
+ eat_current();
+ if (value >= M_substitutions_pos.size() ||
+ M_inside_type > 20) // Rather than core dump.
+ _GLIBCXX_DEMANGLER_FAILURE;
+ ++M_inside_substitution;
+ int saved_pos = M_pos;
+ substitution_st& substitution(M_substitutions_pos[value]);
+ M_pos = substitution.M_start_pos;
+ switch(substitution.M_type)
+ {
+ case type:
+ decode_type(output, qualifiers);
+ break;
+ case template_template_param:
+ decode_template_param(output, qualifiers);
+ break;
+ case nested_name_prefix:
+ case nested_name_template_prefix:
+ for (int cnt = substitution.M_number_of_prefixes; cnt > 0; --cnt)
+ {
+ if (current() == 'I')
+ {
+ if (M_template_args_need_space)
+ output += ' ';
+ M_template_args_need_space = false;
+ if (!decode_template_args(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ }
+ else
+ {
+ if (cnt < substitution.M_number_of_prefixes)
+ output += "::";
+ if (current() == 'S')
+ {
+ if (!decode_substitution(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ }
+ else if (!decode_unqualified_name(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ }
+ }
+ if (qualifiers)
+ qualifiers->printing_suppressed();
+ break;
+ case unscoped_template_name:
+ decode_unscoped_name(output);
+ if (qualifiers)
+ qualifiers->printing_suppressed();
+ break;
+ }
+ M_pos = saved_pos;
+ --M_inside_substitution;
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+
+ // <template-param> ::= T_ # first template parameter
+ // ::= T <parameter-2 non-negative number> _
+ //
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::decode_template_param(string_type& output,
+ qualifier_list<Tp, Allocator>* qualifiers)
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_template_parameter");
+ if (current() != 'T')
+ _GLIBCXX_DEMANGLER_FAILURE;
+ unsigned int value = 0;
+ char c;
+ if ((c = next()) != '_')
+ {
+ while(isdigit(c))
+ {
+ value = value * 10 + c - '0';
+ c = next();
+ }
+ ++value;
+ }
+ if (eat_current() != '_')
+ _GLIBCXX_DEMANGLER_FAILURE;
+ value += M_template_arg_pos_offset;
+ if (value >= M_template_arg_pos.size())
+ _GLIBCXX_DEMANGLER_FAILURE;
+ int saved_pos = M_pos;
+ M_pos = M_template_arg_pos[value];
+ if (M_inside_type > 20) // Rather than core dump.
+ _GLIBCXX_DEMANGLER_FAILURE;
+ ++M_inside_substitution;
+ if (current() == 'X')
+ {
+ eat_current();
+ decode_expression(output);
+ }
+ else if (current() == 'L')
+ decode_literal(output);
+ else
+ decode_type(output, qualifiers);
+ --M_inside_substitution;
+ M_pos = saved_pos;
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::decode_real(string_type& output, size_t size_of_real)
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_real");
+
+ unsigned long words[4]; // 32 bit per long, maximum of 128 bits.
+ unsigned long* word = &words[0];
+
+ int saved_pos;
+ store(saved_pos);
+
+ // The following assumes that leading zeroes are also included in the
+ // mangled name, I am not sure that is conforming to the C++-ABI, but
+ // it is what g++ does.
+ unsigned char nibble, c = current();
+ for(size_t word_cnt = size_of_real / 4; word_cnt > 0; --word_cnt)
+ {
+ for (int nibble_cnt = 0; nibble_cnt < 8; ++nibble_cnt)
+ {
+ // Translate character into nibble.
+ if (c < '0' || c > 'f')
+ _GLIBCXX_DEMANGLER_FAILURE;
+ if (c <= '9')
+ nibble = c - '0';
+ else if (c >= 'a')
+ nibble = c - 'a' + 10;
+ else
+ _GLIBCXX_DEMANGLER_FAILURE;
+ // Write nibble into word array.
+ if (nibble_cnt == 0)
+ *word = nibble << 28;
+ else
+ *word |= (nibble << (28 - 4 * nibble_cnt));
+ c = next();
+ }
+ ++word;
+ }
+ char buf[24];
+ if (M_implementation_details.decode_real(buf, words, size_of_real))
+ {
+ output += buf;
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ restore(saved_pos);
+
+ output += '[';
+ c = current();
+ for(size_t nibble_cnt = 0; nibble_cnt < 2 * size_of_real; ++nibble_cnt)
+ {
+ if (c < '0' || c > 'f' || (c > '9' && c < 'a'))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ output += c;
+ c = next();
+ }
+ output += ']';
+
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::decode_literal(string_type& output)
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_literal");
+ eat_current(); // Eat the 'L'.
+ if (current() == '_')
+ {
+ if (next() != 'Z')
+ _GLIBCXX_DEMANGLER_FAILURE;
+ eat_current();
+ if ((M_pos += decode_encoding(output, M_str + M_pos,
+ M_maxpos - M_pos + 1, M_implementation_details)) < 0)
+ _GLIBCXX_DEMANGLER_FAILURE;
+ }
+ else
+ {
+ // Special cases
+ if (current() == 'b')
+ {
+ if (next() == '0')
+ output += "false";
+ else
+ output += "true";
+ eat_current();
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ char c = current();
+ if ((c == 'i' || c == 'j' || c == 'l' ||
+ c == 'm' || c == 'x' || c == 'y') &&
+ M_implementation_details.get_style_literal())
+ eat_current();
+ else if (c == 'i' &&
+ !M_implementation_details.get_style_literal_int())
+ eat_current();
+ else
+ {
+ output += '(';
+ if (!decode_type(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ output += ')';
+ }
+ if (c >= 'd' && c <= 'g')
+ {
+ size_t size_of_real = (c == 'd') ? sizeof(double) :
+ ((c == 'f') ? sizeof(float) :
+ (c == 'e') ? sizeof(long double) : 16);
+ if (!decode_real(output, size_of_real))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ }
+ else if (!decode_number(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ if (M_implementation_details.get_style_literal())
+ {
+ if (c == 'j' || c == 'm' || c == 'y')
+ output += 'u';
+ if (c == 'l' || c == 'm')
+ output += 'l';
+ if (c == 'x' || c == 'y')
+ output += "ll";
+ }
+ }
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+
+ // <operator-name> ::=
+ // nw # new
+ // na # new[]
+ // dl # delete
+ // da # delete[]
+ // ps # + (unary)
+ // ng # - (unary)
+ // ad # & (unary)
+ // de # * (unary)
+ // co # ~
+ // pl # +
+ // mi # -
+ // ml # *
+ // dv # /
+ // rm # %
+ // an # &
+ // or # |
+ // eo # ^
+ // aS # =
+ // pL # +=
+ // mI # -=
+ // mL # *=
+ // dV # /=
+ // rM # %=
+ // aN # &=
+ // oR # |=
+ // eO # ^=
+ // ls # <<
+ // rs # >>
+ // lS # <<=
+ // rS # >>=
+ // eq # ==
+ // ne # !=
+ // lt # <
+ // gt # >
+ // le # <=
+ // ge # >=
+ // nt # !
+ // aa # &&
+ // oo # ||
+ // pp # ++
+ // mm # --
+ // cm # ,
+ // pm # ->*
+ // pt # ->
+ // cl # ()
+ // ix # []
+ // qu # ?
+ // st # sizeof (a type)
+ // sz # sizeof (an expression)
+ // cv <type> # (cast)
+ // v <digit> <source-name> # vendor extended operator
+ //
+ // Symbol operator codes exist of two characters, we need to find a
+ // quick hash so that their names can be looked up in a table.
+ //
+ // The puzzle :)
+ // Shift the rows so that there is at most one character per column.
+ //
+ // A perfect solution (Oh no, it's THE MATRIX!):
+ // horizontal
+ // ....................................... offset + 'a'
+ // a, a||d|||||||||n||||s|||||||||||||||||||| 0
+ // c, || |||||||lm o||| |||||||||||||||||||| 0
+ // d, || a|||e|| l|| ||||||v||||||||||||| 4
+ // e, || ||| || || |||o|q ||||||||||||| 8
+ // g, || ||| || || e|| | ||||||||t|||| 15
+ // i, || ||| || || || | |||||||| |||x 15
+ // l, |e ||| || st || | |||||||| ||| -2
+ // m, | |i| lm || | |||||||| ||| -2
+ // n, a e g t| w |||||||| ||| 1
+ // o, | ||||o||r ||| 16
+ // p, | ||lm |p st| 17
+ // q, | u| | | 6
+ // r, m s | | 9
+ // s, t z 12
+ // .......................................
+ // ^ ^__ second character
+ // |___ first character
+ //
+
+ // Putting that solution in tables:
+
+ char const offset_table_c [1 + CHAR_MAX - CHAR_MIN ] =
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+#if (CHAR_MIN < 0)
+ // Add -CHAR_MIN extra zeroes (128):
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // a b c d e f g h i j k
+ 0, -97, 0, -97, -93, -89, 0, -82, 0, -82, 0, 0,
+ // l m n o p q r s t u v
+ -99, -99, -96, -81, -80, -91, -88, -85, 0, 0, 0,
+#else
+ // a b c d e f g h i j k
+ 0, 159, 0, 159, 163, 167, 0, 174, 0, 174, 0, 0,
+ // l m n o p q r s t u v
+ 157, 157, 160, 175, 176, 165, 168, 171, 0, 0, 0,
+#endif
+ // ... more zeros
+ };
+
+ enum xary_nt {
+ unary,
+ binary,
+ trinary
+ };
+
+ struct entry_st
+ {
+ char const* opcode;
+ char const* symbol_name;
+ xary_nt type;
+ };
+
+ entry_st const symbol_name_table_c[39] = {
+ { "aa", "operator&&", binary },
+ { "na", "operator new[]", unary },
+ { "le", "operator<=", binary },
+ { "ad", "operator&", unary },
+ { "da", "operator delete[]", unary },
+ { "ne", "operator!=", binary },
+ { "mi=", "operator-", binary },
+ { "ng", "operator-", unary },
+ { "de", "operator*", unary },
+ { "ml=", "operator*", binary },
+ { "mm", "operator--", unary },
+ { "cl", "operator()", unary },
+ { "cm", "operator,", binary },
+ { "an=", "operator&", binary },
+ { "co", "operator~", binary },
+ { "dl", "operator delete", unary },
+ { "ls=", "operator<<", binary },
+ { "lt", "operator<", binary },
+ { "as=", "operator", binary },
+ { "ge", "operator>=", binary },
+ { "nt", "operator!", unary },
+ { "rm=", "operator%", binary },
+ { "eo=", "operator^", binary },
+ { "nw", "operator new", unary },
+ { "eq", "operator==", binary },
+ { "dv=", "operator/", binary },
+ { "qu", "operator?", trinary },
+ { "rs=", "operator>>", binary },
+ { "pl=", "operator+", binary },
+ { "pm", "operator->*", binary },
+ { "oo", "operator||", binary },
+ { "st", "sizeof", unary },
+ { "pp", "operator++", unary },
+ { "or=", "operator|", binary },
+ { "gt", "operator>", binary },
+ { "ps", "operator+", unary },
+ { "pt", "operator->", binary },
+ { "sz", "sizeof", unary },
+ { "ix", "operator[]", unary }
+ };
+
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::decode_operator_name(string_type& output)
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_operator_name");
+
+ char opcode0 = current();
+ char opcode1 = tolower(next());
+
+ register char hash;
+ if ((hash = offset_table_c[opcode0 - CHAR_MIN]))
+ {
+ hash += opcode1;
+ if (
+#if (CHAR_MIN < 0)
+ hash >= 0 &&
+#endif
+ hash < 39)
+ {
+ int index = static_cast<int>(static_cast<unsigned char>(hash));
+ entry_st entry = symbol_name_table_c[index];
+ if (entry.opcode[0] == opcode0 && entry.opcode[1] == opcode1
+ && (opcode1 == current() || entry.opcode[2] == '='))
+ {
+ output += entry.symbol_name;
+ if (opcode1 != current())
+ output += '=';
+ eat_current();
+ if (hash == 16 || hash == 17)
+ M_template_args_need_space = true;
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ else if (opcode0 == 'c' && opcode1 == 'v') // casting operator
+ {
+ eat_current();
+ output += "operator ";
+ if (current() == 'T')
+ {
+ // This is a templated cast operator.
+ // It must be of the form "cvT_I...E".
+ // Let M_template_arg_pos already point
+ // to the template argument.
+ M_template_arg_pos_offset = M_template_arg_pos.size();
+ M_template_arg_pos.push_back(M_pos + 3);
+ }
+ if (!decode_type(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ if (!M_inside_template_args)
+ M_name_is_conversion_operator = true;
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ }
+ }
+ _GLIBCXX_DEMANGLER_FAILURE;
+ }
+
+ //
+ // <expression> ::= <unary operator-name> <expression>
+ // ::= <binary operator-name> <expression> <expression>
+ // ::= <trinary operator-name> <expression> <expression> <expression>
+ // ::= st <type>
+ // ::= <template-param>
+ // ::= sr <type> <unqualified-name> # dependent name
+ // ::= sr <type> <unqualified-name> <template-args> # dependent template-id
+ // ::= <expr-primary>
+ //
+ // <expr-primary> ::= L <type> <value number> E # integer literal
+ // ::= L <type> <value float> E # floating literal
+ // ::= L <mangled-name> E # external name
+ //
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::decode_expression(string_type& output)
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_expression");
+ if (current() == 'T')
+ {
+ if (!decode_template_param(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ else if (current() == 'L')
+ {
+ if (!decode_literal(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ if (current() != 'E')
+ _GLIBCXX_DEMANGLER_FAILURE;
+ eat_current();
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ else if (current() == 's')
+ {
+ char opcode1 = next();
+ if (opcode1 == 't' || opcode1 == 'z')
+ {
+ eat_current();
+ if (M_implementation_details.get_style_compact_expr_ops())
+ output += "sizeof(";
+ else
+ output += "sizeof (";
+ if (opcode1 == 't')
+ {
+ // I cannot think of a mangled name that is valid for both cases
+ // when just replacing the 't' by a 'z' or vica versa, which
+ // indicates that there is no ambiguity that dictates the need
+ // for a seperate "st" case, except to be able catch invalid
+ // mangled names. However there CAN be ambiguity in the demangled
+ // name when there are both a type and a symbol of the same name,
+ // which then leads to different encoding (of course) with
+ // sizeof (type) or sizeof (expression) respectively, but that
+ // ambiguity is not per se related to "sizeof" except that that
+ // is the only place where both a type AND an expression are valid
+ // in as part of a (template function) type.
+ //
+ // Example:
+ //
+ // struct B { typedef int t; };
+ // struct A : public B { static int t[2]; };
+ // template<int i, int j> struct C { typedef int q; };
+ // template<int i, typename T>
+ // void f(typename C<sizeof (typename T::t),
+ // sizeof (T::t)>::q) { }
+ // void instantiate() { f<5, A>(0); }
+ //
+ // Leads to _Z1fILi5E1AEvN1CIXstN1T1tEEXszsrS2_1tEE1qE which
+ // demangles as
+ // void f<5, A>(C<sizeof (T::t), sizeof (T::t)>::q)
+ //
+ // This is ambiguity is very unlikely to happen and it is kind
+ // of fuzzy to detect when adding a 'typename' makes sense.
+ //
+ if (M_implementation_details.get_style_sizeof_typename())
+ {
+ // We can only get here inside a template parameter,
+ // so this is syntactically correct if the given type is
+ // a typedef. The only disadvantage is that it is inconsistent
+ // with all other places where the 'typename' keyword should be
+ // used and we don't.
+ // With this, the above example will demangle as
+ // void f<5, A>(C<sizeof (typename T::t), sizeof (T::t)>::q)
+ if (current() == 'N' || // <nested-name>
+ // This should be a safe bet.
+ (current() == 'S' &&
+ next_peek() == 't')) // std::something, guess that
+ // this involves a typedef.
+ output += "typename ";
+ }
+ if (!decode_type(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ }
+ else
+ {
+ if (!decode_expression(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ }
+ output += ')';
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ else if (current() == 'r')
+ {
+ eat_current();
+ if (!decode_type(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ output += "::";
+ if (!decode_unqualified_name(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ if (current() != 'I' || decode_template_args(output))
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ }
+ else
+ {
+ char opcode0 = current();
+ char opcode1 = tolower(next());
+
+ register char hash;
+ if ((hash = offset_table_c[opcode0 - CHAR_MIN]))
+ {
+ hash += opcode1;
+ if (
+#if (CHAR_MIN < 0)
+ hash >= 0 &&
+#endif
+ hash < 39)
+ {
+ int index = static_cast<int>(static_cast<unsigned char>(hash));
+ entry_st entry = symbol_name_table_c[index];
+ if (entry.opcode[0] == opcode0 && entry.opcode[1] == opcode1
+ && (opcode1 == current() || entry.opcode[2] == '='))
+ {
+ char const* op = entry.symbol_name + 8; // Skip "operator".
+ if (*op == ' ') // operator new and delete.
+ ++op;
+ if (entry.type == unary)
+ output += op;
+ bool is_eq = (opcode1 != current());
+ eat_current();
+ if (index == 34 && M_inside_template_args) // operator>
+ output += '(';
+ output += '(';
+ if (!decode_expression(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ output += ')';
+ if (entry.type != unary)
+ {
+ if (!M_implementation_details.get_style_compact_expr_ops())
+ output += ' ';
+ output += op;
+ if (is_eq)
+ output += '=';
+ if (!M_implementation_details.get_style_compact_expr_ops())
+ output += ' ';
+ output += '(';
+ if (!decode_expression(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ output += ')';
+ if (index == 34 && M_inside_template_args)
+ output += ')';
+ if (entry.type == trinary)
+ {
+ if (M_implementation_details.get_style_compact_expr_ops())
+ output += ":(";
+ else
+ output += " : (";
+ if (!decode_expression(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ output += ')';
+ }
+ }
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ else if (opcode0 == 'c' &&
+ opcode1 == 'v') // casting operator.
+ {
+ eat_current();
+ output += '(';
+ if (!decode_type(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ output += ")(";
+ if (!decode_expression(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ output += ')';
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ }
+ }
+ }
+ _GLIBCXX_DEMANGLER_FAILURE;
+ }
+
+ //
+ // <template-args> ::= I <template-arg>+ E
+ // <template-arg> ::= <type> # type or template
+ // ::= L <type> <value number> E # integer literal
+ // ::= L <type> <value float> E # floating literal
+ // ::= L <mangled-name> E # external name
+ // ::= X <expression> E # expression
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::decode_template_args(string_type& output)
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_template_args");
+ if (eat_current() != 'I')
+ _GLIBCXX_DEMANGLER_FAILURE;
+ int prev_size = M_template_arg_pos.size();
+ ++M_inside_template_args;
+ if (M_template_args_need_space)
+ {
+ output += ' ';
+ M_template_args_need_space = false;
+ }
+ output += '<';
+ for(;;)
+ {
+ if (M_inside_template_args == 1 && !M_inside_type)
+ M_template_arg_pos.push_back(M_pos);
+ if (current() == 'X')
+ {
+ eat_current();
+ if (!decode_expression(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ if (current() != 'E')
+ _GLIBCXX_DEMANGLER_FAILURE;
+ eat_current();
+ }
+ else if (current() == 'L')
+ {
+ if (!decode_literal(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ if (current() != 'E')
+ _GLIBCXX_DEMANGLER_FAILURE;
+ eat_current();
+ }
+ else if (!decode_type(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ if (current() == 'E')
+ break;
+ output += ", ";
+ }
+ eat_current();
+ if (*(output.rbegin()) == '>')
+ output += ' ';
+ output += '>';
+ --M_inside_template_args;
+ if (!M_inside_template_args && !M_inside_type)
+ {
+ M_name_is_template = true;
+ M_template_arg_pos_offset = prev_size;
+ }
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+
+ // <bare-function-type> ::=
+ // <signature type>+ # Types are parameter types.
+ //
+ // Note that the possible return type of the <bare-function-type>
+ // has already been eaten before we call this function. This makes
+ // our <bare-function-type> slightly different from the one in
+ // the C++-ABI description.
+ //
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::decode_bare_function_type(string_type& output)
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_bare_function_type");
+ if (M_saw_destructor)
+ {
+ if (eat_current() != 'v' || (current() != 'E' && current() != 0))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ output += "()";
+ M_saw_destructor = false;
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ if (current() == 'v' && !M_implementation_details.get_style_void())
+ {
+ eat_current();
+ if (current() != 'E' && current() != 0)
+ _GLIBCXX_DEMANGLER_FAILURE;
+ output += "()";
+ M_saw_destructor = false;
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ output += '(';
+ M_template_args_need_space = false;
+ if (!decode_type(output)) // Must have at least one parameter.
+ _GLIBCXX_DEMANGLER_FAILURE;
+ while (current() != 'E' && current() != 0)
+ {
+ output += ", ";
+ if (!decode_type(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ }
+ output += ')';
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+
+ // <type> ::=
+ // <builtin-type> # Starts with a lower case character != r.
+ // <function-type> # Starts with F
+ // <class-enum-type> # Starts with N, S, C, D, Z, a digit or a lower
+ // # case character. Since a lower case character
+ // # would be an operator name, that would be an
+ // # error. The S is a substitution or St
+ // # (::std::). A 'C' would be a constructor and
+ // # thus also an error.
+ // <template-param> # Starts with T
+ // <substitution> # Starts with S
+ // <template-template-param> <template-args> # Starts with T or S,
+ // # equivalent with the above.
+ //
+ // <array-type> # Starts with A
+ // <pointer-to-member-type> # Starts with M
+ // <CV-qualifiers> <type> # Starts with r, V or K
+ // P <type> # pointer-to # Starts with P
+ // R <type> # reference-to # Starts with R
+ // C <type> # complex (C 2000) # Starts with C
+ // G <type> # imaginary (C 2000)# Starts with G
+ // U <source-name> <type> # vendor extended type qualifier,
+ // # starts with U
+ //
+ // <template-template-param> ::= <template-param>
+ // ::= <substitution>
+
+ // My own analysis of how to decode qualifiers:
+ //
+ // F is a <function-type>, <T> is a <builtin-type>, <class-enum-type>,
+ // <template-param> or <template-template-param> <template-args>.
+ // <Q> represents a series of qualifiers (not G or C).
+ // <C> is an unqualified type.
+ // <R> is a qualified type.
+ // <B> is the bare-function-type without return type.
+ // <I> is the array index.
+ // Substitutions:
+ // <Q>M<C><Q2>F<R><B>E ==> R (C::*Q)B Q2 "<C>", "F<R><B>E"
+ // (<R> and <B> recursive),
+ // "M<C><Q2>F<R><B>E".
+ // <Q>F<R><B>E ==> R (Q)B "<R>", "<B>" (<B> recursive)
+ // and "F<R><B>E".
+ //
+ // Note that if <R> has postfix qualifiers (an array or function), then
+ // those are added AFTER the (member) function type. For example:
+ // <Q>FPA<R><B>E ==> R (*(Q)B) [], where the PA added the prefix
+ // "(*" and the postfix ") []".
+ //
+ // <Q>G<T> ==> imaginary T Q "<T>", "G<T>" (<T> recursive).
+ // <Q>C<T> ==> complex T Q "<T>", "C<T>" (<T> recursive).
+ // <Q><T> ==> T Q "<T>" (<T> recursive).
+ //
+ // where <Q> is any of:
+ //
+ // <Q>P ==> *Q "P..."
+ // <Q>R ==> &Q "R..."
+ // <Q>[K|V|r]+ ==> [ const| volatile| restrict]+Q "KVr..."
+ // <Q>U<S> ==> SQ "U<S>..."
+ // <Q>M<C> ==> C::*Q "M<C>..." (<C> recurs.)
+ // A<I> ==> [I] "A<I>..." (<I> recurs.)
+ // <Q>A<I> ==> (Q) [I] "A<I>..." (<I> recurs.)
+ // Note that when <Q> ends on an A<I2> then the brackets are omitted
+ // and no space is written between the two:
+ // A<I2>A<I> ==> [I2][I]
+ // If <Q> ends on [KVr]+, which can happen in combination with
+ // substitutions only, then special handling is required, see below.
+ //
+ // A <substitution> is handled with an input position switch during which
+ // new substitutions are turned off. Because recursive handling of types
+ // (and therefore the order in which substitutions must be generated) must
+ // be done left to right, but the generation of Q needs processing right to
+ // left, substitutions per <type> are generated by reading the input left
+ // to right and marking the starts of all substitutions only - implicitly
+ // finishing them at the end of the type. Then the output and real
+ // substitutions are generated.
+ //
+ // The following comment was for the demangling of g++ version 3.0.x. The
+ // mangling (and I believe even the ABI description) have been fixed now
+ // (as of g++ version 3.1).
+ //
+ // g++ 3.0.x only:
+ // The ABI specifies for pointer-to-member function types the format
+ // <Q>M<T>F<R><B>E. In other words, the qualifier <Q2> (see above) is
+ // implicitely contained in <T> instead of explicitly part of the M format.
+ // I am convinced that this is a bug in the ABI. Unfortunately, this is
+ // how we have to demangle things as it has a direct impact on the order
+ // in which substitutions are stored. This ill-formed design results in
+ // rather ill-formed demangler code too however :/
+ //
+ // <Q2> is now explicitely part of the M format.
+ // For some weird reason, g++ (3.2.1) does not add substitutions for
+ // qualified member function pointers. I think that is another bug.
+ //
+
+ // In the case of
+ // <Q>A<I>
+ // where <Q> ends on [K|V|r]+ then that part should be processed as
+ // if it was behind the A<I> instead of in front of it. This is
+ // because a constant array of ints is normally always mangled as
+ // an array of constant ints. KVr qualifiers can end up in front
+ // of an array when the array is part of a substitution or template
+ // parameter, but the demangling should still result in the same
+ // syntax; thus KA2_i (const array of ints) must result in the same
+ // demangling as A2_Ki (array of const ints). As a result we must
+ // demangle ...[...[[KVr]+A<I0>][KVr]+A<I1>]...[KVr]+A<In>[KVr]+
+ // as A<I0>A<I1>...A<In>[KVr]+ where each K, V and r in the series
+ // collapses to a single character at the right of the string.
+ // For example:
+ // VA9_KrA6_KVi --> A9_A6_KVri --> int volatile const restrict [9][6]
+ // Note that substitutions are still added as usual (the translation
+ // to A9_A6_KVri does not really happen).
+ //
+ // This decoding is achieved by delaying the decoding of any sequence
+ // of [KVrA]'s and processing them together in the order: first the
+ // short-circuited KVr part and then the arrays.
+ static int const cvq_K = 1; // Saw at least one K
+ static int const cvq_V = 2; // Saw at least one V
+ static int const cvq_r = 4; // Saw at least one r
+ static int const cvq_A = 8; // Saw at least one A
+ static int const cvq_last = 16; // No remaining qualifiers.
+ static int const cvq_A_cnt = 32; // Bit 5 and higher represent the
+ // number of A's in the series.
+ // In the function below, iter_array points to the first (right most)
+ // A in the series, if any.
+ template<typename Tp, typename Allocator>
+ void
+ qualifier_list<Tp, Allocator>::decode_KVrA(
+ string_type& prefix, string_type& postfix, int cvq,
+ typename qual_vector::const_reverse_iterator const& iter_array) const
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING3("decode_KVrA");
+ if ((cvq & cvq_K))
+ prefix += " const";
+ if ((cvq & cvq_V))
+ prefix += " volatile";
+ if ((cvq & cvq_r))
+ prefix += " restrict";
+ if ((cvq & cvq_A))
+ {
+ int n = cvq >> 5;
+ for (typename qual_vector::
+ const_reverse_iterator iter = iter_array;
+ iter != M_qualifier_starts.rend(); ++iter)
+ {
+ switch((*iter).first_qualifier())
+ {
+ case 'K':
+ case 'V':
+ case 'r':
+ break;
+ case 'A':
+ {
+ string_type index = (*iter).get_optional_type();
+ if (--n == 0 && (cvq & cvq_last))
+ postfix = " [" + index + "]" + postfix;
+ else if (n > 0)
+ postfix = "[" + index + "]" + postfix;
+ else
+ {
+ prefix += " (";
+ postfix = ") [" + index + "]" + postfix;
+ }
+ break;
+ }
+ default:
+ _GLIBCXX_DEMANGLER_RETURN3;
+ }
+ }
+ }
+ _GLIBCXX_DEMANGLER_RETURN3;
+ }
+
+ template<typename Tp, typename Allocator>
+ void
+ qualifier_list<Tp, Allocator>::decode_qualifiers(
+ string_type& prefix,
+ string_type& postfix,
+ bool member_function_pointer_qualifiers = false) const
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING3("decode_qualifiers");
+ int cvq = 0;
+ typename qual_vector::const_reverse_iterator iter_array;
+ for(typename qual_vector::
+ const_reverse_iterator iter = M_qualifier_starts.rbegin();
+ iter != M_qualifier_starts.rend(); ++iter)
+ {
+ if (!member_function_pointer_qualifiers
+ && !(*iter).part_of_substitution())
+ {
+ int saved_inside_substitution = M_demangler.M_inside_substitution;
+ M_demangler.M_inside_substitution = 0;
+ M_demangler.add_substitution((*iter).get_start_pos(), type);
+ M_demangler.M_inside_substitution = saved_inside_substitution;
+ }
+ char qualifier_char = (*iter).first_qualifier();
+ for(; qualifier_char; qualifier_char = (*iter).next_qualifier())
+ {
+ switch(qualifier_char)
+ {
+ case 'P':
+ if (cvq)
+ {
+ decode_KVrA(prefix, postfix, cvq, iter_array);
+ cvq = 0;
+ }
+ prefix += "*";
+ break;
+ case 'R':
+ if (cvq)
+ {
+ decode_KVrA(prefix, postfix, cvq, iter_array);
+ cvq = 0;
+ }
+ prefix += "&";
+ break;
+ case 'K':
+ cvq |= cvq_K;
+ continue;
+ case 'V':
+ cvq |= cvq_V;
+ continue;
+ case 'r':
+ cvq |= cvq_r;
+ continue;
+ case 'A':
+ if (!(cvq & cvq_A))
+ {
+ cvq |= cvq_A;
+ iter_array = iter;
+ }
+ cvq += cvq_A_cnt;
+ break;
+ case 'M':
+ if (cvq)
+ {
+ decode_KVrA(prefix, postfix, cvq, iter_array);
+ cvq = 0;
+ }
+ prefix += " ";
+ prefix += (*iter).get_optional_type();
+ prefix += "::*";
+ break;
+ case 'U':
+ if (cvq)
+ {
+ decode_KVrA(prefix, postfix, cvq, iter_array);
+ cvq = 0;
+ }
+ prefix += " ";
+ prefix += (*iter).get_optional_type();
+ break;
+ case 'G': // Only here so we added a substitution.
+ break;
+ }
+ break;
+ }
+ }
+ if (cvq)
+ decode_KVrA(prefix, postfix, cvq|cvq_last, iter_array);
+ M_printing_suppressed = false;
+ _GLIBCXX_DEMANGLER_RETURN3;
+ }
+
+ //
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::decode_type_with_postfix(
+ string_type& prefix, string_type& postfix,
+ qualifier_list<Tp, Allocator>* qualifiers)
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING2("decode_type");
+ ++M_inside_type;
+ bool recursive_template_param_or_substitution_call;
+ if (!(recursive_template_param_or_substitution_call = qualifiers))
+ {
+ qualifier_list<Allocator>* raw_qualifiers = M_qualifier_list_alloc.allocate(1);
+ qualifiers = new (raw_qualifiers) qualifier_list<Allocator>(*this);
+ }
+ // First eat all qualifiers.
+ bool failure = false;
+ for(;;) // So we can use 'continue' to eat the next qualifier.
+ {
+ int start_pos = M_pos;
+ switch(current())
+ {
+ case 'P':
+ qualifiers->add_qualifier_start(pointer, start_pos,
+ M_inside_substitution);
+ eat_current();
+ continue;
+ case 'R':
+ qualifiers->add_qualifier_start(reference, start_pos,
+ M_inside_substitution);
+ eat_current();
+ continue;
+ case 'K':
+ case 'V':
+ case 'r':
+ {
+ char c;
+ int count = 0;
+ do
+ {
+ ++count;
+ c = next();
+ }
+ while(c == 'K' || c == 'V' || c == 'r');
+ qualifiers->add_qualifier_start(cv_qualifier, start_pos, count,
+ M_inside_substitution);
+ continue;
+ }
+ case 'U':
+ {
+ eat_current();
+ string_type source_name;
+ if (!decode_source_name(source_name))
+ {
+ failure = true;
+ break;
+ }
+ qualifiers->add_qualifier_start(vendor_extension, start_pos,
+ source_name, M_inside_substitution);
+ continue;
+ }
+ case 'A':
+ {
+ // <array-type> ::= A <positive dimension number> _ <element type>
+ // ::= A [<dimension expression>] _ <element type>
+ //
+ string_type index;
+ int saved_pos;
+ store(saved_pos);
+ if (next() == 'n' || !decode_number(index))
+ {
+ restore(saved_pos);
+ if (next() != '_' && !decode_expression(index))
+ {
+ failure = true;
+ break;
+ }
+ }
+ if (eat_current() != '_')
+ {
+ failure = true;
+ break;
+ }
+ qualifiers->add_qualifier_start(array, start_pos, index,
+ M_inside_substitution);
+ continue;
+ }
+ case 'M':
+ {
+ // <pointer-to-member-type> ::= M <class type> <member type>
+ // <Q>M<C> or <Q>M<C><Q2>F<R><B>E
+ eat_current();
+ string_type class_type;
+ if (!decode_type(class_type)) // Substitution: "<C>".
+ {
+ failure = true;
+ break;
+ }
+ char c = current();
+ if (c == 'F' || c == 'K' || c == 'V' || c == 'r')
+ // Must be CV-qualifiers and a member function pointer.
+ {
+ // <Q>M<C><Q2>F<R><B>E ==> R (C::*Q)B Q2
+ // substitutions: "<C>", "F<R><B>E" (<R> and <B>
+ // recursive), "M<C><Q2>F<R><B>E".
+ int count = 0;
+ int Q2_start_pos = M_pos;
+ while(c == 'K' || c == 'V' || c == 'r') // Decode <Q2>.
+ {
+ ++count;
+ c = next();
+ }
+ qualifier_list<Tp, Allocator> class_type_qualifiers(*this);
+ if (count)
+ class_type_qualifiers.
+ add_qualifier_start(cv_qualifier, Q2_start_pos,
+ count, M_inside_substitution);
+ string_type member_function_qualifiers;
+ // It is unclear why g++ doesn't add a substitution for
+ // "<Q2>F<R><B>E" as it should I think.
+ string_type member_function_qualifiers_postfix;
+ class_type_qualifiers.
+ decode_qualifiers(member_function_qualifiers,
+ member_function_qualifiers_postfix, true);
+ member_function_qualifiers +=
+ member_function_qualifiers_postfix;
+ // I don't think this substitution is actually ever used.
+ int function_pos = M_pos;
+ if (eat_current() != 'F')
+ {
+ failure = true;
+ break;
+ }
+ // Return type.
+ // Constructors, destructors and conversion operators don't
+ // have a return type, but seem to never get here.
+ string_type return_type_postfix;
+ if (!decode_type_with_postfix(prefix, return_type_postfix))
+ // substitution: <R> recursive
+ {
+ failure = true;
+ break;
+ }
+ prefix += " (";
+ prefix += class_type;
+ prefix += "::*";
+ string_type bare_function_type;
+ if (!decode_bare_function_type(bare_function_type)
+ || eat_current() != 'E') // Substitution: <B> recursive.
+ {
+ failure = true;
+ break;
+ }
+ // substitution: "F<R><B>E".
+ add_substitution(function_pos, type);
+ // substitution: "M<C><Q2>F<R><B>E".
+ add_substitution(start_pos, type);
+ // substitution: all qualified types if any.
+ qualifiers->decode_qualifiers(prefix, postfix);
+ postfix += ")";
+ postfix += bare_function_type;
+ postfix += member_function_qualifiers;
+ postfix += return_type_postfix;
+ goto decode_type_exit;
+ }
+ qualifiers->add_qualifier_start(pointer_to_member, start_pos,
+ class_type, M_inside_substitution);
+ continue;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ if (!failure)
+ {
+ // <Q>G<T> ==> imaginary T Q
+ // substitutions: "<T>", "G<T>" (<T> recursive).
+ // <Q>C<T> ==> complex T Q
+ // substitutions: "<T>", "C<T>" (<T> recursive).
+ if (current() == 'C' || current() == 'G')
+ {
+ prefix += current() == 'C' ? "complex " : "imaginary ";
+ qualifiers->add_qualifier_start(complex_or_imaginary, M_pos,
+ M_inside_substitution);
+ eat_current();
+ }
+ int start_pos = M_pos;
+ switch(current())
+ {
+ case 'F':
+ {
+ // <function-type> ::= F [Y] <bare-function-type> E
+ //
+ // Note that g++ never generates the 'Y', but we try to
+ // demangle it anyway.
+ bool extern_C = (next() == 'Y');
+ if (extern_C)
+ eat_current();
+
+ // <Q>F<R><B>E ==> R (Q)B
+ // substitution: "<R>", "<B>" (<B> recursive) and "F<R><B>E".
+
+ // Return type.
+ string_type return_type_postfix;
+ if (!decode_type_with_postfix(prefix, return_type_postfix))
+ // Substitution: "<R>".
+ {
+ failure = true;
+ break;
+ }
+ // Only array and function (pointer) types have a postfix.
+ // In that case we don't want the space but expect something
+ // like prefix is "int (*" and postfix is ") [1]".
+ // We do want the space if this pointer is qualified.
+ if (return_type_postfix.size() == 0 ||
+ (prefix.size() > 0 && *prefix.rbegin() != '*'))
+ prefix += ' ';
+ prefix += '(';
+ string_type bare_function_type;
+ if (!decode_bare_function_type(bare_function_type)
+ // substitution: "<B>" (<B> recursive).
+ || eat_current() != 'E')
+ {
+ failure = true;
+ break;
+ }
+ add_substitution(start_pos, type); // Substitution: "F<R><B>E".
+ qualifiers->decode_qualifiers(prefix, postfix);
+ // substitution: all qualified types, if any.
+ postfix += ")";
+ if (extern_C)
+ postfix += " [extern \"C\"] ";
+ postfix += bare_function_type;
+ postfix += return_type_postfix;
+ break;
+ }
+ case 'T':
+ if (!decode_template_param(prefix, qualifiers))
+ {
+ failure = true;
+ break;
+ }
+ if (current() == 'I')
+ {
+ add_substitution(start_pos, template_template_param);
+ // substitution: "<template-template-param>".
+ if (!decode_template_args(prefix))
+ {
+ failure = true;
+ break;
+ }
+ }
+ if (!recursive_template_param_or_substitution_call
+ && qualifiers->suppressed())
+ {
+ add_substitution(start_pos, type);
+ // substitution: "<template-param>" or
+ // "<template-template-param> <template-args>".
+ qualifiers->decode_qualifiers(prefix, postfix);
+ // substitution: all qualified types, if any.
+ }
+ break;
+ case 'S':
+ if (M_pos >= M_maxpos)
+ {
+ failure = true;
+ break;
+ }
+ if (M_str[M_pos + 1] != 't')
+ {
+ if (!decode_substitution(prefix, qualifiers))
+ {
+ failure = true;
+ break;
+ }
+ if (current() == 'I')
+ {
+ if (!decode_template_args(prefix))
+ {
+ failure = true;
+ break;
+ }
+ if (!recursive_template_param_or_substitution_call
+ && qualifiers->suppressed())
+ add_substitution(start_pos, type);
+ // Substitution:
+ // "<template-template-param> <template-args>".
+ }
+ if (!recursive_template_param_or_substitution_call
+ && qualifiers->suppressed())
+ qualifiers->decode_qualifiers(prefix, postfix);
+ // Substitution: all qualified types, if any.
+ break;
+ }
+ /* Fall-through for St */
+ case 'N':
+ case 'Z':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ // <Q><T> ==> T Q
+ // substitutions: "<T>" (<T> recursive).
+ if (!decode_class_enum_type(prefix))
+ {
+ failure = true;
+ break;
+ }
+ if (!recursive_template_param_or_substitution_call)
+ {
+ add_substitution(start_pos, type);
+ // substitution: "<class-enum-type>".
+ qualifiers->decode_qualifiers(prefix, postfix);
+ // substitution: all qualified types, if any.
+ }
+ else
+ qualifiers->printing_suppressed();
+ break;
+ default:
+ // <Q><T> ==> T Q
+ // substitutions: "<T>" (<T> recursive).
+ if (!decode_builtin_type(prefix))
+ {
+ failure = true;
+ break;
+ }
+ // If decode_type was called from decode_template_param then we
+ // need to suppress calling qualifiers here in order to get a
+ // substitution added anyway (for the <template-param>).
+ if (!recursive_template_param_or_substitution_call)
+ qualifiers->decode_qualifiers(prefix, postfix);
+ else
+ qualifiers->printing_suppressed();
+ break;
+ }
+ }
+ decode_type_exit:
+ --M_inside_type;
+ if (!recursive_template_param_or_substitution_call)
+ {
+ qualifiers->~qualifier_list<Allocator>();
+ M_qualifier_list_alloc.deallocate(qualifiers, 1);
+ }
+ if (failure)
+ _GLIBCXX_DEMANGLER_FAILURE;
+ _GLIBCXX_DEMANGLER_RETURN2;
+ }
+
+ // <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E
+ // ::= N [<CV-qualifiers>] <template-prefix> <template-args> E
+ //
+ // <prefix> ::= <prefix> <unqualified-name>
+ // ::= <template-prefix> <template-args>
+ // ::= <template-param>
+ // ::= # empty
+ // ::= <substitution>
+ //
+ // <template-prefix> ::= <prefix> <template unqualified-name>
+ // ::= <template-param>
+ // ::= <substitution>
+ //
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::decode_nested_name(string_type& output,
+ string_type& qualifiers)
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_nested_name");
+
+ if (current() != 'N' || M_pos >= M_maxpos)
+ _GLIBCXX_DEMANGLER_FAILURE;
+
+ // <CV-qualifiers> ::= [r] [V] [K] # restrict (C99), volatile, const
+ char const* qualifiers_start = &M_str[M_pos + 1];
+ for (char c = next(); c == 'K' || c == 'V' || c == 'r'; c = next());
+ for (char const* qualifier_ptr = &M_str[M_pos - 1];
+ qualifier_ptr >= qualifiers_start; --qualifier_ptr)
+ switch(*qualifier_ptr)
+ {
+ case 'K':
+ qualifiers += " const";
+ break;
+ case 'V':
+ qualifiers += " volatile";
+ break;
+ case 'r':
+ qualifiers += " restrict";
+ break;
+ }
+
+ int number_of_prefixes = 0;
+ int substitution_start = M_pos;
+ for(;;)
+ {
+ ++number_of_prefixes;
+ if (current() == 'S')
+ {
+ if (!decode_substitution(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ }
+ else if (current() == 'I')
+ {
+ if (!decode_template_args(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ if (current() != 'E')
+ {
+ // substitution: "<template-prefix> <template-args>".
+ add_substitution(substitution_start, nested_name_prefix,
+ number_of_prefixes);
+ }
+ }
+ else
+ {
+ if (current() == 'T')
+ {
+ if (!decode_template_param(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ }
+ else if (!decode_unqualified_name(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ if (current() != 'E')
+ {
+ // substitution: "<prefix> <unqualified-name>" or
+ // "<prefix> <template unqualified-name>".
+ add_substitution(substitution_start,
+ (current() == 'I') ? nested_name_template_prefix
+ : nested_name_prefix,
+ number_of_prefixes);
+ }
+ }
+ if (current() == 'E')
+ {
+ eat_current();
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ if (current() != 'I')
+ output += "::";
+ else if (M_template_args_need_space)
+ output += ' ';
+ M_template_args_need_space = false;
+ }
+ _GLIBCXX_DEMANGLER_FAILURE;
+ }
+
+ // <local-name> := Z <function encoding> E <entity name> [<discriminator>]
+ // := Z <function encoding> E s [<discriminator>]
+ // <discriminator> := _ <non-negative number>
+ //
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::decode_local_name(string_type& output)
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_local_name");
+ if (current() != 'Z' || M_pos >= M_maxpos)
+ _GLIBCXX_DEMANGLER_FAILURE;
+ if ((M_pos += decode_encoding(output, M_str + M_pos + 1,
+ M_maxpos - M_pos, M_implementation_details) + 1) < 0 ||
+ eat_current() != 'E')
+ _GLIBCXX_DEMANGLER_FAILURE;
+ output += "::";
+ if (current() == 's')
+ {
+ eat_current();
+ output += "string literal";
+ }
+ else
+ {
+ string_type nested_name_qualifiers;
+ if (!decode_name(output, nested_name_qualifiers))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ output += nested_name_qualifiers;
+ }
+ string_type discriminator;
+ if (current() == '_' && next() != 'n' && !decode_number(discriminator))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+
+ // <source-name> ::= <positive length number> <identifier>
+ //
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::decode_source_name(string_type& output)
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_source_name");
+ int length = current() - '0';
+ if (length < 1 || length > 9)
+ _GLIBCXX_DEMANGLER_FAILURE;
+ while(isdigit(next()))
+ length = 10 * length + current() - '0';
+ char const* ptr = &M_str[M_pos];
+ if (length > 11 && !strncmp(ptr, "_GLOBAL_", 8) && ptr[9] == 'N'
+ && ptr[8] == ptr[10])
+ {
+ output += "(anonymous namespace)";
+ if ((M_pos += length) > M_maxpos + 1)
+ _GLIBCXX_DEMANGLER_FAILURE;
+ }
+ else
+ while(length--)
+ {
+ if (current() == 0)
+ _GLIBCXX_DEMANGLER_FAILURE;
+ output += eat_current();
+ }
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+
+ // <unqualified-name> ::= <operator-name> # Starts with lower case.
+ // ::= <ctor-dtor-name> # Starts with 'C' or 'D'.
+ // ::= <source-name> # Starts with a digit.
+ //
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::decode_unqualified_name(string_type& output)
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_unqualified_name");
+ if (M_inside_template_args)
+ {
+ if (!decode_source_name(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ }
+ else if (isdigit(current()))
+ {
+ bool recursive_unqualified_name = (&M_function_name == &output);
+ // This can be a recursive call when we are decoding
+ // an <operator-name> that is a cast operator for a some
+ // <unqualified-name>; for example "operator Foo()".
+ // In that case this is thus not a ctor or dtor and we
+ // are not interested in updating M_function_name.
+ if (!recursive_unqualified_name)
+ M_function_name.clear();
+ M_name_is_template = false;
+ M_name_is_cdtor = false;
+ M_name_is_conversion_operator = false;
+ if (!decode_source_name(M_function_name))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ if (!recursive_unqualified_name)
+ output += M_function_name;
+ }
+ else if (islower(current()))
+ {
+ M_function_name.clear();
+ M_name_is_template = false;
+ M_name_is_cdtor = false;
+ M_name_is_conversion_operator = false;
+ if (!decode_operator_name(M_function_name))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ output += M_function_name;
+ }
+ else if (current() == 'C' || current() == 'D')
+ {
+ // <ctor-dtor-name> ::=
+ // C1 # complete object (in-charge) constructor
+ // C2 # base object (not-in-charge) constructor
+ // C3 # complete object (in-charge) allocating constructor
+ // D0 # deleting (in-charge) destructor
+ // D1 # complete object (in-charge) destructor
+ // D2 # base object (not-in-charge) destructor
+ //
+ if (current() == 'C')
+ {
+ char c = next();
+ if (c < '1' || c > '3')
+ _GLIBCXX_DEMANGLER_FAILURE;
+ }
+ else
+ {
+ char c = next();
+ if (c < '0' || c > '2')
+ _GLIBCXX_DEMANGLER_FAILURE;
+ output += '~';
+ M_saw_destructor = true;
+ }
+ M_name_is_cdtor = true;
+ eat_current();
+ output += M_function_name;
+ }
+ else
+ _GLIBCXX_DEMANGLER_FAILURE;
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+
+ // <unscoped-name> ::=
+ // <unqualified-name> # Starts not with an 'S'
+ // St <unqualified-name> # ::std::
+ //
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::decode_unscoped_name(string_type& output)
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_unscoped_name");
+ if (current() == 'S')
+ {
+ if (next() != 't')
+ _GLIBCXX_DEMANGLER_FAILURE;
+ eat_current();
+ output += "std::";
+ }
+ decode_unqualified_name(output);
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+
+ // <name> ::=
+ // <nested-name> # Starts with 'N'
+ // <unscoped-template-name> <template-args> # idem
+ // <local-name> # Starts with 'Z'
+ // <unscoped-name> # Starts with 'S', 'C', 'D',
+ // # a digit or a lower case
+ // # character.
+ //
+ // <unscoped-template-name> ::= <unscoped-name>
+ // ::= <substitution>
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::decode_name(string_type& output,
+ string_type& nested_name_qualifiers)
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_name");
+ int substitution_start = M_pos;
+ if (current() == 'S' && (M_pos >= M_maxpos || M_str[M_pos + 1] != 't'))
+ {
+ if (!decode_substitution(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ }
+ else if (current() == 'N')
+ {
+ decode_nested_name(output, nested_name_qualifiers);
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ else if (current() == 'Z')
+ {
+ decode_local_name(output);
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ else if (!decode_unscoped_name(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ if (current() == 'I')
+ {
+ // Must have been an <unscoped-template-name>.
+ add_substitution(substitution_start, unscoped_template_name);
+ if (!decode_template_args(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ }
+ M_template_args_need_space = false;
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+
+ // <call-offset> ::= h <nv-offset> _
+ // ::= v <v-offset> _
+ // <nv-offset> ::= <offset number>
+ // non-virtual base override
+ //
+ // <v-offset> ::= <offset number> _ <virtual offset number>
+ // virtual base override, with vcall offset
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::decode_call_offset(string_type&
+#if _GLIBCXX_DEMANGLER_CWDEBUG
+ output
+#endif
+ )
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_call_offset");
+ if (current() == 'h')
+ {
+ string_type dummy;
+ eat_current();
+ if (decode_number(dummy) && current() == '_')
+ {
+ eat_current();
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ }
+ else if (current() == 'v')
+ {
+ string_type dummy;
+ eat_current();
+ if (decode_number(dummy) && current() == '_')
+ {
+ eat_current();
+ if (decode_number(dummy) && current() == '_')
+ {
+ eat_current();
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ }
+ }
+ _GLIBCXX_DEMANGLER_FAILURE;
+ }
+
+ //
+ // <special-name> ::=
+ // TV <type> # virtual table
+ // TT <type> # VTT structure (construction
+ // vtable index).
+ // TI <type> # typeinfo structure
+ // TS <type> # typeinfo name (null-terminated
+ // byte string).
+ // GV <object name> # Guard variable for one-time
+ // initialization of static objects in
+ // a local scope.
+ // T <call-offset> <base encoding># base is the nominal target function
+ // of thunk.
+ // Tc <call-offset> <call-offset> <base encoding> # base is the nominal
+ // target function of thunk; first
+ // call-offset is 'this' adjustment;
+ // second call-offset is result
+ // adjustment
+ //
+ template<typename Tp, typename Allocator>
+ bool
+ session<Tp, Allocator>::decode_special_name(string_type& output)
+ {
+ _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_special_name");
+ if (current() == 'G')
+ {
+ if (next() != 'V')
+ _GLIBCXX_DEMANGLER_FAILURE;
+ output += "guard variable for ";
+ string_type nested_name_qualifiers;
+ eat_current();
+ if (!decode_name(output, nested_name_qualifiers))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ output += nested_name_qualifiers;
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ else if (current() != 'T')
+ _GLIBCXX_DEMANGLER_FAILURE;
+ switch(next())
+ {
+ case 'V':
+ output += "vtable for ";
+ eat_current();
+ decode_type(output);
+ _GLIBCXX_DEMANGLER_RETURN;
+ case 'T':
+ output += "VTT for ";
+ eat_current();
+ decode_type(output);
+ _GLIBCXX_DEMANGLER_RETURN;
+ case 'I':
+ output += "typeinfo for ";
+ eat_current();
+ decode_type(output);
+ _GLIBCXX_DEMANGLER_RETURN;
+ case 'S':
+ output += "typeinfo name for ";
+ eat_current();
+ decode_type(output);
+ _GLIBCXX_DEMANGLER_RETURN;
+ case 'c':
+ output += "covariant return thunk to ";
+ if (!decode_call_offset(output)
+ || !decode_call_offset(output)
+ || (M_pos += decode_encoding(output, M_str + M_pos,
+ M_maxpos - M_pos + 1, M_implementation_details)) < 0)
+ _GLIBCXX_DEMANGLER_FAILURE;
+ _GLIBCXX_DEMANGLER_RETURN;
+ case 'C': // GNU extension?
+ {
+ string_type first;
+ output += "construction vtable for ";
+ eat_current();
+ if (!decode_type(first))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ while(isdigit(current()))
+ eat_current();
+ if (eat_current() != '_')
+ _GLIBCXX_DEMANGLER_FAILURE;
+ if (!decode_type(output))
+ _GLIBCXX_DEMANGLER_FAILURE;
+ output += "-in-";
+ output += first;
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ default:
+ if (current() == 'v')
+ output += "virtual thunk to ";
+ else
+ output += "non-virtual thunk to ";
+ if (!decode_call_offset(output)
+ || (M_pos += decode_encoding(output, M_str + M_pos,
+ M_maxpos - M_pos + 1, M_implementation_details)) < 0)
+ _GLIBCXX_DEMANGLER_FAILURE;
+ _GLIBCXX_DEMANGLER_RETURN;
+ }
+ }
+
+ // <encoding> ::=
+ // <function name> <bare-function-type> # Starts with 'C', 'D', 'N',
+ // 'S', a digit or a lower case
+ // character.
+ // <data name> # Idem.
+ // <special-name> # Starts with 'T' or 'G'.
+ template<typename Tp, typename Allocator>
+ int
+ session<Tp, Allocator>::decode_encoding(string_type& output,
+ char const* in, int len, implementation_details const& id)
+ {
+#if _GLIBCXX_DEMANGLER_CWDEBUG
+ _GLIBCXX_DEMANGLER_DOUT(dc::demangler,
+ "Output thus far: \"" << output << '"');
+ string_type input(in, len > 0x40000000 ? strlen(in) : len);
+ _GLIBCXX_DEMANGLER_DOUT(
+ dc::demangler, "Entering decode_encoding(\"" << input << "\")");
+#endif
+ if (len <= 0)
+ return INT_MIN;
+ session<Tp, Allocator> demangler_session(in, len, id);
+ string_type nested_name_qualifiers;
+ int saved_pos;
+ demangler_session.store(saved_pos);
+ if (demangler_session.decode_special_name(output))
+ return demangler_session.M_pos;
+ demangler_session.restore(saved_pos);
+ string_type name;
+ if (!demangler_session.decode_name(name, nested_name_qualifiers))
+ return INT_MIN;
+ if (demangler_session.current() == 0
+ || demangler_session.current() == 'E')
+ {
+ output += name;
+ output += nested_name_qualifiers;
+ return demangler_session.M_pos;
+ }
+ // Must have been a <function name>.
+ string_type return_type_postfix;
+ if (demangler_session.M_name_is_template
+ && !(demangler_session.M_name_is_cdtor
+ || demangler_session.M_name_is_conversion_operator))
+ {
+ // Return type of function
+ if (!demangler_session.decode_type_with_postfix(output,
+ return_type_postfix))
+ return INT_MIN;
+ output += ' ';
+ }
+ output += name;
+ if (!demangler_session.decode_bare_function_type(output))
+ return INT_MIN;
+ output += nested_name_qualifiers;
+ output += return_type_postfix;
+ return demangler_session.M_pos;
+ }
+
+ } // namespace demangler
+
+ // Public interface
+ template<typename Tp, typename Allocator>
+ struct demangle
+ {
+ typedef typename Allocator::template rebind<char>::other char_Allocator;
+ typedef std::basic_string<char, std::char_traits<char>, char_Allocator>
+ string_type;
+ static string_type symbol(char const* in,
+ demangler::implementation_details const& id);
+ static string_type type(char const* in,
+ demangler::implementation_details const& id);
+ };
+
+ // demangle::symbol()
+ //
+ // Demangle `input' which should be a mangled function name as for
+ // instance returned by nm(1).
+ template<typename Tp, typename Allocator>
+ typename demangle<Tp, Allocator>::string_type
+ demangle<Tp, Allocator>::symbol(char const* input,
+ demangler::implementation_details const& id)
+ {
+ // <mangled-name> ::= _Z <encoding>
+ // <mangled-name> ::= _GLOBAL_ _<type>_ <disambiguation part>
+ // <type> can be I or D (GNU extension)
+ typedef demangler::session<Tp, Allocator> demangler_type;
+ string_type result;
+ bool failure = (input[0] != '_');
+
+ if (!failure)
+ {
+ if (input[1] == 'G')
+ {
+ if (!strncmp(input, "_GLOBAL__", 9)
+ && (input[9] == 'D' || input[9] == 'I')
+ && input[10] == '_')
+ {
+ if (input[9] == 'D')
+ result.assign("global destructors keyed to ", 28);
+ else
+ result.assign("global constructors keyed to ", 29);
+ // Output the disambiguation part as-is.
+ result += input + 11;
+ }
+ else
+ failure = true;
+ }
+ else if (input[1] == 'Z')
+ {
+ int cnt =
+ demangler_type::decode_encoding(result, input + 2, INT_MAX, id);
+ if (cnt < 0 || input[cnt + 2] != 0)
+ failure = true;
+ }
+ else
+ failure = true;
+ }
+
+ // Failure to demangle, return the mangled name.
+ if (failure)
+ result.assign(input, strlen(input));
+
+ return result;
+ }
+
+ // demangle::type()
+ // Demangle `input' which must be a zero terminated mangled type
+ // name as for instance returned by std::type_info::name().
+ template<typename Tp, typename Allocator>
+ typename demangle<Tp, Allocator>::string_type
+ demangle<Tp, Allocator>::type(char const* input,
+ demangler::implementation_details const& id)
+ {
+ std::basic_string<char, std::char_traits<char>, Allocator> result;
+ if (input == NULL)
+ result = "(null)";
+ else
+ {
+ demangler::session<Tp, Allocator> demangler_session(input, INT_MAX, id);
+ if (!demangler_session.decode_type(result)
+ || demangler_session.remaining_input_characters())
+ {
+ // Failure to demangle, return the mangled name.
+ result = input;
+ }
+ }
+ return result;
+ }
+} // namespace __gnu_cxx
+
+#endif // __DEMANGLE_H
diff --git a/libstdc++-v3/testsuite/22_locale/money_get/get/char/15.cc b/libstdc++-v3/testsuite/22_locale/money_get/get/char/15.cc
new file mode 100644
index 00000000000..a5e766eb45b
--- /dev/null
+++ b/libstdc++-v3/testsuite/22_locale/money_get/get/char/15.cc
@@ -0,0 +1,68 @@
+// 2004-03-01 Paolo Carlini <pcarlini@suse.de>
+
+// Copyright (C) 2004 Free Software Foundation
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING. If not, write to the Free
+// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+// USA.
+
+// 22.2.6.1.1 money_get members
+
+#include <locale>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+// The grammar doesn't allow thousands separator at the beginning of a
+// string, neither two consecutive thousands separators.
+void test01()
+{
+ using namespace std;
+ bool test __attribute__((unused)) = true;
+
+ typedef istreambuf_iterator<char> iterator_type;
+
+ // basic construction
+ locale loc_c = locale::classic();
+ locale loc_de = __gnu_test::try_named_locale("de_DE@euro");
+ VERIFY( loc_c != loc_de );
+
+ iterator_type end01, end02;
+ istringstream iss;
+ iss.imbue(loc_de);
+ // cache the money_get facet
+ const money_get<char>& mon_get = use_facet<money_get<char> >(iss.getloc());
+
+ iss.str(".100");
+ iterator_type is_it01(iss);
+ long double result1;
+ ios_base::iostate err01 = ios_base::goodbit;
+ end01 = mon_get.get(is_it01, end01, true, iss, err01, result1);
+ VERIFY( err01 == ios_base::failbit );
+ VERIFY( *end01 == '.' );
+
+ iss.str("30..0");
+ iterator_type is_it02(iss);
+ long double result2;
+ ios_base::iostate err02 = ios_base::goodbit;
+ end02 = mon_get.get(is_it02, end02, false, iss, err02, result2);
+ VERIFY( err02 == ios_base::failbit );
+ VERIFY( *end02 == '.' );
+}
+
+int main()
+{
+ test01();
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/22_locale/money_get/get/wchar_t/15.cc b/libstdc++-v3/testsuite/22_locale/money_get/get/wchar_t/15.cc
new file mode 100644
index 00000000000..52a4589d1cb
--- /dev/null
+++ b/libstdc++-v3/testsuite/22_locale/money_get/get/wchar_t/15.cc
@@ -0,0 +1,69 @@
+// 2004-03-01 Paolo Carlini <pcarlini@suse.de>
+
+// Copyright (C) 2004 Free Software Foundation
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING. If not, write to the Free
+// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+// USA.
+
+// 22.2.6.1.1 money_get members
+
+#include <locale>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+// The grammar doesn't allow thousands separator at the beginning of a
+// string, neither two consecutive thousands separators.
+void test01()
+{
+ using namespace std;
+ bool test __attribute__((unused)) = true;
+
+ typedef istreambuf_iterator<wchar_t> iterator_type;
+
+ // basic construction
+ locale loc_c = locale::classic();
+ locale loc_de = __gnu_test::try_named_locale("de_DE@euro");
+ VERIFY( loc_c != loc_de );
+
+ iterator_type end01, end02;
+ wistringstream iss;
+ iss.imbue(loc_de);
+ // cache the money_get facet
+ const money_get<wchar_t>& mon_get =
+ use_facet<money_get<wchar_t> >(iss.getloc());
+
+ iss.str(L".100");
+ iterator_type is_it01(iss);
+ long double result1;
+ ios_base::iostate err01 = ios_base::goodbit;
+ end01 = mon_get.get(is_it01, end01, true, iss, err01, result1);
+ VERIFY( err01 == ios_base::failbit );
+ VERIFY( *end01 == L'.' );
+
+ iss.str(L"30..0");
+ iterator_type is_it02(iss);
+ long double result2;
+ ios_base::iostate err02 = ios_base::goodbit;
+ end02 = mon_get.get(is_it02, end02, false, iss, err02, result2);
+ VERIFY( err02 == ios_base::failbit );
+ VERIFY( *end02 == L'.' );
+}
+
+int main()
+{
+ test01();
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/22_locale/num_get/get/char/14.cc b/libstdc++-v3/testsuite/22_locale/num_get/get/char/14.cc
new file mode 100644
index 00000000000..e2ce6daa5e8
--- /dev/null
+++ b/libstdc++-v3/testsuite/22_locale/num_get/get/char/14.cc
@@ -0,0 +1,59 @@
+// 2004-02-28 Paolo Carlini <pcarlini@suse.de>
+
+// Copyright (C) 2004 Free Software Foundation
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING. If not, write to the Free
+// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+// USA.
+
+// 22.2.2.1.1 num_get members
+
+#include <locale>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+struct Punct: std::numpunct<char>
+{
+ std::string do_grouping() const { return "\1"; }
+};
+
+void test01()
+{
+ using namespace std;
+ typedef istreambuf_iterator<char> iterator_type;
+
+ bool test __attribute__((unused)) = true;
+
+ istringstream iss;
+ iss.imbue(locale(iss.getloc(), static_cast<numpunct<char>*>(new Punct)));
+ const num_get<char>& ng = use_facet<num_get<char> >(iss.getloc());
+
+ ios_base::iostate err = ios_base::goodbit;
+ iterator_type end;
+ double d = 0.0;
+ double d1 = 1000.0;
+
+ iss.str("1,0e2");
+ err = ios_base::goodbit;
+ end = ng.get(iss.rdbuf(), 0, iss, err, d);
+ VERIFY( err == ios_base::eofbit );
+ VERIFY( d == d1 );
+}
+
+int main()
+{
+ test01();
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/22_locale/num_get/get/char/15.cc b/libstdc++-v3/testsuite/22_locale/num_get/get/char/15.cc
new file mode 100644
index 00000000000..c550181d489
--- /dev/null
+++ b/libstdc++-v3/testsuite/22_locale/num_get/get/char/15.cc
@@ -0,0 +1,75 @@
+// 2004-03-01 Paolo Carlini <pcarlini@suse.de>
+
+// Copyright (C) 2004 Free Software Foundation
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING. If not, write to the Free
+// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+// USA.
+
+// 22.2.2.1.1 num_get members
+
+#include <locale>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+struct Punct1: std::numpunct<char>
+{
+ std::string do_grouping() const { return "\1"; }
+ char do_thousands_sep() const { return '+'; }
+};
+
+struct Punct2: std::numpunct<char>
+{
+ char do_decimal_point() const { return '-'; }
+};
+
+// http://gcc.gnu.org/ml/libstdc++/2003-12/msg00201.html
+void test01()
+{
+ using namespace std;
+ typedef istreambuf_iterator<char> iterator_type;
+
+ bool test __attribute__((unused)) = true;
+
+ istringstream iss1, iss2;
+ iss1.imbue(locale(iss1.getloc(), static_cast<numpunct<char>*>(new Punct1)));
+ iss2.imbue(locale(iss2.getloc(), static_cast<numpunct<char>*>(new Punct2)));
+ const num_get<char>& ng1 = use_facet<num_get<char> >(iss1.getloc());
+ const num_get<char>& ng2 = use_facet<num_get<char> >(iss2.getloc());
+
+ ios_base::iostate err = ios_base::goodbit;
+ iterator_type end;
+ double d = 0.0;
+ double d1 = 1.0;
+ double d2 = 3.0;
+
+ iss1.str("1e+2");
+ err = ios_base::goodbit;
+ end = ng1.get(iss1.rdbuf(), 0, iss1, err, d);
+ VERIFY( err == ios_base::goodbit );
+ VERIFY( d == d1 );
+
+ iss2.str("3e-1");
+ err = ios_base::goodbit;
+ end = ng2.get(iss2.rdbuf(), 0, iss2, err, d);
+ VERIFY( err == ios_base::goodbit );
+ VERIFY( d == d2 );
+}
+
+int main()
+{
+ test01();
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/22_locale/num_get/get/wchar_t/14.cc b/libstdc++-v3/testsuite/22_locale/num_get/get/wchar_t/14.cc
new file mode 100644
index 00000000000..fd5e55e2a9f
--- /dev/null
+++ b/libstdc++-v3/testsuite/22_locale/num_get/get/wchar_t/14.cc
@@ -0,0 +1,59 @@
+// 2004-02-28 Paolo Carlini <pcarlini@suse.de>
+
+// Copyright (C) 2004 Free Software Foundation
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING. If not, write to the Free
+// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+// USA.
+
+// 22.2.2.1.1 num_get members
+
+#include <locale>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+struct Punct: std::numpunct<wchar_t>
+{
+ std::string do_grouping() const { return "\1"; }
+};
+
+void test01()
+{
+ using namespace std;
+ typedef istreambuf_iterator<wchar_t> iterator_type;
+
+ bool test __attribute__((unused)) = true;
+
+ wistringstream iss;
+ iss.imbue(locale(iss.getloc(), static_cast<numpunct<wchar_t>*>(new Punct)));
+ const num_get<wchar_t>& ng = use_facet<num_get<wchar_t> >(iss.getloc());
+
+ ios_base::iostate err = ios_base::goodbit;
+ iterator_type end;
+ double d = 0.0;
+ double d1 = 1000.0;
+
+ iss.str(L"1,0e2");
+ err = ios_base::goodbit;
+ end = ng.get(iss.rdbuf(), 0, iss, err, d);
+ VERIFY( err == ios_base::eofbit );
+ VERIFY( d == d1 );
+}
+
+int main()
+{
+ test01();
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/22_locale/num_get/get/wchar_t/15.cc b/libstdc++-v3/testsuite/22_locale/num_get/get/wchar_t/15.cc
new file mode 100644
index 00000000000..ef4d7b738d9
--- /dev/null
+++ b/libstdc++-v3/testsuite/22_locale/num_get/get/wchar_t/15.cc
@@ -0,0 +1,75 @@
+// 2004-03-01 Paolo Carlini <pcarlini@suse.de>
+
+// Copyright (C) 2004 Free Software Foundation
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING. If not, write to the Free
+// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+// USA.
+
+// 22.2.2.1.1 num_get members
+
+#include <locale>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+struct Punct1: std::numpunct<wchar_t>
+{
+ std::string do_grouping() const { return "\1"; }
+ wchar_t do_thousands_sep() const { return L'+'; }
+};
+
+struct Punct2: std::numpunct<wchar_t>
+{
+ wchar_t do_decimal_point() const { return L'-'; }
+};
+
+// http://gcc.gnu.org/ml/libstdc++/2003-12/msg00201.html
+void test01()
+{
+ using namespace std;
+ typedef istreambuf_iterator<wchar_t> iterator_type;
+
+ bool test __attribute__((unused)) = true;
+
+ wistringstream iss1, iss2;
+ iss1.imbue(locale(iss1.getloc(), static_cast<numpunct<wchar_t>*>(new Punct1)));
+ iss2.imbue(locale(iss2.getloc(), static_cast<numpunct<wchar_t>*>(new Punct2)));
+ const num_get<wchar_t>& ng1 = use_facet<num_get<wchar_t> >(iss1.getloc());
+ const num_get<wchar_t>& ng2 = use_facet<num_get<wchar_t> >(iss2.getloc());
+
+ ios_base::iostate err = ios_base::goodbit;
+ iterator_type end;
+ double d = 0.0;
+ double d1 = 1.0;
+ double d2 = 3.0;
+
+ iss1.str(L"1e+2");
+ err = ios_base::goodbit;
+ end = ng1.get(iss1.rdbuf(), 0, iss1, err, d);
+ VERIFY( err == ios_base::goodbit );
+ VERIFY( d == d1 );
+
+ iss2.str(L"3e-1");
+ err = ios_base::goodbit;
+ end = ng2.get(iss2.rdbuf(), 0, iss2, err, d);
+ VERIFY( err == ios_base::goodbit );
+ VERIFY( d == d2 );
+}
+
+int main()
+{
+ test01();
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/lib/dg-options.exp b/libstdc++-v3/testsuite/lib/dg-options.exp
new file mode 100644
index 00000000000..06001cb7514
--- /dev/null
+++ b/libstdc++-v3/testsuite/lib/dg-options.exp
@@ -0,0 +1,29 @@
+# Handlers for additional dg-xxx keywords in tests.
+
+# Copyright (C) 2004 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+# Copied from GCC.
+proc dg-require-iconv { args } {
+ if { ![ check_iconv_available ${args} ] } {
+ upvar dg-do-what dg-do-what
+ set dg-do-what [list [lindex ${dg-do-what} 0] "N" "P"]
+ return
+ }
+ return
+}
+