diff mbox series

[v5,2/4] tree-object-size: Handle function parameters

Message ID 20211218123511.139456-3-siddhesh@gotplt.org
State New
Headers show
Series __builtin_dynamic_object_size | expand

Commit Message

Siddhesh Poyarekar Dec. 18, 2021, 12:35 p.m. UTC
Handle hints provided by __attribute__ ((access (...))) to compute
dynamic sizes for objects.

gcc/ChangeLog:

	* tree-object-size.c: Include tree-dfa.h.
	(parm_object_size): New function.
	(collect_object_sizes_for): Call it.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c (test_parmsz_simple):
	New function.
	(main): Call it.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 .../gcc.dg/builtin-dynamic-object-size-0.c    | 11 ++++
 gcc/tree-object-size.c                        | 50 ++++++++++++++++++-
 2 files changed, 60 insertions(+), 1 deletion(-)

Comments

Jakub Jelinek Jan. 10, 2022, 10:50 a.m. UTC | #1
On Sat, Dec 18, 2021 at 06:05:09PM +0530, Siddhesh Poyarekar wrote:
> @@ -1440,6 +1441,53 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
>    return reexamine;
>  }
>  
> +/* Find size of an object passed as a parameter to the function.  */
> +
> +static void
> +parm_object_size (struct object_size_info *osi, tree var)
> +{
> +  int object_size_type = osi->object_size_type;
> +  tree parm = SSA_NAME_VAR (var);
> +
> +  if (!(object_size_type & OST_DYNAMIC) || !POINTER_TYPE_P (TREE_TYPE (parm)))
> +    expr_object_size (osi, var, parm);

This looks very suspicious.  Didn't you mean { expr_object_size (...); return; } here?
Because the code below e.g. certainly assumes OST_DYNAMIC and that TREE_TYPE (parm)
is a pointer type (otherwise TREE_TYPE (TREE_TYPE (...) wouldn't work.

> +
> +  /* Look for access attribute.  */
> +  rdwr_map rdwr_idx;
> +
> +  tree fndecl = cfun->decl;
> +  const attr_access *access = get_parm_access (rdwr_idx, parm, fndecl);
> +  tree typesize = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (parm)));
> +  tree sz = NULL_TREE;
> +
> +  if (access && access->sizarg != UINT_MAX)

Perhaps && typesize here?  It makes no sense to e.g. create ssa default def
when you aren't going to use it in any way.

> +    {
> +      tree fnargs = DECL_ARGUMENTS (fndecl);
> +      tree arg = NULL_TREE;
> +      unsigned argpos = 0;
> +
> +      /* Walk through the parameters to pick the size parameter and safely
> +	 scale it by the type size.  */
> +      for (arg = fnargs; argpos != access->sizarg && arg;
> +	   arg = TREE_CHAIN (arg), ++argpos);

Instead of a loop with empty body wouldn't it be better to
do the work in that for loop?
I.e. take argpos != access->sizarg && from the condition,
replace arg != NULL_TREE with that argpos == access->sizarg
and add a break;?

> +
> +      if (arg != NULL_TREE && INTEGRAL_TYPE_P (TREE_TYPE (arg)))
> +	{
> +	  sz = get_or_create_ssa_default_def (cfun, arg);

Also, I must say I'm little bit worried about this
get_or_create_ssa_default_def call.  If the SSA_NAME doesn't exist,
so you create it and then attempt to use it but in the end don't
because e.g. some PHI's another argument was unknown etc., will
that SSA_NAME be released through release_ssa_name?
I think GIMPLE is fairly unhappy if there are SSA_NAMEs created and not
released that don't appear in the IL anywhere.

> +	  if (sz != NULL_TREE)
> +	    {
> +	      sz = fold_convert (sizetype, sz);
> +	      if (typesize)
> +		sz = size_binop (MULT_EXPR, sz, typesize);
> +	    }
> +	}
> +    }

	Jakub
Siddhesh Poyarekar Jan. 11, 2022, 12:32 a.m. UTC | #2
On 10/01/2022 16:20, Jakub Jelinek wrote:
> On Sat, Dec 18, 2021 at 06:05:09PM +0530, Siddhesh Poyarekar wrote:
>> @@ -1440,6 +1441,53 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
>>     return reexamine;
>>   }
>>   
>> +/* Find size of an object passed as a parameter to the function.  */
>> +
>> +static void
>> +parm_object_size (struct object_size_info *osi, tree var)
>> +{
>> +  int object_size_type = osi->object_size_type;
>> +  tree parm = SSA_NAME_VAR (var);
>> +
>> +  if (!(object_size_type & OST_DYNAMIC) || !POINTER_TYPE_P (TREE_TYPE (parm)))
>> +    expr_object_size (osi, var, parm);
> 
> This looks very suspicious.  Didn't you mean { expr_object_size (...); return; } here?
> Because the code below e.g. certainly assumes OST_DYNAMIC and that TREE_TYPE (parm)
> is a pointer type (otherwise TREE_TYPE (TREE_TYPE (...) wouldn't work.

Indeed, fixed.

> 
>> +
>> +  /* Look for access attribute.  */
>> +  rdwr_map rdwr_idx;
>> +
>> +  tree fndecl = cfun->decl;
>> +  const attr_access *access = get_parm_access (rdwr_idx, parm, fndecl);
>> +  tree typesize = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (parm)));
>> +  tree sz = NULL_TREE;
>> +
>> +  if (access && access->sizarg != UINT_MAX)
> 
> Perhaps && typesize here?  It makes no sense to e.g. create ssa default def
> when you aren't going to use it in any way.

The typesize is only for scaling; the result of 
get_or_create_ssa_default_def should get returned unscaled if it is 
non-NULL and typesize is NULL; the latter happens when the type is void *:

	  sz = get_or_create_ssa_default_def (cfun, arg);
	  if (sz != NULL_TREE)
	    {
	      sz = fold_convert (sizetype, sz);
	      if (typesize)
		sz = size_binop (MULT_EXPR, sz, typesize);
	    }
	}

