https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116533

            Bug ID: 116533
           Summary: Possibly missing SAVE_EXPR in
                    get_member_function_from_ptrfunc()
           Product: gcc
           Version: 15.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: sirl at gcc dot gnu.org
  Target Milestone: ---

This came up during the discussion of PR 116449.
get_member_function_from_ptrfunc() generates a statement with repeated
expressions, but the repeated expressions are not wrapped into SAVE_EXPRs.

This code

class C
{
public:
        void P(int);
        void IP();
        int parr[16];
};

typedef void (C::*fp)();

typedef struct arr_t
{
         fp func;
} arr_t;

static arr_t farr[1] =
{
        { &C::IP },
};

void C::P(int c)
{
        ((*this).*farr[parr[c]].func)();
}

results in this statement for C::P() in .original:

<<cleanup_point <<< Unknown tree: expr_stmt
  (((long int) farr[((struct C *) this)->parr[c]].func.__pfn & 1) != 0) 
 ? (void C::<T3fd> (struct C *) *) *(*((int (*) () * *) this 
   + (sizetype) farr[((struct C *) this)->parr[c]].func.__delta) 
   + (sizetype) ((long int) farr[((struct C *) this)->parr[c]].func.__pfn +
-1)) 
 : farr[((struct C *) this)->parr[c]].func.__pfn 
   ((struct C *) this + (sizetype) farr[((struct C *)
this)->parr[c]].func.__delta) >>>>>;

As you can see the terms "farr[((struct C *) this)->parr[c]].func" and
"farr[((struct C *) this)->parr[c]].func.__pfn" are used everywhere (condition
and both arms of the ternary).

Compiling results in the following .gimple:

void C::P (struct C * const this, int c)
{
  void C::<T3fd> (struct C *) * iftmp.0;

  # DEBUG BEGIN_STMT
  _1 = this->parr[c];
  _2 = farr[_1].func.__pfn;
  _3 = (long int) _2;
  _4 = _3 & 1;
  if (_4 != 0) goto <D.2821>; else goto <D.2822>;
  <D.2821>:
  _5 = this->parr[c];
  _6 = farr[_5].func.__delta;
  _7 = (sizetype) _6;
  _8 = this + _7;
  _9 = *_8;
  _10 = this->parr[c];
  _11 = farr[_10].func.__pfn;
  _12 = (long int) _11;
  _13 = _12 + -1;
  _14 = (sizetype) _13;
  _15 = _9 + _14;
  iftmp.0 = *_15;
  goto <D.2823>;
  <D.2822>:
  _16 = this->parr[c];
  iftmp.0 = farr[_16].func.__pfn;
  <D.2823>:
  iftmp.1_17 = iftmp.0;
  _18 = this->parr[c];
  _19 = farr[_18].func.__delta;
  _20 = (sizetype) _19;
  _21 = this + _20;
  iftmp.1_17 (_21);
}

Now with this tentative patch:

@@ -4191,11 +4191,10 @@ get_member_function_from_ptrfunc (tree
*instance_ptrptr, tree function,
       if (TREE_SIDE_EFFECTS (instance_ptr))
        instance_ptr = instance_save_expr = save_expr (instance_ptr);

-      if (TREE_SIDE_EFFECTS (function))
-       function = save_expr (function);
+      function = save_expr(function);

       /* Start by extracting all the information from the PMF itself.  */
-      e3 = pfn_from_ptrmemfunc (function);
+      e3 = save_expr(pfn_from_ptrmemfunc (function));
       delta = delta_from_ptrmemfunc (function);
       idx = build1 (NOP_EXPR, vtable_index_type, e3);
       switch (TARGET_PTRMEMFUNC_VBIT_LOCATION)

results in this statement:

<<cleanup_point <<< Unknown tree: expr_stmt
  (((long int) SAVE_EXPR <SAVE_EXPR <farr[((struct C *)
this)->parr[c]].func>.__pfn> & 1) != 0) 
   ? (void C::<T40a> (struct C *) *) *(*((int (*) () * *) this 
     + (sizetype) SAVE_EXPR <farr[((struct C *) this)->parr[c]].func>.__delta) 
     + (sizetype) ((long int) SAVE_EXPR <SAVE_EXPR <farr[((struct C *)
this)->parr[c]].func>.__pfn> + -1))
   : SAVE_EXPR <SAVE_EXPR <farr[((struct C *) this)->parr[c]].func>.__pfn> 
        ((struct C *) this + (sizetype) SAVE_EXPR <farr[((struct C *)
this)->parr[c]].func>.__delta) >>>>>;

and gimple like this:

void C::P (struct C * const this, int c)
{
  void C::<T40a> (struct C *) * iftmp.0;
  struct 
{
  void C::<T40a> (struct C *) * __pfn;
  long int __delta;
} D.2853;
  void C::<T40a> (struct C *) * D.2854;

  # DEBUG BEGIN_STMT
  _1 = this->parr[c];
  D.2853 = farr[_1].func;
  D.2854 = D.2853.__pfn;
  _2 = (long int) D.2854;
  _3 = _2 & 1;
  if (_3 != 0) goto <D.2855>; else goto <D.2856>;
  <D.2855>:
  _4 = D.2853.__delta;
  _5 = (sizetype) _4;
  _6 = this + _5;
  _7 = *_6;
  _8 = (long int) D.2854;
  _9 = _8 + -1;
  _10 = (sizetype) _9;
  _11 = _7 + _10;
  iftmp.0 = *_11;
  goto <D.2857>;
  <D.2856>:
  iftmp.0 = D.2854;
  <D.2857>:
  iftmp.1_12 = iftmp.0;
  _13 = D.2853.__delta;
  _14 = (sizetype) _13;
  _15 = this + _14;
  iftmp.1_12 (_15);
}

In my eyes this gimple looks much better, but there maybe valid reasons why the
SAVE_EXPRs were not added before.
But a lot of the code in get_member_function_from_ptrfunc() is decades old
("75th Cygnus merge" for example), so it's hard
to find anything related in the ChangeLogs.
The tentative patch maybe incomplete and/or too broad, but it nicely shows
what's going on.

So, what do you think? Should more SAVE_EXPR be added in
get_member_function_from_ptrfunc()?

Reply via email to