diff options
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<T></code>. + </li> + <li>The interface of the <code>allocator<T></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>"n"</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>"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." [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<int> gets cut in half + and frees a bunch of its storage, that memory can be reused by the + private std::list<WonkyWidget> 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<T,A></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<T,A></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<T, __alloc></code> is thus the same as + <code>allocator<T></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<T></td> + <td><ext/new_allocator.h></td> + <td>std::__new_alloc</td> + <td><memory></td> + </tr> + <tr> + <td>__gnu_cxx::malloc_allocator<T></td> + <td><ext/malloc_allocator.h></td> + <td>std::__malloc_alloc_template<int></td> + <td><memory></td> + </tr> + <tr> + <td>__gnu_cxx::debug_allocator<T></td> + <td><ext/debug_allocator.h></td> + <td>std::debug_alloc<T></td> + <td><memory></td> + </tr> + <tr> + <td>__gnu_cxx::__pool_alloc<bool, int></td> + <td><ext/pool_allocator.h></td> + <td>std::__default_alloc_template<bool,int></td> + <td><memory></td> + </tr> + <tr> + <td>__gnu_cxx::__mt_alloc<T></td> + <td><ext/mt_allocator.h></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<true,0> normal; + typedef __pool_alloc<true,1> private; + typedef __pool_alloc<true,42> 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 <int, __gnu_cxx::malloc_allocator<int> > malloc_list;</pre> + Likewise, a debugging form of whichever allocator is currently in use: + <pre> + std::deque <int, __gnu_cxx::debug_allocator<std::allocator<int> > > 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 <stefan@xapa.se>" /> + <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<typename _Tp> size_t + __mt_alloc<_Tp>::_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->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->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->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->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->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* |<-+ (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 > _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 > _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 +} + |