Working with GCC 4.0.1, we're implementing an experimental dialect of C,
called UPC, which offers language extensions for parallel computing in
a distributed shared memory setting (see: http://intrepid.com/upc).

Generally, the work has proceeded well, and the language-specific callout
in gimplify_expr() have been sufficient to implement UPC features by
rewriting language extensions into C-like tree structures that can be
further gimplified.  However, we've run into a glitch, and I'm not
quite certain where the fix should go, or how the fix should be implemented.

UPC has a shared pointer that can address data in another process (called
a thread in UPC terminology).  A shared pointer has the following fields:

struct shared_ptr_struct
  {
    unsigned long int phase  : 48;
    unsigned int thread : 16;
    void *offset;
  };
typedef struct shared_ptr_struct shared_ptr_t;

Two shared pointers are equal if  all fields are equal:

int
cmp_ptr_eq (shared_ptr_t p1, shared_ptr_t p2)
{
  return p1.offset == p2.offset
         && p1.phase == p2.phase
         && p1.thread == p2.thread;
}

The UPC-specific gimplify routine which implements shared pointer
comparisons rewrites an expression like (p1 == p2) into the
sort of code shown above.

Here's the actual UPC-specific gimplify code:

        *expr_p = build_binary_op (TRUTH_ANDIF_EXPR, off_cmp,
                        build_binary_op (TRUTH_ANDIF_EXPR,
                                         phase_cmp, offset_cmp, 0), 0);

where off_cmp, thread_cmp and phase_cmp are expressions which evaluate
the equality comparison for the offset, thread, and phase fields.
For example,

  off0 = build3 (COMPONENT_REF, o_t, op0,
                 upc_vaddr_field_node, NULL_TREE);
  off1 = build3 (COMPONENT_REF, o_t, op1,
                 upc_vaddr_field_node, NULL_TREE);
  off_cmp = build_binary_op (code, off0, off1, 0);

All this works pretty well, but ICE's on the following small UPC test
program:

shared int *p;

int main(int argc, char **argv)
{
    int errors = 0;
    if (p == NULL) {
        /* no action */
    } else {
        errors = 1;
    }
}

% upc t.upc
t.upc: In function 'main':
t.upc:9: internal compiler error: in invert_truthvalue, at fold-const.c:3026
Please submit a full bug report,
with preprocessed source if appropriate.
See <URL:http://www.intrepid.com/upc/bugs.html> for instructions.

It fails here:

#1  0x00000000005c2c39 in invert_truthvalue (arg=0x2aaaae0bac30)
    at /upc/gcc-upc-4/src/gcc/fold-const.c:3026
3026      gcc_assert (TREE_CODE (TREE_TYPE (arg)) == BOOLEAN_TYPE);

The type of the arg is integer_type, not boolean:

(gdb) p arg->common.type
$1 = 0x2aaaadecca90
(gdb) pt
 <integer_type 0x2aaaadecca90 int sizes-gimplified public SI
    size <integer_cst 0x2aaaadec6a80 type <integer_type 0x2aaaadecc5b0
bit_size_type> constant invariant 32>
    unit size <integer_cst 0x2aaaadec65a0 type <integer_type 0x2aaaadecc4e0
long unsigned int> constant invariant 4>
    align 32 symtab 0 alias set -1 precision 32 min <integer_cst
0x2aaaadec69f0 -2147483648> max <integer_cst 0x2aaaadec6a20 2147483647>
    pointer_to_this <pointer_type 0x2aaaadee44e0>>

It is an integer type because the initial build_binary_op(TRUTH_ANDIF_EXPR
...
uses the type of the result of the comparisons, which is integer_type.

The TRUTH_ANDIF expr is gimplified in gimplify_boolean_expr:

3079    gimplify_boolean_expr (tree *expr_p)
3080    {
3081      /* Preserve the original type of the expression.  */
3082      tree type = TREE_TYPE (*expr_p);
3083
3084      *expr_p = build (COND_EXPR, type, *expr_p,
3085                       convert (type, boolean_true_node),
3086                       convert (type, boolean_false_node));
3087
3088      return GS_OK;
3089    }

basically a boolean expression b is converted into a true or false value
by rewriting it as:
   (b) ? true : false
However, as the comment states "Preserve the original type of the
expression.",
the original type of the expression, 'b', is kept.  In this case, the type
is integer_type not boolean type.

Thus the original
  (EQ_EXPR p1 p2)

is rewritten into
  (COND_EXPR integer_type
             (TRUTH_ANDIF_EXPR (EQ_EXPR p1.offset p2.offset)
                 TRUTH_ANDIF_EXPR (EQ_EXPR p1.phase p2.phase) (EQ_EXPR
p1.thread p2.thread)))
             (boolean_type true)
             (boolean_type false))

If we call the condition expression above, 'cond', then the test program has
the following structure:
  (COND_EXPR (cond)
             (void)
             (MODIFY_EXPR (VAR_DECL errors) (constant 1)))

Invert_truthvalue wants to rewrite the construct above into:
  (COND_EXPR (TRUTH_NOT_EXPR (cond))
             (MODIFY_EXPR (VAR_DECL errors) (constant 1))
             (void))

This runs into trouble when invert_truthvalue attempts to
negate the condition 'cond', insisting that cond be a boolean
expression. Under normal conditions this isn't a problem, because
the normal flow of control of parsing if statemtns and then
gimplifying them would have forced 'cond' to be of boolen type.

The problem arises when UPC rewrites the EQ_EXPR into a TRUTH_ANDIF
expr.  The condition expression missed a chance to be converted to a
boolean type in gimplify_boolean_expr(), because that function
preserves the incoming (integer) type, and it misses an opportunity
again when the 'cond' expression is sent to gimplify_cond_expr().

If the control flow in gimplify_cond_expr() had made it to here:

2193      /* Make sure the condition has BOOLEAN_TYPE.  */
2194      TREE_OPERAND (expr, 0) = gimple_boolify (TREE_OPERAND (expr, 0));

Then, the condition, 'cond' and its sub-parts, would've been converted
to a boolean type.  But, in the case described above, the logic exits early,
just above the call to the call to gimple_boolify() above.  The
actual sequence of events within gimplify_cond_expr() for the case
being discussed is:

Breakpoint 5, gimplify_cond_expr (expr_p=0x2aaaae0a1a38,
    pre_p=0x7fffffac1550, post_p=0x7fffffac13f8, target=0x0,
    fallback=fb_rvalue) at /upc/gcc-upc-4/src/gcc/gimplify.c:2123
2123      tree expr = *expr_p;
(gdb) n
2127      type = TREE_TYPE (expr);

where type is an integer type:

(gdb) pt
 <integer_type 0x2aaaadecca90 int sizes-gimplified public SI
    size <integer_cst 0x2aaaadec6a80 type <integer_type 0x2aaaadecc5b0
bit_size_type> constant invariant 32>
    unit size <integer_cst 0x2aaaadec65a0 type <integer_type 0x2aaaadecc4e0
long unsigned int> constant invariant 4>
    align 32 symtab 0 alias set -1 precision 32 min <integer_cst
0x2aaaadec69f0 -2147483648> max <integer_cst 0x2aaaadec6a20 2147483647>
    pointer_to_this <pointer_type 0x2aaaadee44e0>>

the next step creates a gimple temp variable of the same
type as the expression (integer type):

2133      else if (! VOID_TYPE_P (type))
(gdb)
2137          if (target)
(gdb)
2146          else if ((fallback & fb_lvalue) == 0)
(gdb)
2148              result = tmp2 = tmp = create_tmp_var (TREE_TYPE (expr),
"iftmp");

A little more work is done, and then gimplify_cond_expr() returns,
before getting to the call to gimple_boolify().

2186          /* Move the COND_EXPR to the prequeue.  */
2187          gimplify_and_add (expr, pre_p);
2188
2189          *expr_p = result;
2190          return ret;

Here's what expr looks like just before returning:

 <cond_expr 0x2aaaae0bc050
    type <integer_type 0x2aaaadecca90 int sizes-gimplified public SI
        size <integer_cst 0x2aaaadec6a80 constant invariant 32>
        unit size <integer_cst 0x2aaaadec65a0 constant invariant 4>
        align 32 symtab 0 alias set -1 precision 32 min <integer_cst
0x2aaaadec69f0 -2147483648> max <integer_cst 0x2aaaadec6a20 2147483647>
        pointer_to_this <pointer_type 0x2aaaadee44e0>>
    side-effects
    arg 0 <truth_andif_expr 0x2aaaae0bc000 type <integer_type 0x2aaaadecca90
int>
        side-effects
        arg 0 <eq_expr 0x2aaaae0a1be0 type <integer_type 0x2aaaadecca90 int>
            side-effects
            arg 0 <component_ref 0x2aaaae0a1b40 type <pointer_type
0x2aaaadee59c0>
                side-effects
                arg 0 <save_expr 0x2aaaae0ac240 type <record_type
0x2aaaadf87b60 upc_shared_ptr_t>
                    side-effects invariant
                    arg 0 <view_convert_expr 0x2aaaae0ac200 type
<record_type 0x2aaaadf87b60 upc_shared_ptr_t>
                        arg 0 <var_decl 0x2aaaae0ba410 p>>> arg 1
<field_decl 0x2aaaadf87ea0 vaddr>>
            arg 1 <component_ref 0x2aaaae0a1b90 type <pointer_type
0x2aaaadee59c0>
                side-effects
                arg 0 <save_expr 0x2aaaae0ac280 type <record_type
0x2aaaadf87b60 upc_shared_ptr_t>
                    side-effects invariant
                    arg 0 <constructor 0x2aaaadf8b000 type <record_type
0x2aaaadf87b60 upc_shared_ptr_t>
                        constant static
                        arg 0 <tree_list 0x2aaaadf884e0 purpose <field_decl
0x2aaaadf87d00 phase> value <integer_cst 0x2aaaadedf720 0> chain <tree_list
0x2aaaadf884b0>>>> arg 1 <field_decl 0x2aaaadf87ea0 vaddr>>>
        arg 1 <truth_andif_expr 0x2aaaae0a1fa0 type <integer_type
0x2aaaadecca90 int>
            side-effects
            arg 0 <eq_expr 0x2aaaae0a1f50 type <integer_type 0x2aaaadecca90
int>
                side-effects
                arg 0 <bit_and_expr 0x2aaaae0a1f00 type <integer_type
0x2aaaadeccd00 long unsigned int>
                    side-effects
                    arg 0 <bit_field_ref 0x2aaaae0a1eb0 type <integer_type
0x2aaaadeccd00 long unsigned int>
                        side-effects unsigned arg 0 <save_expr
0x2aaaae0ac240>
                        arg 1 <integer_cst 0x2aaaae0b7ab0 constant invariant
64>
                        arg 2 <integer_cst 0x2aaaadedf600 constant invariant
0>>
                    arg 1 <integer_cst 0x2aaaae0b7a50 constant invariant
281474976710655>>
                arg 1 <bit_and_expr 0x2aaaae0a1e60 type <integer_type
0x2aaaadeccd00 long unsigned int>
                    side-effects
                    arg 0 <bit_field_ref 0x2aaaae0a1e10 type <integer_type
0x2aaaadeccd00 long unsigned int>
                        side-effects unsigned arg 0 <save_expr
0x2aaaae0ac280> arg 1 <integer_cst 0x2aaaae0b7ab0 64> arg 2 <integer_cst
0x2aaaadedf600 0>> arg 1 <integer_cst 0x2aaaae0b7a50 281474976710655>>>
            arg 1 <eq_expr 0x2aaaae0a1cd0 type <integer_type 0x2aaaadecca90
int>
                side-effects
                arg 0 <component_ref 0x2aaaae0a1c30 type <integer_type
0x2aaaadecc9c0 short unsigned int>
                    side-effects arg 0 <save_expr 0x2aaaae0ac240> arg 1
<field_decl 0x2aaaadf87dd0 thread>>
                arg 1 <component_ref 0x2aaaae0a1c80 type <integer_type
0x2aaaadecc9c0 short unsigned int>
                    side-effects arg 0 <save_expr 0x2aaaae0ac280> arg 1
<field_decl 0x2aaaadf87dd0 thread>>>>>
    arg 1 <modify_expr 0x2aaaae0bc0a0
        type <void_type 0x2aaaadee1c30 void VOID
            align 8 symtab 0 alias set -1
            pointer_to_this <pointer_type 0x2aaaadee1d00>>
        side-effects
        arg 0 <var_decl 0x2aaaae0bac30 iftmp.0 type <integer_type
0x2aaaadecca90 int>
            used ignored SI file t.upc line 9 size <integer_cst
0x2aaaadec6a80 32> unit size <integer_cst 0x2aaaadec65a0 4>
            align 32 context <function_decl 0x2aaaae0b89c0 main>>
        arg 1 <integer_cst 0x2aaaadedf750 constant invariant 1>>
    arg 2 <modify_expr 0x2aaaae0bc0f0 type <void_type 0x2aaaadee1c30 void>
        side-effects arg 0 <var_decl 0x2aaaae0bac30 iftmp.0>
        arg 1 <integer_cst 0x2aaaadedf720 constant invariant 0>>>


Notice how the type of arg0, the TRUTH_ANDIF_EXPR, is integer type, and
not boolean.  This will cause invert_truthvalue() to abort a bit later on.

Possible fixes are:

1. Move the call to gimplify_boolean_expr() up to just before line 2137:

2135          tree result;
2136
2137          if (target)
2138            {
2139              ret = gimplify_expr (&target, pre_p, post_p,
2140                                   is_gimple_min_lval, fb_lvalue);
2141              if (ret != GS_ERROR)
2142                ret = GS_OK;
2143              result = tmp = target;
2144              tmp2 = unshare_expr (target);
2145            }
2146          else if ((fallback & fb_lvalue) == 0)

2. place the call to gimplify_boolean_expr() just before the
call to invert_truthvalue(), in gimplify_cond_expr() - shown
ifdef'ed out below:

2234      else if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 2)))
2235        /* Rewrite "if (a); else b" to "if (!a) b"  */
2236        {
2237    #if 0
2238          /* Make sure the condition has BOOLEAN_TYPE.  */
2239          TREE_OPERAND (expr, 0) = gimple_boolify (TREE_OPERAND (expr,
0));
2240    #endif
2241          TREE_OPERAND (expr, 0) = invert_truthvalue (TREE_OPERAND
(expr, 0));
2242          ret = gimplify_expr (&TREE_OPERAND (expr, 0), pre_p, NULL,
2243                               is_gimple_condexpr, fb_rvalue);

3. Or call gimplify_boolean_expr() in the original language-specific
expansion of pointer comparison.  This strikes me as not a good
solution because it may change the type semantics of the expression
in an undersirable way.

I've tested alternative #2 above, and it fixes the problem.  I tried
replicating the problem in regular C code, but cannot.

I'd appreciate hearing from those who understand the gimplify logic,
and can tell me if the language-specific hook is doing something wrong,
or if a fix such #1 or #2 above makes sense?

Reply via email to