> 
>> +    {
>> +      tree fnargs = DECL_ARGUMENTS (fndecl);
>> +      tree arg = NULL_TREE;
>> +      unsigned argpos = 0;
>> +
>> +      /* Walk through the parameters to pick the size parameter and safely
>> +	 scale it by the type size.  */
>> +      for (arg = fnargs; argpos != access->sizarg && arg;
>> +	   arg = TREE_CHAIN (arg), ++argpos);
> 
> Instead of a loop with empty body wouldn't it be better to
> do the work in that for loop?
> I.e. take argpos != access->sizarg && from the condition,
> replace arg != NULL_TREE with that argpos == access->sizarg
> and add a break;?

Fixed.

> 
>> +
>> +      if (arg != NULL_TREE && INTEGRAL_TYPE_P (TREE_TYPE (arg)))
>> +	{
>> +	  sz = get_or_create_ssa_default_def (cfun, arg);
> 
> Also, I must say I'm little bit worried about this
> get_or_create_ssa_default_def call.  If the SSA_NAME doesn't exist,
> so you create it and then attempt to use it but in the end don't
> because e.g. some PHI's another argument was unknown etc., will
> that SSA_NAME be released through release_ssa_name?
> I think GIMPLE is fairly unhappy if there are SSA_NAMEs created and not
> released that don't appear in the IL anywhere.

AFAICT, set_ss_default_def ends up creating a definition for the new 
SSA_NAME it creates, so it does end up in the IR and in case of object 
size computation failure, it just ends up being a dead store.  I've 
added a test to verify this:

size_t
__attribute__ ((access (__read_write__, 1, 3)))
__attribute__ ((noinline))
test_parmsz_unknown (void *obj, void *unknown, size_t sz, int cond)
{
   return __builtin_dynamic_object_size (cond ? obj : unknown, 0);
}

which works as expected and returns -1.

Thanks,
Siddhesh
diff mbox series

Patch

diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
index 81588cb28a6..7616ffa4cf0 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -51,6 +51,14 @@  test_deploop (size_t sz, size_t cond)
   return __builtin_dynamic_object_size (bin, 0);
 }
 
+size_t
+__attribute__ ((access (__read_write__, 1, 2)))
+__attribute__ ((noinline))
+test_parmsz_simple (void *obj, size_t sz)
+{
+  return __builtin_dynamic_object_size (obj, 0);
+}
+
 unsigned nfails = 0;
 
 #define FAIL() ({ \
@@ -69,6 +77,9 @@  main (int argc, char **argv)
     FAIL ();
   if (test_deploop (128, 129) != 32)
     FAIL ();
+  if (test_parmsz_simple (argv[0], __builtin_strlen (argv[0]) + 1)
+      != __builtin_strlen (argv[0]) + 1)
+    FAIL ();
 
   if (nfails > 0)
     __builtin_abort ();
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 95cb44d9c7e..bf33ac93b93 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -32,6 +32,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "gimple-fold.h"
 #include "gimple-iterator.h"
 #include "tree-cfg.h"
+#include "tree-dfa.h"
 #include "stringpool.h"
 #include "attribs.h"
 #include "builtins.h"
@@ -1440,6 +1441,53 @@  cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   return reexamine;
 }
 
