2010-11-26 Tom de Vries gcc/ * gcc/ee.c: New file. * gcc/tree-pass.h (pass_ee): Declare. * gcc/opts.c (decode_options): Set flag_ee at -O2. * gcc/timevar.def (TV_EE): New timevar. * gcc/common.opt (fextension-elimination): New option. * gcc/Makefile.in (ee.o): New rule. * gcc/passes.c (pass_ee): Add it. * gcc/testsuite/gcc.dg/extend-4.c: New test. * gcc/testsuite/gcc.dg/extend-1.c: New test. * gcc/testsuite/gcc.dg/extend-2.c: New test. * gcc/testsuite/gcc.dg/extend-2-64.c: New test. * gcc/testsuite/gcc.dg/extend-3.c: New test. === modified file 'gcc/Makefile.in' --- old/gcc/Makefile.in 2010-11-16 18:05:53 +0000 +++ new/gcc/Makefile.in 2010-12-10 15:33:37 +0000 @@ -1194,6 +1194,7 @@ dse.o \ dwarf2asm.o \ dwarf2out.o \ + ee.o \ ebitmap.o \ emit-rtl.o \ et-forest.o \ @@ -2965,6 +2966,11 @@ web.o : web.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ hard-reg-set.h $(FLAGS_H) $(BASIC_BLOCK_H) $(FUNCTION_H) output.h $(TOPLEV_H) \ $(DF_H) $(OBSTACK_H) $(TIMEVAR_H) $(TREE_PASS_H) +ee.o : ee.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ + hard-reg-set.h $(FLAGS_H) $(BASIC_BLOCK_H) $(FUNCTION_H) output.h \ + $(DF_H) $(TIMEVAR_H) tree-pass.h $(RECOG_H) $(EXPR_H) \ + $(REGS_H) $(TREE_H) $(TM_P_H) insn-config.h $(INSN_ATTR_H) $(TOPLEV_H) $(DIAGNOSTIC_CORE_H) \ + $(TARGET_H) $(OPTABS_H) insn-codes.h rtlhooks-def.h $(PARAMS_H) $(CGRAPH_H) gcse.o : gcse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(REGS_H) hard-reg-set.h $(FLAGS_H) $(REAL_H) insn-config.h $(GGC_H) \ $(RECOG_H) $(EXPR_H) $(BASIC_BLOCK_H) $(FUNCTION_H) output.h $(TOPLEV_H) \ === modified file 'gcc/common.opt' --- old/gcc/common.opt 2010-11-04 12:43:52 +0000 +++ new/gcc/common.opt 2010-12-10 15:33:37 +0000 @@ -496,6 +496,10 @@ Common Report Var(flag_early_inlining) Init(1) Optimization Perform early inlining +fextension-elimination +Common Report Var(flag_ee) Init(0) Optimization +Perform extension elimination + feliminate-dwarf2-dups Common Report Var(flag_eliminate_dwarf2_dups) Perform DWARF2 duplicate elimination === added file 'gcc/ee.c' --- old/gcc/ee.c 1970-01-01 00:00:00 +0000 +++ new/gcc/ee.c 2010-12-10 15:33:37 +0000 @@ -0,0 +1,662 @@ +/* Redundant extension elimination + Copyright (C) 2010 Free Software Foundation, Inc. + Contributed by Tom de Vries (tom@codesourcery.com) + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, 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 COPYING3. If not see +. */ + +/* + + MOTIVATING EXAMPLE + + The motivating example for this pass is: + + void f(unsigned char *p, short s, int c, int *z) + { + if (c) + *z = 0; + *p ^= (unsigned char)s; + } + + For MIPS, compilation results in the following insns. + + (set (reg/v:SI 199) + (sign_extend:SI (subreg:HI (reg:SI 200) 2))) + + ... + + (set (reg:QI 203) + (subreg:QI (reg/v:SI 199) 3)) + + These insns are the only def and the only use of reg 199, each located in a + different bb. + + The sign-extension preserves the lower half of reg 200 and copies them to + reg 199, and the subreg use of reg 199 only reads the least significant byte. + The sign extension is therefore redundant (the extension part, not the copy + part), and can safely be replaced with a regcopy from reg 200 to reg 199. + + + OTHER SIGN/ZERO EXTENSION ELIMINATION PASSES + + There are other passes which eliminate sign/zero-extension: combine and + implicit_zee. Both attempt to eliminate extensions by combining them with + other instructions. The combine pass does this at bb level, + implicit_zee works at inter-bb level. + + The combine pass combine an extension with either: + - all uses of the extension, or + - all defs of the operand of the extension. + The implicit_zee pass only implements the latter. + + For our motivating example, combine doesn't work since the def and the use of + reg 199 are in a different bb. + + Implicit_zee does not work since it only combines an extension with the defs + of its operand. + + + INTENDED EFFECT + + This pass works by removing sign/zero-extensions, or replacing them with + regcopies. The idea there is that the regcopy might be eliminated by a later + pass. In case the regcopy cannot be eliminated, it might at least be cheaper + than the extension. + + + IMPLEMENTATION + + The pass scans twice over all instructions. + + The first scan registers all uses of a reg in the biggest_use array. After + that first scan, the biggest_use array contains the size in bits of the + biggest use of each reg. + + The second scan finds extensions, determines whether they are redundant based + on the biggest use, and deletes or replaces them. + + In case that the src and dest reg of the replacement are not of the same size, + we do not replace with a normal regcopy, but with a truncate or with the copy + of a paradoxical subreg instead. + + + LIMITATIONS + + The scope of the analysis is limited to an extension and its uses. The other + type of analysis (related to the defs of the operand of an extension) is not + done. + + Furthermore, we do the analysis of biggest use per reg. So when determining + whether an extension is redundant, we take all uses of a the dest reg into + account, also the ones that are not uses of the extension. This could be + overcome by calculating the def-use chains and using those for analysis + instead. + + Finally, during the analysis each insn is looked at in isolation. There is no + propagation of information during the analysis. To overcome this limitation, + a backward iterative bit-level liveness analysis is needed. */ + + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "rtl.h" +#include "tree.h" +#include "tm_p.h" +#include "flags.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "basic-block.h" +#include "insn-config.h" +#include "function.h" +#include "expr.h" +#include "insn-attr.h" +#include "recog.h" +#include "toplev.h" +#include "target.h" +#include "timevar.h" +#include "optabs.h" +#include "insn-codes.h" +#include "rtlhooks-def.h" +#include "output.h" +#include "params.h" +#include "timevar.h" +#include "tree-pass.h" +#include "cgraph.h" + +#define SKIP_REG (-1) + +/* Array to register the biggest use of a reg, in bits. */ + +static int *biggest_use; + +/* Forward declaration. */ + +static void note_use (rtx *x, void *data); + +/* The following two functions are borrowed from trunk/gcc/toplev.c. They can be + removed for a check-in into gcc trunk. */ + +/* Given X, an unsigned number, return the number of least significant bits + that are zero. When X == 0, the result is the word size. */ + +static int +ctz_hwi (unsigned HOST_WIDE_INT x) +{ + return x ? floor_log2 (x & -x) : HOST_BITS_PER_WIDE_INT; +} + +/* Similarly for most significant bits. */ + +static int +clz_hwi (unsigned HOST_WIDE_INT x) +{ + return HOST_BITS_PER_WIDE_INT - 1 - floor_log2(x); +} + +/* Check whether this is a paradoxical subreg. */ + +static bool +paradoxical_subreg_p (rtx subreg) +{ + enum machine_mode subreg_mode, reg_mode; + + if (GET_CODE (subreg) != SUBREG) + return false; + + subreg_mode = GET_MODE (subreg); + reg_mode = GET_MODE (SUBREG_REG (subreg)); + + if (GET_MODE_SIZE (subreg_mode) > GET_MODE_SIZE (reg_mode)) + return true; + + return false; +} + +/* Get the size and reg number of a REG or SUBREG use. */ + +static bool +reg_use_p (rtx use, int *size, unsigned int *regno) +{ + rtx reg; + + if (REG_P (use)) + { + *regno = REGNO (use); + *size = GET_MODE_BITSIZE (GET_MODE (use)); + return true; + } + else if (GET_CODE (use) == SUBREG) + { + reg = SUBREG_REG (use); + + if (!REG_P (reg)) + return false; + + *regno = REGNO (reg); + + if (paradoxical_subreg_p (use)) + *size = GET_MODE_BITSIZE (GET_MODE (reg)); + else + *size = subreg_lsb (use) + GET_MODE_BITSIZE (GET_MODE (use)); + + return true; + } + + return false; +} + +/* Register the use of a reg. */ + +static void +register_use (int size, unsigned int regno) +{ + int *current = &biggest_use[regno]; + + if (*current == SKIP_REG) + return; + + *current = MAX (*current, size); +} + +/* Handle embedded uses. */ + +static void +note_embedded_uses (rtx use, rtx pattern) +{ + const char *format_ptr; + int i, j; + + format_ptr = GET_RTX_FORMAT (GET_CODE (use)); + for (i = 0; i < GET_RTX_LENGTH (GET_CODE (use)); i++) + if (format_ptr[i] == 'e') + note_use (&XEXP (use, i), pattern); + else if (format_ptr[i] == 'E') + for (j = 0; j < XVECLEN (use, i); j++) + note_use (&XVECEXP (use, i, j), pattern); +} + +/* Get the set that has use as its SRC operand. */ + +static rtx +get_set (rtx use, rtx pattern) +{ + rtx sub; + int i; + + if (GET_CODE (pattern) == SET && SET_SRC (pattern) == use) + return pattern; + + if (GET_CODE (pattern) == PARALLEL) + for (i = 0; i < XVECLEN (pattern, 0); ++i) + { + sub = XVECEXP (pattern, 0, i); + if (GET_CODE (sub) == SET && SET_SRC (sub) == use) + return sub; + } + + return NULL_RTX; +} + +/* Handle a restricted op use. In this context restricted means that a bit in an + operand influences only the same bit or more significant bits in the result. + The bitwise ops are a subclass, but PLUS is one as well. */ + +static void +note_restricted_op_use (rtx use, unsigned int nr_operands, rtx pattern) +{ + unsigned int i, smallest; + int operand_size[2]; + int used_size; + unsigned int operand_regno[2]; + bool operand_reg[2]; + bool operand_ignore[2]; + rtx set; + + /* Init operand_reg, operand_size, operand_regno and operand_ignore. */ + for (i = 0; i < nr_operands; ++i) + { + operand_reg[i] = reg_use_p (XEXP (use, i), &operand_size[i], + &operand_regno[i]); + operand_ignore[i] = false; + } + + /* Handle case of reg and-masked with const. */ + if (GET_CODE (use) == AND && CONST_INT_P (XEXP (use, 1)) && operand_reg[0]) + { + used_size = + HOST_BITS_PER_WIDE_INT - clz_hwi (UINTVAL (XEXP (use, 1))); + operand_size[0] = MIN (operand_size[0], used_size); + } + + /* Handle case of reg or-masked with const. */ + if (GET_CODE (use) == IOR && CONST_INT_P (XEXP (use, 1)) && operand_reg[0]) + { + used_size = + HOST_BITS_PER_WIDE_INT - clz_hwi (~UINTVAL (XEXP (use, 1))); + operand_size[0] = MIN (operand_size[0], used_size); + } + + /* Ignore the use of a in 'a = a + b'. */ + set = get_set (use, pattern); + if (set != NULL_RTX && REG_P (SET_DEST (set))) + for (i = 0; i < nr_operands; ++i) + operand_ignore[i] = (operand_reg[i] + && (REGNO (SET_DEST (set)) == operand_regno[i])); + + /* Handle the case a reg is combined with don't care bits. */ + if (nr_operands == 2 && operand_reg[0] && operand_reg[1] + && operand_size[0] != operand_size[1]) + { + smallest = operand_size[0] > operand_size[1]; + + if (paradoxical_subreg_p (XEXP (use, smallest)) + && !SUBREG_PROMOTED_VAR_P (XEXP (use, smallest))) + operand_size[1 - smallest] = operand_size[smallest]; + } + + /* Register the operand use, if necessary. */ + for (i = 0; i < nr_operands; ++i) + if (!operand_reg[i]) + note_use (&XEXP (use, i), pattern); + else if (!operand_ignore[i]) + register_use (operand_size[i], operand_regno[i]); +} + +/* Handle all uses noted by note_uses. */ + +static void +note_use (rtx *x, void *data) +{ + rtx use = *x; + rtx pattern = (rtx)data; + int use_size; + unsigned int use_regno; + + switch (GET_CODE (use)) + { + case REG: + case SUBREG: + if (!reg_use_p (use, &use_size, &use_regno)) + { + note_embedded_uses (use, pattern); + return; + } + register_use (use_size, use_regno); + return; + case IOR: + case AND: + case XOR: + case PLUS: + case MINUS: + note_restricted_op_use (use, 2, pattern); + return; + case NOT: + case NEG: + note_restricted_op_use (use, 1, pattern); + return; + case ASHIFT: + if (!reg_use_p (XEXP (use, 0), &use_size, &use_regno) + || !CONST_INT_P (XEXP (use, 1)) + || INTVAL (XEXP (use, 1)) <= 0 + || paradoxical_subreg_p (XEXP (use, 0))) + { + note_embedded_uses (use, pattern); + return; + } + register_use (use_size - INTVAL (XEXP (use, 1)), use_regno); + return; + default: + note_embedded_uses (use, pattern); + return; + } +} + +/* Check whether reg is implicitly used. */ + +static bool +implicit_use_p (int regno) +{ +#ifdef EPILOGUE_USES + if (EPILOGUE_USES (regno)) + return true; +#endif + +#ifdef EH_USES + if (EH_USES (regno)) + return true; +#endif + + return false; +} + +/* Note the uses of argument registers in a call. */ + +static void +note_call_uses (rtx insn) +{ + rtx link, link_expr; + + if (!CALL_P (insn)) + return; + + for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1)) + { + link_expr = XEXP (link, 0); + + if (GET_CODE (link_expr) == USE) + note_use (&XEXP (link_expr, 0), link); + } +} + +/* Calculate the biggest use mode for all regs. */ + +static void +calculate_biggest_use (void) +{ + int i; + basic_block bb; + rtx insn; + + /* Initialize biggest_use for all regs to 0. If a reg is used implicitly, we + handle that reg conservatively and set it to SKIP_REG instead. */ + for (i = 0; i < max_reg_num (); i++) + biggest_use[i] = ((implicit_use_p (i) || HARD_REGISTER_NUM_P (i)) + ? SKIP_REG : 0); + + /* For all insns, call note_use for each use in insn. */ + FOR_EACH_BB (bb) + FOR_BB_INSNS (bb, insn) + { + if (!NONDEBUG_INSN_P (insn)) + continue; + + note_uses (&PATTERN (insn), note_use, PATTERN (insn)); + + if (CALL_P (insn)) + note_call_uses (insn); + } + + /* Dump the biggest uses found. */ + if (dump_file) + for (i = 0; i < max_reg_num (); i++) + if (biggest_use[i] > 0) + fprintf (dump_file, "reg %d: size %d\n", i, biggest_use[i]); +} + +/* Check whether this is a sign/zero extension. */ + +static bool +extension_p (rtx insn, rtx *dest, rtx *inner, int *preserved_size) +{ + rtx src, op0; + + /* Detect set of reg. */ + if (GET_CODE (PATTERN (insn)) != SET) + return false; + + src = SET_SRC (PATTERN (insn)); + *dest = SET_DEST (PATTERN (insn)); + + if (!REG_P (*dest)) + return false; + + /* Detect sign or zero extension. */ + if (GET_CODE (src) == ZERO_EXTEND || GET_CODE (src) == SIGN_EXTEND + || (GET_CODE (src) == AND && CONST_INT_P (XEXP (src, 1)))) + { + op0 = XEXP (src, 0); + + /* Determine amount of least significant bits preserved by operation. */ + if (GET_CODE (src) == AND) + *preserved_size = ctz_hwi (~UINTVAL (XEXP (src, 1))); + else + *preserved_size = GET_MODE_BITSIZE (GET_MODE (op0)); + + if (GET_CODE (op0) == SUBREG) + { + if (subreg_lsb (op0) != 0) + return false; + + *inner = SUBREG_REG (op0); + return true; + } + else if (REG_P (op0)) + { + *inner = op0; + return true; + } + } + + return false; +} + +/* Check whether this is a redundant sign/zero extension. */ + +static bool +redundant_extension_p (rtx insn, rtx *dest, rtx *inner) +{ + int biggest_dest_use; + int preserved_size; + + if (!extension_p (insn, dest, inner, &preserved_size)) + return false; + + if (dump_file) + fprintf (dump_file, "considering extension %u with preserved size %d\n", + INSN_UID (insn), preserved_size); + + biggest_dest_use = biggest_use[REGNO (*dest)]; + + if (biggest_dest_use == SKIP_REG) + return false; + + if (preserved_size < biggest_dest_use) + return false; + + if (dump_file) + fprintf (dump_file, "found superfluous extension %u\n", INSN_UID (insn)); + + return true; +} + +/* Try to remove or replace the redundant extension. */ + +static void +try_remove_or_replace_extension (rtx insn, rtx dest, rtx inner) +{ + rtx cp_src, cp_dest, seq, one; + + if (GET_MODE_CLASS (GET_MODE (dest)) != GET_MODE_CLASS (GET_MODE (inner))) + return; + + /* Check whether replacement is needed. */ + if (dest != inner) + { + start_sequence (); + + /* Determine the proper replacement operation. */ + if (GET_MODE (dest) == GET_MODE (inner)) + { + cp_src = inner; + cp_dest = dest; + } + else if (GET_MODE_SIZE (GET_MODE (dest)) + > GET_MODE_SIZE (GET_MODE (inner))) + { + emit_clobber (dest); + cp_src = inner; + cp_dest = gen_lowpart_SUBREG (GET_MODE (inner), dest); + } + else + { + cp_src = gen_rtx_TRUNCATE (GET_MODE (dest), inner); + cp_dest = dest; + } + + emit_move_insn (cp_dest, cp_src); + + seq = get_insns (); + end_sequence (); + + /* If the replacement is not supported, bail out. */ + for (one = seq; one != NULL_RTX; one = NEXT_INSN (one)) + if (recog_memoized (one) < 0 && GET_CODE (PATTERN (one)) != CLOBBER) + return; + + /* Insert the replacement. */ + emit_insn_before (seq, insn); + } + + /* Note replacement/removal in the dump. */ + if (dump_file) + { + fprintf (dump_file, "superfluous extension %u ", INSN_UID (insn)); + if (dest != inner) + fprintf (dump_file, "replaced by %u\n", INSN_UID (seq)); + else + fprintf (dump_file, "removed\n"); + } + + /* Remove the extension. */ + delete_insn (insn); +} + +/* Find redundant extensions and remove or replace them if possible. */ + +static void +remove_redundant_extensions (void) +{ + basic_block bb; + rtx insn, next, dest, inner; + + biggest_use = XNEWVEC (int, max_reg_num ()); + calculate_biggest_use (); + + /* Remove redundant extensions. */ + FOR_EACH_BB (bb) + FOR_BB_INSNS_SAFE (bb, insn, next) + { + if (!NONDEBUG_INSN_P (insn)) + continue; + + if (!redundant_extension_p (insn, &dest, &inner)) + continue; + + try_remove_or_replace_extension (insn, dest, inner); + } + + free (biggest_use); +} + +/* Remove redundant extensions. */ + +static unsigned int +rest_of_handle_ee (void) +{ + remove_redundant_extensions (); + return 0; +} + +/* Run ee pass when flag_ee is set at optimization level > 0. */ + +static bool +gate_handle_ee (void) +{ + return (optimize > 0 && flag_ee); +} + +struct rtl_opt_pass pass_ee = +{ + { + RTL_PASS, + "ee", /* name */ + gate_handle_ee, /* gate */ + rest_of_handle_ee, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_EE, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_ggc_collect | + TODO_dump_func | + TODO_verify_rtl_sharing, /* todo_flags_finish */ + } +}; === modified file 'gcc/opts.c' --- old/gcc/opts.c 2010-05-17 09:13:28 +0000 +++ new/gcc/opts.c 2010-12-10 15:33:37 +0000 @@ -907,6 +907,7 @@ flag_tree_switch_conversion = opt2; flag_ipa_cp = opt2; flag_ipa_sra = opt2; + flag_ee = opt2; /* Track fields in field-sensitive alias analysis. */ set_param_value ("max-fields-for-field-sensitive", === modified file 'gcc/passes.c' --- old/gcc/passes.c 2010-09-01 13:29:58 +0000 +++ new/gcc/passes.c 2010-12-10 15:33:37 +0000 @@ -974,6 +974,7 @@ NEXT_PASS (pass_lower_subreg); NEXT_PASS (pass_df_initialize_opt); NEXT_PASS (pass_cse); + NEXT_PASS (pass_ee); NEXT_PASS (pass_rtl_fwprop); NEXT_PASS (pass_rtl_cprop); NEXT_PASS (pass_rtl_pre); === added file 'gcc/testsuite/gcc.dg/extend-1.c' --- old/gcc/testsuite/gcc.dg/extend-1.c 1970-01-01 00:00:00 +0000 +++ new/gcc/testsuite/gcc.dg/extend-1.c 2010-12-10 15:33:37 +0000 @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-rtl-ee" } */ + +void f(unsigned char * p, short s, int c, int *z) +{ + if (c) + *z = 0; + *p ^= (unsigned char)s; +} + +/* { dg-final { scan-rtl-dump-times "sign_extend:" 0 "ee" { target mips*-*-* } } } */ +/* { dg-final { scan-rtl-dump-times "superfluous extension \[0-9\]+ replaced" 1 "ee" { target mips*-*-* } } } */ +/* { dg-final { cleanup-rtl-dump "ee" } } */ === added file 'gcc/testsuite/gcc.dg/extend-2-64.c' --- old/gcc/testsuite/gcc.dg/extend-2-64.c 1970-01-01 00:00:00 +0000 +++ new/gcc/testsuite/gcc.dg/extend-2-64.c 2010-12-10 15:33:37 +0000 @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-rtl-ee" } */ +/* { dg-require-effective-target mips64 } */ + +void f(unsigned char * p, short *s, int c) +{ + short or = 0; + while (c) + { + or = or | s[c]; + c --; + } + *p = (unsigned char)or; +} + +/* { dg-final { scan-rtl-dump-times "zero_extend:" 1 "ee" { target mips*-*-* } } } */ +/* { dg-final { scan-rtl-dump-times "sign_extend:" 0 "ee" { target mips*-*-* } } } */ +/* { dg-final { scan-rtl-dump-times "superfluous extension \[0-9\]+ replaced" 3 "ee" { target mips*-*-* } } } */ +/* { dg-final { cleanup-rtl-dump "ee" } } */ + === added file 'gcc/testsuite/gcc.dg/extend-2.c' --- old/gcc/testsuite/gcc.dg/extend-2.c 1970-01-01 00:00:00 +0000 +++ new/gcc/testsuite/gcc.dg/extend-2.c 2010-12-10 15:33:37 +0000 @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-rtl-ee" } */ +/* { dg-require-effective-target ilp32 } */ + +void f(unsigned char * p, short *s, int c) +{ + short or = 0; + while (c) + { + or = or | s[c]; + c --; + } + *p = (unsigned char)or; +} + +/* { dg-final { scan-rtl-dump-times "zero_extend" 0 "ee" { target mips*-*-* } } } */ +/* { dg-final { scan-rtl-dump-times "sign_extend" 0 "ee" { target mips*-*-* } } } */ +/* { dg-final { scan-rtl-dump-times "superfluous extension \[0-9\]+ replaced" 2 "ee" { target mips*-*-* } } } */ +/* { dg-final { cleanup-rtl-dump "ee" } } */ + === added file 'gcc/testsuite/gcc.dg/extend-3.c' --- old/gcc/testsuite/gcc.dg/extend-3.c 1970-01-01 00:00:00 +0000 +++ new/gcc/testsuite/gcc.dg/extend-3.c 2010-12-10 15:33:37 +0000 @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-rtl-ee" } */ + +unsigned int f(unsigned char byte) +{ + return byte << 25; +} + +/* { dg-final { scan-rtl-dump-times "zero_extend:" 0 "ee" { target mips*-*-* } } } */ +/* { dg-final { scan-rtl-dump "superfluous extension \[0-9\]+ replaced" "ee" { target mips*-*-* } } } */ +/* { dg-final { cleanup-rtl-dump "ee" } } */ + === added file 'gcc/testsuite/gcc.dg/extend-4.c' --- old/gcc/testsuite/gcc.dg/extend-4.c 1970-01-01 00:00:00 +0000 +++ new/gcc/testsuite/gcc.dg/extend-4.c 2010-12-10 15:33:37 +0000 @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-rtl-ee" } */ + +unsigned char f(unsigned int a) +{ + unsigned int b = a & 0x10ff; + return b; +} + +/* { dg-final { scan-rtl-dump-times "and:" 0 "ee" { target mips*-*-* } } } */ +/* { dg-final { scan-rtl-dump-times "superfluous extension \[0-9\]+ replaced" 1 "ee" { target mips*-*-* } } } */ +/* { dg-final { cleanup-rtl-dump "ee" } } */ + === modified file 'gcc/timevar.def' --- old/gcc/timevar.def 2009-11-27 12:43:08 +0000 +++ new/gcc/timevar.def 2010-12-10 15:33:37 +0000 @@ -162,6 +162,7 @@ DEFTIMEVAR (TV_VARCONST , "varconst") DEFTIMEVAR (TV_LOWER_SUBREG , "lower subreg") DEFTIMEVAR (TV_JUMP , "jump") +DEFTIMEVAR (TV_EE , "extension elimination") DEFTIMEVAR (TV_FWPROP , "forward prop") DEFTIMEVAR (TV_CSE , "CSE") DEFTIMEVAR (TV_DCE , "dead code elimination") === modified file 'gcc/tree-pass.h' --- old/gcc/tree-pass.h 2010-09-01 13:29:58 +0000 +++ new/gcc/tree-pass.h 2010-12-10 15:33:37 +0000 @@ -468,6 +468,7 @@ extern struct rtl_opt_pass pass_initial_value_sets; extern struct rtl_opt_pass pass_unshare_all_rtl; extern struct rtl_opt_pass pass_instantiate_virtual_regs; +extern struct rtl_opt_pass pass_ee; extern struct rtl_opt_pass pass_rtl_fwprop; extern struct rtl_opt_pass pass_rtl_fwprop_addr; extern struct rtl_opt_pass pass_jump2;