> On Apr 26, 2021, at 12:47 PM, Richard Sandiford <richard.sandif...@arm.com> 
> wrote:
> 
> Qing Zhao <qing.z...@oracle.com> writes:
>>>> @@ -1831,6 +2000,17 @@ gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_p)
>>>>           as they may contain a label address.  */
>>>>        walk_tree (&init, force_labels_r, NULL, NULL);
>>>>    }
>>>> +      /* When there is no explicit initializer, if the user requested,
>>>> +   We should insert an artifical initializer for this automatic
>>>> +   variable for non vla variables.  */
>>> 
>>> I think we should explain why we can skip VLAs here.
>> 
>> VLA is handled in another place already, it should be initialized with calls 
>> to memset/memcpy.
> 
> Yeah, what I meant here was that the comment should explain the
> difference between the handling of VLAs and non-VLAs.  It's fairly
> obvious when reading the patch, but it won't be as obvious once the
> patch is applied.

Okay, I see, will update the comments.
> 
>>>> +   children are to be processed.  TOP_OFFSET is the offset  of the 
>>>> processed
>>>> +   subtree which has to be subtracted from offsets of individual accesses 
>>>> to
>>>> +   get corresponding offsets for AGG.  GSI is a statement iterator used 
>>>> to place
>>>> +   the new statements.  */
>>>> +static void
>>>> +generate_subtree_deferred_init (struct access *access, tree agg,
>>>> +                          enum auto_init_type init_type,
>>>> +                          HOST_WIDE_INT top_offset,
>>>> +                          gimple_stmt_iterator *gsi,
>>>> +                          location_t loc)
>>>> +{
>>>> +  do
>>>> +    {
>>>> +      if (access->grp_to_be_replaced)
>>>> +  {
>>>> +    tree repl = get_access_replacement (access);
>>>> +    tree init_type_node
>>>> +      = build_int_cst (integer_type_node, (int) init_type);
>>>> +    gimple *call = gimple_build_call_internal (IFN_DEFERRED_INIT, 2,
>>>> +                                               repl, init_type_node);
>>>> +    gimple_call_set_lhs (call, repl);
>>> 
>>> AFAICT “access” is specifically for the lhs of the original call.
>>> So there seems to be an implicit assumption here that the lhs of the
>>> original call is the same as the first argument of the original call.
>>> Is that guaranteed/required?
>> 
>> For call to DEFFERED_INIT, yes, this is guaranteed.
> 
> OK, in that case…
> 
>>> If so, I think it's something that
>>> tree-cfg.c should check.  It might also be worth having an assertion
>>> in sra_modify_deferred_init.
>> I can definitely add an assertion to make sure this.
> 
> …I think we need the tree-cfg.c check too.  Having the check there
> ensures that the invariant is maintained throughout gimple.

Okay, will add check in tree-cfg.c too.

> 
>>>> +    gimple_set_location (call, loc);
>>>> +
>>>> +    sra_stats.subtree_deferred_init++;
>>>> +  }
>>>> +      else if (access->grp_to_be_debug_replaced)
>>>> +  {
>>>> +    /* FIXME, this part might have some issue.  */
>>>> +    tree drhs = build_debug_ref_for_model (loc, agg,
>>>> +                                           access->offset - top_offset,
>>>> +                                           access);
>>>> +    gdebug *ds = gimple_build_debug_bind (get_access_replacement (access),
>>>> +                                          drhs, gsi_stmt (*gsi));
>>>> +    gsi_insert_before (gsi, ds, GSI_SAME_STMT);
>>> 
>>> Would be good to fix the FIXME :-)
>> 
>> This is the part I am not very sure, so I added the FIXME in order to get 
>> more review and suggestion
>> to make sure it. -:)
>>> 
>>> I guess the thing we need to decide here is whether -ftrivial-auto-var-init
>>> should affect debug-only constructs too.
>> 
>> Where can I get more details on Debug-only constructs ?
> 
> What I meant by “debug-only construct” is a piece of source-level data
> (in this case a field of an aggregate) that has been optimised out of
> the executable code but still exists in debug stmts.

Okay, I see now.

Then, we should handle this case too, I think.

>  AIUI that's what
> the code above is handling.
> 
>>>> @@ -4863,6 +4863,29 @@ find_func_aliases_for_builtin_call (struct function 
>>>> *fn, gcall *t)
>>>>  return false;
>>>> }
>>>> 
>>>> +static void
>>>> +find_func_aliases_for_deferred_init (gcall *t)
>>>> +{
>>>> +  tree lhsop = gimple_call_lhs (t);
>>>> +  enum auto_init_type init_type
>>>> +    = (enum auto_init_type) TREE_INT_CST_LOW (gimple_call_arg (t, 1));
>>>> +  auto_vec<ce_s, 2> lhsc;
>>>> +  auto_vec<ce_s, 4> rhsc;
>>>> +  struct constraint_expr temp;
>>>> +
>>>> +  get_constraint_for (lhsop, &lhsc);
>>>> +  if (init_type == AUTO_INIT_ZERO && flag_delete_null_pointer_checks)
>>>> +    temp.var = nothing_id;
>>>> +  else
>>>> +    temp.var = nonlocal_id;
>>>> +  temp.type = ADDRESSOF;
>>>> +  temp.offset = 0;
>>>> +  rhsc.safe_push (temp);
>>>> +
>>>> +  process_all_all_constraints (lhsc, rhsc);
>>>> +  return;
>>>> +}
>>>> +
>>> 
>>> What's the reasoning behind doing it like this?  AFAICT the result
>>> of the call doesn't validly alias anything, regardless of the init type.
>>> Even if there happens to be a valid decl at the address given by the
>>> chosen fill pattern, there's no expectation that accesses to that
>>> decl will be coherent wrt accesses via the result of the call.
>> 
>> So, you mean the above change will not have any impact at all?
> 
> Stepping back a bit first: why did you add the code?  What case
> it is trying to handle?  Perhaps I misunderstood the motivation.
> 
> In the above reply I meant more that the code seems unnecessarily
> pessimistic.  It looks like it's based on:
> 
>  /* x = integer is all glommed to a single variable, which doesn't
>     point to anything by itself.  That is, of course, unless it is an
>     integer constant being treated as a pointer, in which case, we
>     will return that this is really the addressof anything.  This
>     happens below, since it will fall into the default case. The only
>     case we know something about an integer treated like a pointer is
>     when it is the NULL pointer, and then we just say it points to
>     NULL.
> 
>     Do not do that if -fno-delete-null-pointer-checks though, because
>     in that case *NULL does not fail, so it _should_ alias *anything.
>     It is not worth adding a new option or renaming the existing one,
>     since this case is relatively obscure.  */
>  if ((TREE_CODE (t) == INTEGER_CST
>       && integer_zerop (t))
>      /* The only valid CONSTRUCTORs in gimple with pointer typed
>        elements are zero-initializer.  But in IPA mode we also
>        process global initializers, so verify at least.  */
>      || (TREE_CODE (t) == CONSTRUCTOR
>         && CONSTRUCTOR_NELTS (t) == 0))
>    {
>      if (flag_delete_null_pointer_checks)
>       temp.var = nothing_id;
>      else
>       temp.var = nonlocal_id;
>      temp.type = ADDRESSOF;
>      temp.offset = 0;
>      results->safe_push (temp);
>      return;
>    }
> 
> But if the user writes:
> 
>   intptr_t x = 0;
>   *(int *)x = 42;
> 
> and compiles with -fno-delete-null-pointer-checks, then we have to
> assume that the code is valid and is accessing a global decl that the
> user knows is at address 0.  So in that case we need the &nonlocal_id
> constraint.
> 
> But even with the new option, I don't think:
> 
>   intptr_t x;
>   *(int *)x = 42;
> 
> is well-defined in the same way.  Maybe there's an argument that using
> -fno-delete-null-pointer-checks is such a niche case that we might as
> well treat it as though it were well-defined.  That'd feel to me like
> a half-measure though.  It comes back to Richard Smith's argument
> that we shouldn't try to create a new dialect of C/C++.  We're
> explicitly not trying to make:
> 
>   intptr_t x;
> 
> and
> 
>   intptr_t x = 0;
> 
> equivalent in all respects.  And if we were trying to make them
> equivalent, we'd need to do much more than this.
> 
> The same applies to the pattern case.  If “x” is initialised to a pattern
> that happens to point to a real decl, we don't have to preserve the
> order of accesses to the decl wrt accesses to “*x” (especially since
> we're hoping that “*x” will trap).
> 
> I think for aliasing purposes, the .DEFERRED_INIT return value is still
> analogous to an undefined SSA name, even though we will later generate
> code to initialise it.

Okay, I see now.

Will delete this part from the code.

> 
>>> In fact it might be simpler to have something like:
>>> 
>>> if (POINTER_TYPE_P (type) && TYPE_PRECISION (type) < 64)
>>>   return build_int_cstu (type, 0xAA);
>>> 
>>> if (INTEGRAL_TYPE_P (type) || POINTER_TYPE_P (type))
>>>   ...the wi::from_buffer thing above...;
>> 
>> Okay.
>>> 
>>> I'm not sure if this makes sense for NULLPTR_TYPE.
>> 
>> Is there a good testing case in gcc test suite about NULLPTR_TYPE that I can 
>> take a look?
> 
> I'm guessing you'll need to construct a new one to exercise this code path.

Will do this.

> 
>>>> +    case RECORD_TYPE:
>>>> +      {
>>>> +  tree field;
>>>> +  tree field_value;
>>>> +  vec<constructor_elt, va_gc> *v = NULL;
>>>> +  for (field= TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
>>>> +    {
>>>> +      if (TREE_CODE (field) != FIELD_DECL)
>>>> +        continue;
>>>> +      /* if the field is a variable length array, it should be the last
>>>> +         field of the record, and no need to initialize.  */
>>> 
>>> Why doesn't it need to be initialized though?
>> 
>> My understanding is, the compiler will not allocate memory for the latest 
>> field of the record if its a VLA, it’s the user’s responsibility to allocate 
>> Memory for it.  Therefore, compiler doesn’t  need to initialize it.
> 
> Ah, OK.  This sounds like the behaviour for flexible array members rather
> than VLA members.

Oh, Yes. My bad to use a wrong concept to confuse you.


>  E.g. GCC allows:
> 
>  void bar (int *);
>  int
>  foo (int a)
>  {
>    // VLA in struct
>    struct { int x[a]; } foo;
>    bar (foo.x);
>  }
> 
> In this case the foo.x really does have “a” elements that would
> need to be initialised.
> 
> I think the case you're talking about is:
> 
>  void bar (int *);
>  int
>  foo (int a)
>  {
>    // struct ending in a flexible array
>    struct { int prefix; int x[]; } foo;
>    bar (foo.x);
>  }
> 
> where foo.x has zero elements.

Yes. 

Then, that part of the code currently is okay?

> 
>>>> @@ -11950,6 +12088,72 @@ lower_bound_in_type (tree outer, tree inner)
>>>>    }
>>>> }
>>>> 
>>>> +/* Returns true when the given TYPE has padding inside it.
>>>> +   return false otherwise.  */
>>>> +bool
>>>> +type_has_padding (tree type)
>>> 
>>> Would it be possible to reuse __builtin_clear_padding here?
>> 
>> Not sure, where can I get more details on __builtin_clear_padding? I can 
>> study a little bit more on this to make sure this.
> 
> It's documented in doc/extend.texi.

Okay, will check on that.

Thanks a lot.

Qing
> 
> Thanks,
> Richard

Reply via email to