+/* Find size of an object passed as a parameter to the function.  */
+
+static void
+parm_object_size (struct object_size_info *osi, tree var)
+{
+  int object_size_type = osi->object_size_type;
+  tree parm = SSA_NAME_VAR (var);
+
+  if (!(object_size_type & OST_DYNAMIC) || !POINTER_TYPE_P (TREE_TYPE (parm)))
+    expr_object_size (osi, var, parm);
+
+  /* Look for access attribute.  */
+  rdwr_map rdwr_idx;
+
+  tree fndecl = cfun->decl;
+  const attr_access *access = get_parm_access (rdwr_idx, parm, fndecl);
+  tree typesize = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (parm)));
+  tree sz = NULL_TREE;
+
+  if (access && access->sizarg != UINT_MAX)
+    {
+      tree fnargs = DECL_ARGUMENTS (fndecl);
+      tree arg = NULL_TREE;
+      unsigned argpos = 0;
+
+      /* Walk through the parameters to pick the size parameter and safely
+	 scale it by the type size.  */
+      for (arg = fnargs; argpos != access->sizarg && arg;
+	   arg = TREE_CHAIN (arg), ++argpos);
+
+      if (arg != NULL_TREE && INTEGRAL_TYPE_P (TREE_TYPE (arg)))
+	{
+	  sz = get_or_create_ssa_default_def (cfun, arg);
+	  if (sz != NULL_TREE)
+	    {
+	      sz = fold_convert (sizetype, sz);
+	      if (typesize)
+		sz = size_binop (MULT_EXPR, sz, typesize);
+	    }
+	}
+    }
+  if (!sz)
+    sz = size_unknown (object_size_type);
+
+  object_sizes_set (osi, SSA_NAME_VERSION (var), sz, sz);
+}
+
 /* Compute an object size expression for VAR, which is the result of a PHI
    node.  */
 
@@ -1610,7 +1658,7 @@  collect_object_sizes_for (struct object_size_info *osi, tree var)
     case GIMPLE_NOP:
       if (SSA_NAME_VAR (var)
 	  && TREE_CODE (SSA_NAME_VAR (var)) == PARM_DECL)
-	expr_object_size (osi, var, SSA_NAME_VAR (var));
+	parm_object_size (osi, var);
       else
 	/* Uninitialized SSA names point nowhere.  */
 	unknown_object_size (osi, var);