Hi, Sid, I further studied the approach to generate .ACCESS_WITH_SIZE in C FE for the pointer with a STRUCTURE TYPE with counted-by FAM.
One thing bother me is the following: For the following small example: [ counted_by_whole]$ cat t.c #include <stdlib.h> #include <stddef.h> struct annotated { size_t count; char other; char array[] __attribute__((counted_by (count))); }; #define MAX(A, B) (A > B) ? (A) : (B) #define ALLOC_SIZE_ANNOTATED(COUNT) \ MAX(sizeof (struct annotated), \ offsetof(struct annotated, array[0]) + (COUNT) * sizeof(char)) static struct annotated * __attribute__((__noinline__)) alloc_buf (int index) { struct annotated *p; p = (struct annotated *) malloc (ALLOC_SIZE_ANNOTATED(index)); p->count = index; return p; } int main() { struct annotated *q = alloc_buf (10); __builtin_printf ("the bdos whole is %d\n", __builtin_dynamic_object_size(q, 1)); return 0; } The gimple IR is: 1 int main () 2 { 3 int D.5072; 4 5 { 6 struct annotated * q; 7 8 q = alloc_buf (10); 9 _1 = __builtin_dynamic_object_size (q, 1); 10 __builtin_printf ("the bdos whole is %d\n", _1); 11 D.5072 = 0; 12 return D.5072; 13 } 14 D.5072 = 0; 15 return D.5072; 16 } 17 18 19 __attribute__((noinline)) 20 struct annotated * alloc_buf (int index) 21 { 22 struct annotated * D.5074; 23 struct annotated * p; 24 25 _1 = (long unsigned int) index; 26 _2 = _1 + 9; 27 _3 = MAX_EXPR <_2, 16>; 28 p = malloc (_3); 29 _4 = (long unsigned int) index; 30 p->count = _4; 31 D.5074 = p; 32 return D.5074; 33 } When we generate the .ACCESS_WITH_SIZE for a pointer reference to “struct annotated”, Looks like all the pointer references, at line 8, “q”, at line 9, “q”, at line 28, “p”, need to be changed to a call to .ACCESS_WITH_SIZE. this might increase the IR size unnecessarily. Might have some Impact on the inlining decision heuristics or other heuristic that depend on the code size. Not sure whether this is a concern or not. Any comment? Qing > On Apr 8, 2025, at 10:53, Qing Zhao <qing.z...@oracle.com> wrote: > > > >> On Apr 7, 2025, at 22:15, Siddhesh Poyarekar <siddh...@gotplt.org> wrote: >> >> On 2025-04-07 14:53, Qing Zhao wrote: >>>> Is there a reason to do this at the very end like this and not in the >>>> GIMPLE_ASSIGN case in the switch block? Something like this: >>>> >>>> tree rhs = gimple_assign_rhs1 (stmt); >>>> tree counted_by_ref = NULL_TREE; >>>> if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR >>>> || (gimple_assign_rhs_code (stmt) == ADDR_EXPR >>>> && TREE_CODE (TREE_OPERAND (rhs, 0)) == MEM_REF)) >>>> reexamine = plus_stmt_object_size (osi, var, stmt); >>>> else if (gimple_assign_rhs_code (stmt) == COND_EXPR) >>>> reexamine = cond_expr_object_size (osi, var, stmt); >>>> else if (gimple_assign_single_p (stmt) >>>> || gimple_assign_unary_nop_p (stmt)) >>>> { >>>> if (TREE_CODE (rhs) == SSA_NAME >>>> && POINTER_TYPE_P (TREE_TYPE (rhs))) >>>> reexamine = merge_object_sizes (osi, var, rhs); >>>> else >>>> expr_object_size (osi, var, rhs); >>>> } >>>> + else if ((counted_by_ref = fam_struct_with_counted_by (rhs))) >>>> + record_fam_object_size (osi, var, counted_by_ref); >>>> else >>>> unknown_object_size (osi, var); >>>> >>>> where you can then fold in all your gating conditions, including getting >>>> the counted_by ref into the fam_struct_with_counted_by and then limit the >>>> record_fam_object_size to just evaluating the type size + counted_by size. >>>> >>>> This may even help avoid the insertion order hack you have to do, i.e. the >>>> gsi_insert_seq_before vs gsi_insert_seq_after. >>> This is a good suggestion. I will try this to see any issue there. >>> My initial thought is to give the counted_by information the lowest priority >>> if there are other information (for example, malloc) available. >>> Do you see any issue here? >> >> No, that is the right idea, but I don't know if you'll actually need to >> choose. AFAICT, you'll either be able to trace the pointer to an >> allocation, in which case the fallback is unnecessary. Otherwise you'll >> trace it to one of the following: >> >> 1. An assignment from an expression that returns the pointer >> 2. A NOP with a var_decl, which is handled in the GIMPLE_NOP case; you'd >> need to add a similar hook there. >> >> I can't think of any other cases off the top of my head, how about you? >> >>>> Also, it seems like simply making build_counted_by_ref available may be >>>> unnecessary and maybe you could explore associating the counted_by >>>> component_ref with the parent type somehow. Alternatively, how about >>>> building an .ACCESS_WITH_SIZE for types with FAM that have a counted_by? >>>> That would allow the current access_with_size_object_size() to work out of >>>> the box. >>> I am not sure about this though. >>> Our initial design is to change every component_ref (which is a reference >>> to the FAM) >>> in the data flow of the routine that has a counted_by attribute to a call >>> to .ACCESS_WITH_SIZE. >>> Then put this call to .ACCESS_WITH_SIZE into the data flow of the routine. >>> Now, if we try to associate counted_by information to the parent type, how >>> can we add such information >>> To the data flow of the routine if there is no explicit reference to the >>> array itself? >> >> I'm not entirely sure, but maybe whenever there is an access on a ptr to the >> parent struct, add a call to .ACCESS_WITH_SIZE there, with a built >> expression for its size? e.g for: >> >> struct S >> { >> size_t c; >> char a[] __counted_by (c); >> } >> >> void foo (Struct S *s) >> { >> ... >> sz = __builtin_dynamic_object_size (s, 0); >> ... >> } >> >> we could generate: >> >> void foo (struct S *s) >> { >> ... >> sz_exp = c + sizeof (struct S); >> s_1 = .ACCESS_WITH_SIZE (&s..., &c, ...); >> ... >> sz = __builtin_dynamic_object_size (*s_1, 0); >> } >> > > Yes, I prefer this approach. > > For the definition of .ACCESS_WITH_SIZE: > > ACCESS_WITH_SIZE (REF_TO_OBJ, REF_TO_SIZE, CLASS_OF_SIZE, > TYPE_OF_SIZE, ACCESS_MODE) > which returns the REF_TO_OBJ same as the 1st argument; > > 1st argument REF_TO_OBJ: The reference to the object; > 2nd argument REF_TO_SIZE: The reference to the size of the object, > 3rd argument CLASS_OF_SIZE: The size referenced by the REF_TO_SIZE > represents > 0: the number of bytes. > 1: the number of the elements of the object type; > 4th argument TYPE_OF_SIZE: A constant 0 with its TYPE being the same as the > TYPE > of the object referenced by REF_TO_SIZE > 5th argument ACCESS_MODE: > -1: Unknown access semantics > 0: none > 1: read_only > 2: write_only > 3: read_write > 6th argument: A constant 0 with the pointer TYPE to the original flexible > array type. > > Currently, we only generate .ACCESS_WITH_SIZE for p->array if it’s a FAM with > counted_by, the 3rd argument > of this call is 1 (the number of the elements of the object type). And this > information can be used in both __builtin_object_size > and array bound sanitizer. > > For a reference to a structure with FAM, such as p, we can generate a call to > .ACCESS_WITH_SIZE whose 3rd argument > is 0 (the number of bytes). And this information can be used in > __builtin_object_size. But not in array bound sanitizer. > > So, for your above example: > > struct S > { > size_t c; > char a[] __counted_by (c); > } > > void foo (Struct S *s) > { > ... > sz = __builtin_dynamic_object_size (s, 0); > … > } > > C FE could generate: > > void foo (struct S *s) > { > … > If (!type_unsigned (typeof (s->c)) > s->c = (s->c < 0) ? 0 : s->c; > sz_exp = MAX (sizeof (struct S), offset (struct S, a) + s->c * sizeof (char)); > s_1 = .ACCESS_WITH_SIZE (&s, &sz_exp, 0, ...); > ... > sz = __builtin_dynamic_object_size (*s_1, 0); > } > > > Then, in the tree-object-size.cc <http://tree-object-size.cc/>, we can use > the path to access_with_size_object_size() directly. > > How do you think? > > Thanks a lot for your suggestion. > > Qing >> or something like that. But like I said, it's an alternative idea to avoid >> special-casing in tree-object-size, which should provide size information >> across all passes not just for object size. >> >> Thanks, >> Sid