On Mon, Apr 18, 2011 at 3:37 PM, Richard Sandiford
<richard.sandif...@linaro.org> wrote:
> Richard Guenther <richard.guent...@gmail.com> writes:
>>> +/* Expand a call to internal function FN.  ARGS is an array of the
>>> +   function's arguments and LHS is where the result should be stored.  */
>>> +
>>> +void
>>> +expand_internal_call (enum internal_fn fn, tree lhs, tree *args)
>>> +{
>>> +  internal_fn_expanders[(int) fn] (lhs, args);
>>
>> Can you use an interface that simply takes a gimple statement?
>> We want to eventually transistion to expanding directly from them
>> without re-creating a GENERIC call-expr for 4.7.
>
> OK.
>
>>>  /* Return the function type of the function called by GS.  */
>>>
>>>  static inline tree
>>>  gimple_call_fntype (const_gimple gs)
>>>  {
>>>   GIMPLE_CHECK (gs, GIMPLE_CALL);
>>> -  return gs->gimple_call.fntype;
>>> +  gcc_gimple_checking_assert (!gimple_call_internal_p (gs));
>>
>> I'm not sure this is the best fallback but it's certainly safe ;)
>> I think we should return NULL for internal fns, that is sure to trap
>> also for non-checking-enabled builds.
>
> trap == segfault?  We'd do that either way on most machines.
> But yeah, if you think we should be able to call this for internal
> functions, I'll change it.

Yes, we should be able to call it, but deal with NULL returns.

>>> +  return gs->gimple_call.u.fntype;
>>>  }
>>>
>>>  /* Set the type of the function called by GS to FNTYPE.  */
>>> @@ -2019,7 +2049,8 @@ gimple_call_fntype (const_gimple gs)
>>>  gimple_call_set_fntype (gimple gs, tree fntype)
>>>  {
>>>   GIMPLE_CHECK (gs, GIMPLE_CALL);
>>> -  gs->gimple_call.fntype = fntype;
>>> +  gcc_gimple_checking_assert (!gimple_call_internal_p (gs));
>>> +  gs->gimple_call.u.fntype = fntype;
>>>  }
>>>
>>>
>>> @@ -2029,8 +2060,12 @@ gimple_call_set_fntype (gimple gs, tree
>>>  static inline tree
>>>  gimple_call_fn (const_gimple gs)
>>>  {
>>> +  tree op;
>>> +
>>>   GIMPLE_CHECK (gs, GIMPLE_CALL);
>>> -  return gimple_op (gs, 1);
>>> +  op = gimple_op (gs, 1);
>>> +  gcc_gimple_checking_assert (op != NULL_TREE);
>>
>> Hmmm... probably good for your time of development but please remove
>> this.  Rather add a hunk to verify_gimple_call that checks that internal
>> fns have a NULL fn.
>
> Well, TBH, I had it there because I thought it was a better interface.
> I don't think it ever triggered during testing.  But again, if you think
> it should be OK to call this for internal functions, I'll change it.

Yep, see above

>>> +/* Set internal function FN to be the function called by call statement 
>>> GS.  */
>>> +
>>> +static inline void
>>> +gimple_call_set_internal_fn (gimple gs, enum internal_fn fn)
>>> +{
>>> +  GIMPLE_CHECK (gs, GIMPLE_CALL);
>>> +  gs->gsbase.subcode |= GF_CALL_INTERNAL;
>>> +  gimple_set_op (gs, 1, NULL_TREE);
>>
>> can we instead assert this?  We shouldn't ever change a calls state
>> from non-internal to internal (or vice-versa).
>
> OK.
>
>>> +  gs->gimple_call.u.internal_fn = fn;
>>> +}
>>> +
>>> +
>>>  /* Set FN to be the function called by call statement GS.  */
>>>
>>>  static inline void
>>>  gimple_call_set_fn (gimple gs, tree fn)
>>>  {
>>>   GIMPLE_CHECK (gs, GIMPLE_CALL);
>>> +  gs->gsbase.subcode &= ~GF_CALL_INTERNAL;
>>
>> Likewise.  Or just let the stmt verifier catch it.
>
> Which is better?  I gather from the above that it's better not to
> assert in these accessors.

Both are setters, not accessors.  I think for setters we want asserts
to be able to spot errors where they creep in.  They are also not called
that often.

>>> +/* Return true if calls C1 and C2 are known to go to the same function.  */
>>> +
>>> +bool
>>> +gimple_call_same_target_p (const_gimple c1, const_gimple c2)
>>> +{
>>> +  if (gimple_call_internal_p (c1))
>>> +    return (gimple_call_internal_p (c2)
>>> +           && gimple_call_internal_fn (c1) == gimple_call_internal_fn 
>>> (c2));
>>> +  else
>>> +    return (!gimple_call_internal_p (c2)
>>> +           && operand_equal_p (gimple_call_fn (c1), gimple_call_fn (c2), 
>>> 0));
>>
>> After a patch I am about to commit in a sec. the following for comparing
>> gimple_call_fn would be better:
>>
>>   gimple_call_fn (c1) == gimple_call_fn (c2)
>>   || (gimple_call_fndecl (c1)
>>      && gimple_call_fndecl (c1) == gimple_call_fndecl (c2))
>>
>> because we have several forms of specifying a function-decl.
>
> OK.
>
>>> -  new_stmt = gimple_build_call_vec (fn, vargs);
>>> +  if (gimple_call_internal_p (stmt))
>>> +    new_stmt = gimple_build_call_internal_vec (gimple_call_internal_fn 
>>> (stmt),
>>> +                                              vargs);
>>> +  else
>>> +    new_stmt = gimple_build_call_vec (gimple_call_fn (stmt), vargs);
>>
>> This code looks like it could be simplified by using gimple_copy, a
>> loop to set arguments and gimple_set_num_ops.  That of course looks
>> unrelated to your change, but it makes apparent that sth is wrong with
>> this function ;)
>
> Wouldn't that allocate unnecessary ops (as many as the original statement
> had, rather than the number that have been kept)?  But like you say,
> it seems unrelated to this patch, so I'd like to leave it be if that's OK.

True, it would allocate some waste, but it would make the code
clearer and 100% safe as to what needs to be copied and how.
Leaving it be is ok.

>>> Index: gcc/expr.c
>>> ===================================================================
>>> --- gcc/expr.c  2011-04-18 10:18:49.000000000 +0100
>>> +++ gcc/expr.c  2011-04-18 10:18:54.000000000 +0100
>>> @@ -8521,10 +8521,15 @@ expand_expr_real_1 (tree exp, rtx target
>>>          enum machine_mode pmode;
>>>
>>>          /* Get the signedness to be used for this variable.  Ensure we get
>>> -            the same mode we got when the variable was declared.  */
>>> +            the same mode we got when the variable was declared.
>>> +
>>> +            Note that calls to internal functions do not result in a
>>> +            call instruction, so promote_function_mode is not meaningful
>>> +            in that case.  */
>>>          if (code == SSA_NAME
>>>              && (g = SSA_NAME_DEF_STMT (ssa_name))
>>> -             && gimple_code (g) == GIMPLE_CALL)
>>> +             && gimple_code (g) == GIMPLE_CALL
>>> +             && !gimple_call_internal_p (g))
>>
>> I'm not sure we'll never need to do promotions for internal functions
>> but at least for now this looks ok, and we can think of how to deal
>> with this when it's necessary.  Of course it looks fishy that you
>> even arrive here ...?
>
> Yeah, I don't think we did arrive here.  I was trying to fix all uses of
> gimple_call_fn and gimple_call_fntype by inspection.  I can assert instead
> if you think it really shouldn't ever happen.

An assert would indeed be better (I suppose promote_function_mode
happily does some random stuff with a NULL function type?)

>>> Index: gcc/gimple-low.c
>>> ===================================================================
>>> --- gcc/gimple-low.c    2011-04-18 10:18:49.000000000 +0100
>>> +++ gcc/gimple-low.c    2011-04-18 10:18:54.000000000 +0100
>>> @@ -224,6 +224,8 @@ gimple_check_call_args (gimple stmt, tre
>>>   /* Get argument types for verification.  */
>>>   if (fndecl)
>>>     parms = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
>>> +  else if (gimple_call_internal_p (stmt))
>>> +    parms = NULL_TREE;
>>
>> Instead do a
>>
>>   /* Calls to internal functions always match their signature.  */
>>   if (gimple_call_internal_p (stmt))
>>     return true;
>>
>> early.
>
> OK.
>
>>>  verify_gimple_call (gimple stmt)
>>>  {
>>> -  tree fn = gimple_call_fn (stmt);
>>> +  tree fn;
>>>   tree fntype, fndecl;
>>>   unsigned i;
>>>
>>> -  if (!is_gimple_call_addr (fn))
>>> +  if (!gimple_call_internal_p (stmt))
>>
>> Please do not re-indent most of the function but instead do an upfront
>> conditionl verification for internal calls:
>>
>>   if (gimple_call_internal_p (stmt))
>>     {
>>       if (gimple_op (stmt, 1) != NULL_TREE)
>>         {
>>           error ("non-NULL function in gimple internal call");
>>           return true;
>>         }
>>       return false;
>>     }
>
> But what about stuff like:
>
>  if (gimple_call_lhs (stmt)
>      && (!is_gimple_lvalue (gimple_call_lhs (stmt))
>          || verify_types_in_gimple_reference (gimple_call_lhs (stmt), true)))
>    {
>      error ("invalid LHS in gimple call");
>      return true;
>    }
>
> I left quite a few bits of the function out of the reindentation because
> they seemed to apply (or be neutral) for internal functions.

Ah, indeed.  Maybe just re-order bits so the early out for internal
functions works?

>>
>>>     {
>>> -      error ("invalid function in gimple call");
>>> -      debug_generic_stmt (fn);
>>> -      return true;
>>> -    }
>>> -
>>> -  if (!POINTER_TYPE_P (TREE_TYPE  (fn))
>>> -      || (TREE_CODE (TREE_TYPE (TREE_TYPE (fn))) != FUNCTION_TYPE
>>> -         && TREE_CODE (TREE_TYPE (TREE_TYPE (fn))) != METHOD_TYPE))
>>> -    {
>>> -      error ("non-function in gimple call");
>>> -      return true;
>>> +      fn = gimple_call_fn (stmt);
>>> +      if (!is_gimple_call_addr (fn))
>>> +       {
>>> +         error ("invalid function in gimple call");
>>> +         debug_generic_stmt (fn);
>>> +         return true;
>>> +       }
>>> +      if (!POINTER_TYPE_P (TREE_TYPE  (fn))
>>> +         || (TREE_CODE (TREE_TYPE (TREE_TYPE (fn))) != FUNCTION_TYPE
>>> +             && TREE_CODE (TREE_TYPE (TREE_TYPE (fn))) != METHOD_TYPE))
>>> +       {
>>> +         error ("non-function in gimple call");
>>> +         return true;
>>> +       }
>>> +      fntype = gimple_call_fntype (stmt);
>>> +      if (gimple_call_lhs (stmt)
>>> +         && !useless_type_conversion_p (TREE_TYPE (gimple_call_lhs (stmt)),
>>> +                                        TREE_TYPE (fntype))
>>> +         /* ???  At least C++ misses conversions at assignments from
>>> +            void * call results.
>>> +            ???  Java is completely off.  Especially with functions
>>> +            returning java.lang.Object.
>>> +            For now simply allow arbitrary pointer type conversions.  */
>>> +         && !(POINTER_TYPE_P (TREE_TYPE (gimple_call_lhs (stmt)))
>>> +              && POINTER_TYPE_P (TREE_TYPE (fntype))))
>>> +       {
>>> +         error ("invalid conversion in gimple call");
>>> +         debug_generic_stmt (TREE_TYPE (gimple_call_lhs (stmt)));
>>> +         debug_generic_stmt (TREE_TYPE (fntype));
>>> +         return true;
>>> +       }
>>>     }
>>>
>>>    fndecl = gimple_call_fndecl (stmt);
>>> @@ -3086,24 +3106,6 @@ verify_gimple_call (gimple stmt)
>>>       return true;
>>>     }
>>>
>>> -  fntype = gimple_call_fntype (stmt);
>>> -  if (gimple_call_lhs (stmt)
>>> -      && !useless_type_conversion_p (TREE_TYPE (gimple_call_lhs (stmt)),
>>> -                                    TREE_TYPE (fntype))
>>> -      /* ???  At least C++ misses conversions at assignments from
>>> -        void * call results.
>>> -        ???  Java is completely off.  Especially with functions
>>> -        returning java.lang.Object.
>>> -        For now simply allow arbitrary pointer type conversions.  */
>>> -      && !(POINTER_TYPE_P (TREE_TYPE (gimple_call_lhs (stmt)))
>>> -          && POINTER_TYPE_P (TREE_TYPE (fntype))))
>>> -    {
>>> -      error ("invalid conversion in gimple call");
>>> -      debug_generic_stmt (TREE_TYPE (gimple_call_lhs (stmt)));
>>> -      debug_generic_stmt (TREE_TYPE (fntype));
>>> -      return true;
>>> -    }
>>> -
>>>   if (gimple_call_chain (stmt)
>>>       && !is_gimple_val (gimple_call_chain (stmt)))
>>>     {
>>> @@ -3121,7 +3123,7 @@ verify_gimple_call (gimple stmt)
>>>          error ("static chain in indirect gimple call");
>>>          return true;
>>>        }
>>> -      fn = TREE_OPERAND (fn, 0);
>>> +      fn = TREE_OPERAND (gimple_call_fn (stmt), 0);
>>>
>>>       if (!DECL_STATIC_CHAIN (fn))
>>>        {
>>> @@ -7436,6 +7438,8 @@ do_warn_unused_result (gimple_seq seq)
>>>        case GIMPLE_CALL:
>>>          if (gimple_call_lhs (g))
>>>            break;
>>> +         if (gimple_call_internal_p (g))
>>> +           break;
>>>
>>>          /* This is a naked call, as opposed to a GIMPLE_CALL with an
>>>             LHS.  All calls whose value is ignored should be
>>> Index: gcc/tree-eh.c
>>> ===================================================================
>>> --- gcc/tree-eh.c       2011-04-18 10:18:49.000000000 +0100
>>> +++ gcc/tree-eh.c       2011-04-18 10:18:54.000000000 +0100
>>> @@ -2743,7 +2743,7 @@ same_handler_p (gimple_seq oneh, gimple_
>>>       || gimple_call_lhs (twos)
>>>       || gimple_call_chain (ones)
>>>       || gimple_call_chain (twos)
>>> -      || !operand_equal_p (gimple_call_fn (ones), gimple_call_fn (twos), 0)
>>> +      || !gimple_call_same_target_p (ones, twos)
>>
>> Why do we even end up here?  internal function calls should never throw,
>> maybe that is what you need to adjust?
>
> We didn't end up here for internal functions.  It was just a case of
> using the new API rather than continuing to do a lower-level check.

Ok, that's fine with me.

>>> @@ -565,8 +571,14 @@ print_expr_hash_elt (FILE * stream, cons
>>>         {
>>>           size_t i;
>>>           size_t nargs = element->expr.ops.call.nargs;
>>> +          gimple fn_from;
>>>
>>> -          print_generic_expr (stream, element->expr.ops.call.fn, 0);
>>> +          fn_from = element->expr.ops.call.fn_from;
>>> +          if (gimple_call_internal_p (fn_from))
>>> +            fputs (internal_fn_name (gimple_call_internal_fn (fn_from)),
>>> +                   stream);
>>> +          else
>>> +            print_generic_expr (stream, gimple_call_fn (fn_from), 0);
>>
>> 2nd time, can you add a print_gimple_call_target () function instead?
>
> OK.
>
>>> Index: gcc/tree-ssa-sccvn.c
>>> ===================================================================
>>> --- gcc/tree-ssa-sccvn.c        2011-04-18 10:18:49.000000000 +0100
>>> +++ gcc/tree-ssa-sccvn.c        2011-04-18 10:18:54.000000000 +0100
>>> @@ -911,7 +911,10 @@ copy_reference_ops_from_call (gimple cal
>>>   memset (&temp, 0, sizeof (temp));
>>>   temp.type = gimple_call_return_type (call);
>>>   temp.opcode = CALL_EXPR;
>>> -  temp.op0 = gimple_call_fn (call);
>>> +  if (gimple_call_internal_p (call))
>>> +    temp.op0 = NULL_TREE;
>>> +  else
>>> +    temp.op0 = gimple_call_fn (call);
>>
>> That will now value-number all internal calls the same.  A safe change
>> would be to trivially value-number internal calls, like with
>>
>> Index: gcc/tree-ssa-sccvn.c
>> ===================================================================
>> --- gcc/tree-ssa-sccvn.c        (revision 172640)
>> +++ gcc/tree-ssa-sccvn.c        (working copy)
>> @@ -3125,7 +3125,8 @@ visit_use (tree use)
>>           /* ???  We should handle stores from calls.  */
>>           else if (TREE_CODE (lhs) == SSA_NAME)
>>             {
>> -             if (gimple_call_flags (stmt) & (ECF_PURE | ECF_CONST))
>> +             if (!gimple_call_internal_p (stmt)
>> +                 && gimple_call_flags (stmt) & (ECF_PURE | ECF_CONST))
>>                 changed = visit_reference_op_call (lhs, stmt);
>>               else
>>                 changed = defs_to_varying (stmt);
>>
>> and a similar change in tree-ssa-pre.c:can_value_number_call.
>
> We really should be able to treat calls to pure internal functions
> like calls to pure "real" functions though.
>
> TBH, I think this is an example of why trying to so hard to avoid a tree
> code for the internal function is working against us.  Most of the patch
> feels like sprinkling hacks around the code base just because we don't
> have a real tree representation for what we're calling.  Any new code
> that handles calls is going to have to remember to make the same
> distinction.

Most code either doesn't care about the callee or only handles calls
to a specific function and thus already checks whether gimple_call_fndecl ()
returns NULL.  The patch doesn't feel like it adds hacks everywhere,
instead it is quite small (smaller than I thought at least).

> The tree I suggested was an INTERNAL_FUNCTION node, with the appropriate
> type and number attached.  The nodes could be shared, rather than be
> one-per-call.  If we had that, then I think this, and a lot of the other
> places I'm touching, would Just Work.

I don't think this is in any way better than untyped calls.  Instead you'd
run into a lot of generic tree stuff (think of walk_tree, tree_code_size, etc.).
Also it would be semantically the same as an untyped call, just in
non-tuple form which is backward from what we want. To avoid that you'd
have to at least do INTERNAL_UNARY_FUNCTION, INTERNAL_BINARY_FUNCTION,
etc. (and have to stop at TERNARY).
You would have exactly the same type issues.  Also you would have
the issue that such things can only be const, not pure, or you'd go
the non-tuples way again.

So no, an INTERNAL_FUNCTION tree code would be ugly.  If at all
the alternative would be a LOAD_LANE and STORE_LANE tree code,
which I already said is working against canonicalizing memory references
and thus would be need to split into a load via a MEM_REF and then
a SHUFFLE or PERMUTE unary op that works on a SSA name
array that was loaded.  But as you said, that's a lot more work ;)

Richard.

> Richard
>

Reply via email to