Bug 94693 - IPA SRA should elide unused out parameters
Summary: IPA SRA should elide unused out parameters
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: ipa (show other bugs)
Version: 10.0
: P3 enhancement
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: missed-optimization
Depends on:
Blocks: 90591
  Show dependency treegraph
 
Reported: 2020-04-21 14:28 UTC by Richard Biener
Modified: 2020-04-21 15:30 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Richard Biener 2020-04-21 14:28:10 UTC
IPA SRA should elide 'out' in

struct outs { int kind; int i; };

void foo (struct outs *out)
{
  if (out->kind == 0)
    ;
  else
    out->i = out->kind;
}

int main()
{
  struct outs out;
  out.kind = 3;
  foo (&out);
  // 'out' is unused [after the call]
}

IPA SRA transform should then produce

void foo.isra (int kind)
{
  struct outs out_;
  out_.kind = kind;
  struct outs *out = out_;
  if (out->kind == 0)
    ;
  else
    out->i = out->kind;
}

int main()
{
  struct outs out;
  out.kind = 3;
  foo (3);
}

and DCE can then eliminate the dead code.  Note the same can work for
the case where out is not written to at all in main () and thus nothing
needs to be passed to foo (that might be the most common case, like when
passing an alternate output by reference that's not needed).  The testcase
above is a more general case.

If analysis at the call site is flow-sensitive it can check whether any
side-effects to 'out' might be observable to handle

{
   struct outs out;
   if (test)
     {
       out.kind = 3;
       foo (&out);
       // out is dead after the call
     }
   else
     {
       bar (&out);
       printf ("%d", out.i);
     }
}

but probably IPA SRA local analysis isn't that powerful.

Note the advantage is not eliding the out variable at the caller side
but possible dead code removal in the callee which need to compute it
(similar to the now handled unused return value handling).
Comment 1 Richard Biener 2020-04-21 14:29:42 UTC
Inspired by the testcase in PR90591.
Comment 2 Richard Biener 2020-04-21 14:47:05 UTC
Another testcase would be like

int foo (void **ret)
{
  *ret = NULL;
  return 0;
}

int bar ()
{
  void *dummy;
  return bar (&dummy);
}


int main()
{
  void *dummy;
  if (!foo (&dummy))
    return 0;
  abort ();
}

and ret/dummy can be elided.  I've included the twist that we pass two different
'dummy' (but I understand IPA SRA works per call and doesn't try "sharing"
the clones?)
Comment 3 Richard Biener 2020-04-21 14:54:09 UTC
Or a bit more twisty

struct A { int key; double payload_A; };
struct B { int key; int payload_B; };

void foo (int *key)
{
  switch (*key)
  {
  case 1:
    ((struct A *)key)->payload_A = 1.0;
    break;
  case 2:
    ((struct B *)key)->payload_B = 1;
    break;
  default:;
  }
}

void bar()
{
  struct A s;
  s.key = 1;
  foo (&s);
}
void baz()
{
  struct B s;
  s.key = 2;
  foo  (&s);
}

to show that the type of the variable to instantiate as storage in the
callee is non-trivial (the only real constraint is it should be the
maximum size of the storage actually accessed).  Possibly simply
make sure the actually passed types are types_compatible_p and use
one of those.  With the above cases it also appears that IPA CP
could be the one handling this since &s is a (non-IP invariant) constant
and the initializer is a constant as well.  But I guess it's more IPA SRA.
Comment 4 Eric Gallager 2020-04-21 15:30:18 UTC
is there an opportunity to extend -Wunused-parameter here, too? I don't get anything from it currently... (see also bug 68230 for another case where IPA SRA could help extend -Wunused-parameter)