> On Jul 28, 2025, at 12:48, Jakub Jelinek <ja...@redhat.com> wrote:
> 
> On Wed, Jul 23, 2025 at 05:59:22PM +0000, Qing Zhao wrote:
>> struct S {
>>  int n;
>>  int *p __attribute__((counted_by(n)));
>> } *f;
>> Int *g;
>> void setup (int **ptr, int count)
>> {
>> *ptr = __builtin_malloc (sizeof (int) * count);
>>  g = *ptr;
>> };
>> int main ()
>> {
>> f = __builtin_malloc (sizeof (struct S));
>> setup (&f->p, 10);
> 
> This is neither read nor write, it is taking an address of f->p.
> The above case is definitely questionable because nothing really initializes
> f->n, so any later uses of f->p would be invalid unless it is initialized
> first.

 f->p is actually initialized by the call to the routine “setup” through the 
1st argument.  
So, later uses of f->p after the call to “setup” in the routine main should be 
valid. 

However, without inter-procedural analysis and data-flow information, we can 
not determine
whether a later read of f->p is valid or not. 

That’s the major concern I have.

> Anyway, the choices are not mark with .ACCESS_WITH_SIZE taking address of
> such pointers,

So, you mean:  Not generate call to .ACCESS_WITh_SIZE for a f->p when its 
address is taken, i.e, 
not generate call to .ACCESS_WITH_SIZE for &f->p?

> or mark it with another mode and handle it differently later.

What do you mean here? A little confused. 

> At least for the start, I'd strongly suggest the former.

Okay, I can do that.  -:)

> With the above setup mess, it will always be just best effort, if it is
> inline, bos pass can see what it has been initialized to and associated
> with, if not, then it will simply not know it has an counted_by attribute.

Yes, the tradeoff for selectively generating .ACCESS_WITH_SIZE  for a f->p 
depending 
on its context will be:  we cannot get the counted_by information reliably 
through the call
to .ACCESS_WITH_SIZE in middle-end. 

> 
>> C FE has no such capability to determine whether the f->p is a read or a 
>> write.  Is this right?
> 
> C certainly can determine that, otherwise e.g. the -Wunused-but-set-*
> warnings wouldn't work.

Thanks a lot for the info. I just checked how C FE handles -Wunused-but-set-* 
options,  and see that
DECL_READ_P and TREE_USED are used for this purpose.  Will check further. 


> If there is an lvalue to rvalue conversion, it was read, so you can attach
> .ACCESS_WITH_SIZE to that if it is COMPONENT_REF with pointer type with
> counted_by attribute.
> If there is not an lvalue to rvalue conversion, it is write or something
> else.

What does “something else” include?  Taken address of it, and ? 

Can we just generate the call to .ACCESS_WITH_SIZE inside the routine 
“convert_lvalue_to_rvalue”
when the “EXP” is a COMPONENT_REF that represents a pointer field with 
counted_by attribute? 

Will this routine include all the READs from a pointer field? 

> So, one possibility is e.g. to look for mark_exp_read calls.

> Another is try default_function_array_read_conversion and a few other spots.

Is there any possibility that we cannot find all the places to generate the 
call to .ACCESS_WITH_SIZE
for pointer fields that are read from? 

> 
> Or another option might be don't mark even the loads with .ACCESS_WITH_SIZE
> when pointer type, tweak the content of the counted_by attribute (its
> argument) instead on the FIELD_DECLs such that the middle-end could figure
> it out and just handle it on the bos pass side.  

Inserting call to .ACCESS_WITH_SIZE to IL has two major purposes as our initial 
design:

struct S {
 int n;
 int *p __attribute__((counted_by(n)));
} *f;

For
 f->p = malloc (sz * sizeof (int));
 f->n = sz;
 __builtin_dynamic_object_size (f->p, 0);

1. Encode the implicit data dependence on “f->n” from 
__builtin_dynamic_object_size (f->p, 0) to IL
    to avoid incorrect code reordering of “f->n = sz” and 
“__builtin_dynamic_object_size (f->p, 0) before object size phase. 

Before any middle end optimization, we should generate:

  f->p = malloc (sz * sizeof (int));
  f->n = sz;
  tmp = .ACCESS_WITH_SIZE (f->p, &f->n, …);
__builtin_dynamic_object_size (tmp, 0);

2. Carry the size information explicitly in IL.  Then object_size phase will 
consistently get the size information from the 
    Call to .ACCESS_WITH_SIZE without other hack. 

Due to the above 1, we should generate the call to .ACCESS_WITH_SIZE BEFORE any 
middle-end optimization. 

> Though if counted_by
> argument is not just an identifier of a field in the same structure but
> complex expression, trying to reintroduce it into the IL might be too
> challenging at that point.

When the counted_by field is extended to complex expression,  the issue with 
implicit data dependence embedded in the 
__builtin_dynamic_object_size still exist (and might be even worse),  adding 
the expression explicitly into the IL through
an argument to .ACCESS_WITH_SIZE still necessary. 

For example:

int number;

struct S {
 Int n;
 Int *p __attribute__((counted_by_exp (int n; number * n)))
}

f->p = malloc (10 * 4 * sizeof (int));
number = 10;                                    
f->n = 4;
__builtin_dynamic_object_size (f->p, 0);

In the above, there is no data dependence between “number = 10”, “f->n = 4” and 
“__builtin_dynamic_object_size (f->p, 0)” in IL,
The compiler optimization is freely to reorder them. 

IN order to avoid such incorrect compiler optimization, we should insert the 
call to .ACCESS_WITH_SIZE to make such implicit
data dependence as explicit. 

However, we need to figure out how to pass the expressions to the call to 
.ACCESS_WITH_SIZE. 

Thanks.

Qing



> 
> Jakub
> 

Reply via email to