(Configuration: x86_64, GCC 4.2.3 base line)

I've run into a problem where GCSE decides to kill a
conditional jump instruction because it thinks that the
result is always false.  This happens when GCSE decides
to propagate a constant that is "narrowed" [the original
mode of the constant is word_mode (DImode) and the use of
the constant is in a narrower mode (SImode)].

This situation arises inside the code generated by our
GCC/UPC compiler, and so far I haven't been able to
come up with a regular C test case that demonstrates
the failure.  For efficiency reasons, internal to the
compiler, we overlay a 16 byte struct on top of a TImode
value.  The 16 byte struct is the representation of UPC's
"pointer-to-shared", which is a potentially cross-node
pointer consisting of three parts (vaddr, thread, phase).
It looks like this:

    typedef struct {
       void *vaddr;
       unsigned int thread;
       unsigned int phase;
    }
    __attribute__ ((__aligned__(16)))
    upc_shared_ptr_t;

Although not allowed by GCC, you can think of it has having
an additional "__attribute__ ((__mode__(__TI__)))" specification.

Here is an excerpt from the offending RTL that when passed to
GCSE will lead to incorrect deletion of a conditional jump:

[...]

(insn 19 16 21 2 (set (reg:DI 81)
        (const_int 4294967296 [0x100000000])) 81 {*movdi_1_rex64} (nil)
    (nil))

(insn 21 19 24 2 (set (subreg:DI (reg:TI 70 [ D.2967 ]) 8)
        (reg:DI 81)) 81 {*movdi_1_rex64} (nil)
    (nil))

(insn 24 21 25 2 (set (reg:SI 60 [ p$phase ])
        (const_int 1 [0x1])) 40 {*movsi_1} (nil)
    (nil))

(insn 25 24 26 2 (set (reg:SI 61 [ p$thread ])
        (subreg:SI (reg:TI 70 [ D.2967 ]) 8)) 40 {*movsi_1} (nil)
    (expr_list:REG_EQUAL (const_int 4294967296 [0x100000000])
        (nil)))


[...]

;; Start of basic block 5, registers live: (nil)
(code_label 53 52 54 5 2 "" [2 uses])

(note 54 53 56 5 [bb 5] NOTE_INSN_BASIC_BLOCK)

(insn 56 54 57 5 (set (reg:CCZ 17 flags)
        (compare:CCZ (reg:SI 61 [ p$thread ])
            (const_int 0 [0x0]))) 3 {*cmpsi_ccno_1} (nil)
    (nil))

(jump_insn 57 56 59 5 (set (pc)
        (if_then_else (eq (reg:CCZ 17 flags)
                (const_int 0 [0x0]))
            (label_ref 63)
            (pc))) 531 {*jcc_1} (nil)
    (expr_list:REG_BR_PROB (const_int 7000 [0x1b58])
        (nil)))

[...]

The conditional jump instruction formed by instructions
56 and 57 above is deleted because GCSE thinks that
(reg:SI 61 [ p$thread ]) is non-zero.  It comes to this
conclusion when it propagates the
   REG_EQUAL (const_int 4294967296 [0x100000000])
value listed in instruction 25:

(insn 25 24 26 2 (set (reg:SI 61 [ p$thread ])
        (subreg:SI (reg:TI 70 [ D.2967 ]) 8)) 40 {*movsi_1} (nil)
    (expr_list:REG_EQUAL (const_int 4294967296 [0x100000000])
        (nil)))

Note that it takes 33 bits to express 0x100000000, and it won't
fit into an SImode container.  What CSE/GCSE should have done here is
written that REG_EQUAL note as follows:

(insn 25 24 26 2 (set (reg:SI 61 [ p$thread ])
        (subreg:SI (reg:TI 70 [ D.2967 ]) 8)) 40 {*movsi_1} (nil)
    (expr_list:REG_EQUAL (const_int 0)
        (nil)))

because only the lower 32 bits of the value are relevant.
In that case, the conditional jump can be rewritten into
an unconditional jump, but certainly not deleted.

The code that decides it is OK to use the wider constant,
without adjustment to the narrow mode is here:

  /* If we are looking for a CONST_INT, the mode doesn't really matter, as
     long as we are narrowing.  So if we looked in vain for a mode narrower
     than word_mode before, look for word_mode now.  */
  if (p == 0 && code == CONST_INT
      && GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (word_mode))
    {
      x = copy_rtx (x);
      PUT_MODE (x, word_mode);
      p = lookup (x, SAFE_HASH (x, VOIDmode), word_mode);
    }

The logic above is OK as far as it goes, but the subsequent
return of the unadjusted wider constant causes problems:

  for (p = p->first_same_value; p; p = p->next_same_value)
    if (GET_CODE (p->exp) == code
        /* Make sure this is a valid entry in the table.  */
        && exp_equiv_p (p->exp, p->exp, 1, false))
      return p->exp;

I'd think that somewhere in there gen_lowpart() needs to
be called.  I'd appreciate your review of the above analysis
and any suggestions that you might have on implementing a fix.

Reply via email to