https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67714
Bug ID: 67714 Summary: [6 Regression] char is zero-extended instead of sign-extended Product: gcc Version: 6.0 Status: UNCONFIRMED Keywords: wrong-code Severity: normal Priority: P3 Component: middle-end Assignee: unassigned at gcc dot gnu.org Reporter: ktkachov at gcc dot gnu.org CC: kuganv at linaro dot org, rguenth at gcc dot gnu.org Target Milestone: --- Target: arm* The testcase for arm is: #include <stdio.h> unsigned int b; int c; signed char fn1() { signed char d; for (int i = 0; i < 1; i++) d = -15; return d; } int main() { for (c = 0; c < 1; c++) b = 0; char e = fn1(); signed char f = e ^ b; printf("checksum = %x\n", (int)f); } As of r226139 I'm seeing this printing: checksum = f1 whereas before it printed: checksum = fffffff1 This is at -O1. Curiously -O2 and other optimisations print the expected: checksum = fffffff1 There is a zero-extension sneaking in where it didn't before. If I look at the tree dumps before and after r226139 I see the difference starts at forwprop4 but it doesn't seem to be wrong to me. Before: signed charD.14 fD.5068; charD.4 eD.5067; signed charD.14 _8; intD.3 _14; ;; basic block 2, loop depth 0, count 0, freq 900, maybe hot ;; prev block 0, next block 1, flags: (NEW, REACHABLE) ;; pred: ENTRY [100.0%] (FALLTHRU,EXECUTABLE) # .MEM_23 = VDEF <.MEM_2(D)> bD.5053 = 0; # .MEM_25 = VDEF <.MEM_23> cD.5054 = 1; _8 = fn1D.5055 (); e_9 = (charD.4) _8; f_13 = _8; _14 = (intD.3) f_13; # .MEM_15 = VDEF <.MEM_25> # USE = nonlocal null # CLB = nonlocal null printfD.782 ("checksum = %x\n", _14); # VUSE <.MEM_15> return 0; and after: signed charD.14 fD.5068; charD.4 eD.5067; signed charD.14 _8; intD.3 _14; ;; basic block 2, loop depth 0, count 0, freq 900, maybe hot ;; prev block 0, next block 1, flags: (NEW, REACHABLE) ;; pred: ENTRY [100.0%] (FALLTHRU,EXECUTABLE) # .MEM_23 = VDEF <.MEM_2(D)> bD.5053 = 0; # .MEM_25 = VDEF <.MEM_23> cD.5054 = 1; _8 = fn1D.5055 (); e_9 = (charD.4) _8; f_13 = _8; _14 = (intD.3) _8; <<<==== This was _14 = (intD.3) f_13 before. # .MEM_15 = VDEF <.MEM_25> # USE = nonlocal null # CLB = nonlocal null printfD.782 ("checksum = %x\n", _14); # VUSE <.MEM_15> return 0; ;; succ: EXIT [100.0%] The zero-extension sneaks in during expand: ;; f_13 = _8; (insn 15 14 0 (set (reg/v:SI 110 [ f ]) (zero_extend:SI (subreg/u:QI (reg/v:SI 110 [ f ]) 0))) arm-zext.c:18 -1 (nil)) Looking at the expansion of that in gdb I see in expand_assignment that 'to' and 'from' seem to have correct signed types: (gdb) call debug_tree (to) <ssa_name 0x7ffff7238750 type <integer_type 0x7ffff7321348 signed char sizes-gimplified public string-flag QI size <integer_cst 0x7ffff732e000 constant 8> unit size <integer_cst 0x7ffff732e018 constant 1> align 8 symtab 0 alias set -1 canonical type 0x7ffff7321348 precision 8 min <integer_cst 0x7ffff731cfa8 -128> max <integer_cst 0x7ffff731cfd8 127> context <translation_unit_decl 0x7ffff7226d20 D.5069>> var <var_decl 0x7ffff7247090 f>def_stmt f_13 = _8; version 13> (gdb) call debug_tree (from) <ssa_name 0x7ffff72385e8 type <integer_type 0x7ffff7321348 signed char sizes-gimplified public string-flag QI size <integer_cst 0x7ffff732e000 constant 8> unit size <integer_cst 0x7ffff732e018 constant 1> align 8 symtab 0 alias set -1 canonical type 0x7ffff7321348 precision 8 min <integer_cst 0x7ffff731cfa8 -128> max <integer_cst 0x7ffff731cfd8 127> context <translation_unit_decl 0x7ffff7226d20 D.5069>> visiteddef_stmt _8 = fn1 (); version 8> The problem seems to occur one level below in store_expr_with_bounds where exp is changed to have an unsigned type and in particular this code that changes the type based on SUBREG_PROMOTED_SIGN: if (!SUBREG_CHECK_PROMOTED_SIGN (target, TYPE_UNSIGNED (TREE_TYPE (exp)))) { /* Some types, e.g. Fortran's logical*4, won't have a signed version, so use the mode instead. */ tree ntype = (signed_or_unsigned_type_for (SUBREG_PROMOTED_SIGN (target), TREE_TYPE (exp))); if (ntype == NULL) ntype = lang_hooks.types.type_for_mode (TYPE_MODE (TREE_TYPE (exp)), SUBREG_PROMOTED_SIGN (target)); exp = fold_convert_loc (loc, ntype, exp); } The type of exp as it enters store_expr_with_bounds is: <integer_type 0x7f7e44528348 signed char sizes-gimplified public string-flag QI size <integer_cst 0x7f7e44535000 type <integer_type 0x7f7e44528150 bitsizetype> constant 8> unit size <integer_cst 0x7f7e44535018 type <integer_type 0x7f7e445280a8 sizetype> constant 1> align 8 symtab 0 alias set -1 canonical type 0x7f7e44528348 precision 8 min <integer_cst 0x7f7e44523fa8 -128> max <integer_cst 0x7f7e44523fd8 127> context <translation_unit_decl 0x7f7e4442dd20 D.5069>> whereas after this snippet it becomes: <integer_type 0x7f7e4444f0a8 public unsigned QI size <integer_cst 0x7f7e44535000 type <integer_type 0x7f7e44528150 bitsizetype> constant 8> unit size <integer_cst 0x7f7e44535018 type <integer_type 0x7f7e445280a8 sizetype> constant 1> align 8 symtab 0 alias set -1 canonical type 0x7f7e4444f0a8 precision 8 min <integer_cst 0x7f7e4444d120 0> max <integer_cst 0x7f7e443deee8 255>> Kugan, I believe you introduced the SUBREG_PROMOTED_SIGN machinery. Do you think r226139 just exposes a pre-existing bug in that functionality?