diff mbox

[Fortran,OOP] PR 50919: Don't use vtable for NON_OVERRIDABLE TBP

Message ID CAKwh3qi8rsBrgUTEGGDzbWAdC3S+8VpdySb8Qp6kF7szbgWt-A@mail.gmail.com
State New
Headers show

Commit Message

Janus Weil Nov. 6, 2011, 11:14 p.m. UTC
Hi all,

up to now we call all type-bound procedures in a dynamic way, i.e.
through their entry in the vtable. However, for non-overridable
procedures this is not necessary. Since they can not be overridden, a
call to those can be resolved at compile time to an ordinary function
call, without the need of a 'detour' through the vtable. This is what
the attached patch does, thereby removing unneeded overhead and
improving the possibility of optimization (inlining, etc).

The patch actually consists of two parts:
1) The resolve.c part prevents the conversion to a PPC call via the
_vptr (for functions and subroutines).
2) The class.c parts prevents adding the non-overridable TBP to the vtable.

As noted by Tobias, the second part breaks the ABI, so we might
consider deferring it until other ABI-breaking features will be
implemented (cf. http://gcc.gnu.org/wiki/LibgfortranAbiCleanup). On
the other hand, one could argue that the OOP ABI is still quite young
and hasn't really stabilized yet (it was broken already from 4.5 to
4.6), so we might as well break it again. I know that there are a
couple of real-world codes out there, which make use of gfortran's OOP
features already, but I have a hard time estimating how many such
projects exists, or how problematic an ABI breaking would be for them
(user input welcome).

So, the question is: Should I commit both parts, or only the resolve.c
one for now? The patch was regtested on x86_64-unknown-linux-gnu.

Cheers,
Janus


2011-11-06  Janus Weil  <janus@gcc.gnu.org>

	PR fortran/50919
	* class.c (add_proc_comp): Don't add non-overridable procedures to the
	vtable.
	* resolve.c (resolve_typebound_function,resolve_typebound_subroutine):
	Don't generate a dynamic _vptr call for non-overridable procedures.

2011-11-06  Janus Weil  <janus@gcc.gnu.org>

	PR fortran/50919
	* gfortran.dg/typebound_call_21.f03: New.

Comments

Paul Richard Thomas Nov. 7, 2011, 7:46 a.m. UTC | #1
Dear Janus,

On Mon, Nov 7, 2011 at 12:14 AM, Janus Weil <janus@gcc.gnu.org> wrote:

> The patch actually consists of two parts:
> 1) The resolve.c part prevents the conversion to a PPC call via the
> _vptr (for functions and subroutines).

This is obviously OK

> 2) The class.c parts prevents adding the non-overridable TBP to the vtable.
>
> As noted by Tobias, the second part breaks the ABI, so we might
> consider deferring it until other ABI-breaking features will be
> implemented (cf. http://gcc.gnu.org/wiki/LibgfortranAbiCleanup). On
> the other hand, one could argue that the OOP ABI is still quite young
> and hasn't really stabilized yet (it was broken already from 4.5 to
> 4.6), so we might as well break it again. I know that there are a
> couple of real-world codes out there, which make use of gfortran's OOP
> features already, but I have a hard time estimating how many such
> projects exists, or how problematic an ABI breaking would be for them
> (user input welcome).

Do we need to exclude it from the _vtable?  I have to confess that,
although I tried, I could not think of any reason to exclude it.  On
the other hand, I could not see any great harm in retaining a pointer,
albeit a redundant one.

>
> So, the question is: Should I commit both parts, or only the resolve.c
> one for now? The patch was regtested on x86_64-unknown-linux-gnu.

In spite of the above remark, I think that you should commit both
parts.  Perhaps, until just before 4.7 release, a warning should be
triggered that says that pre-existing code containing non-overridable
procedures, should be recompiled before linking?

OK for trunk.

Thanks for the patch

Paul
Janus Weil Nov. 7, 2011, 8:50 a.m. UTC | #2
Hi Paul,

>> The patch actually consists of two parts:
>> 1) The resolve.c part prevents the conversion to a PPC call via the
>> _vptr (for functions and subroutines).
>
> This is obviously OK

thanks for the review.


>> 2) The class.c parts prevents adding the non-overridable TBP to the vtable.
>>
>> As noted by Tobias, the second part breaks the ABI, so we might
>> consider deferring it until other ABI-breaking features will be
>> implemented (cf. http://gcc.gnu.org/wiki/LibgfortranAbiCleanup). On
>> the other hand, one could argue that the OOP ABI is still quite young
>> and hasn't really stabilized yet (it was broken already from 4.5 to
>> 4.6), so we might as well break it again. I know that there are a
>> couple of real-world codes out there, which make use of gfortran's OOP
>> features already, but I have a hard time estimating how many such
>> projects exists, or how problematic an ABI breaking would be for them
>> (user input welcome).
>
> Do we need to exclude it from the _vtable?  I have to confess that,
> although I tried, I could not think of any reason to exclude it.  On
> the other hand, I could not see any great harm in retaining a pointer,
> albeit a redundant one.

Well, no, there is no hard reason why we *need* to exclude it. The
point is that, given we call these non-overriable procedure in a
"non-vptr" way, there is no need to have them in the vtable either ;)
.... except for "historical" / ABI-compatibility reasons.


>> So, the question is: Should I commit both parts, or only the resolve.c
>> one for now? The patch was regtested on x86_64-unknown-linux-gnu.
>
> In spite of the above remark, I think that you should commit both
> parts.

I would in principle also prefer to commit both, but we need to be
aware of the risks.

In fact I'm not sure whether it is so easy to produce wrong behavior
at all, combining code parts that were compiled with different
gfortran versions (4.6/4.7). Since the vtabs are supposed to be
'global', usually there should be only one version of them generated
in the main program (at least that is how it's supposed to be, but
probably that does not work in *every* situation yet). I will try to
see if I can find with a failing code example. (The issue is of course
that if the vtabs change their contents between 4.6 and 4.7, a
type-bound call could end up calling the wrong procedure).

Moreover, I have the feeling that NON_OVERRIDABLE is not so widely
used "in the wild" (but that is just a feeling, of course).


> Perhaps, until just before 4.7 release, a warning should be
> triggered that says that pre-existing code containing non-overridable
> procedures, should be recompiled before linking?

Do you mean the warning should be given on the trunk now, and later
taken out of the release? Why not the other way around?



> OK for trunk.
>
> Thanks for the patch

Thanks. I will wait a bit before committing to give others a chance to comment.

Cheers,
Janus
Janne Blomqvist Nov. 7, 2011, 9:01 a.m. UTC | #3
On Mon, Nov 7, 2011 at 10:50, Janus Weil <janus@gcc.gnu.org> wrote:
>>> 2) The class.c parts prevents adding the non-overridable TBP to the vtable.
>>>
>>> As noted by Tobias, the second part breaks the ABI, so we might
>>> consider deferring it until other ABI-breaking features will be
>>> implemented (cf. http://gcc.gnu.org/wiki/LibgfortranAbiCleanup). On
>>> the other hand, one could argue that the OOP ABI is still quite young
>>> and hasn't really stabilized yet (it was broken already from 4.5 to
>>> 4.6), so we might as well break it again. I know that there are a
>>> couple of real-world codes out there, which make use of gfortran's OOP
>>> features already, but I have a hard time estimating how many such
>>> projects exists, or how problematic an ABI breaking would be for them
>>> (user input welcome).
>>
>> Do we need to exclude it from the _vtable?  I have to confess that,
>> although I tried, I could not think of any reason to exclude it.  On
>> the other hand, I could not see any great harm in retaining a pointer,
>> albeit a redundant one.
>
> Well, no, there is no hard reason why we *need* to exclude it. The
> point is that, given we call these non-overriable procedure in a
> "non-vptr" way, there is no need to have them in the vtable either ;)
> .... except for "historical" / ABI-compatibility reasons.
>
>
>>> So, the question is: Should I commit both parts, or only the resolve.c
>>> one for now? The patch was regtested on x86_64-unknown-linux-gnu.
>>
>> In spite of the above remark, I think that you should commit both
>> parts.
>
> I would in principle also prefer to commit both, but we need to be
> aware of the risks.
>
> In fact I'm not sure whether it is so easy to produce wrong behavior
> at all, combining code parts that were compiled with different
> gfortran versions (4.6/4.7). Since the vtabs are supposed to be
> 'global', usually there should be only one version of them generated
> in the main program (at least that is how it's supposed to be, but
> probably that does not work in *every* situation yet). I will try to
> see if I can find with a failing code example. (The issue is of course
> that if the vtabs change their contents between 4.6 and 4.7, a
> type-bound call could end up calling the wrong procedure).
>
> Moreover, I have the feeling that NON_OVERRIDABLE is not so widely
> used "in the wild" (but that is just a feeling, of course).

Well, we have no stable module ABI either, so I wouldn't worry about
this. And, as you say, OOP is still a quite new feature (compared to
modules, at least!).
Tobias Burnus Nov. 7, 2011, 9:24 a.m. UTC | #4
On 11/07/2011 09:50 AM, Janus Weil wrote:
>>> As noted by Tobias, the second part breaks the ABI ... On
>>> the other hand, one could argue that the OOP ABI is still quite young
>>> and hasn't really stabilized yet (it was broken already from 4.5 to
>>> 4.6), so we might as well break it again. I know that there are a
>>> couple of real-world codes out there, which make use of gfortran's OOP
>>> features already, but I have a hard time estimating how many such
>>> projects exists, or how problematic an ABI breaking would be for them
>>> (user input welcome).
>> Do we need to exclude it from the _vtable?  I have to confess that,
>> although I tried, I could not think of any reason to exclude it.  On
>> the other hand, I could not see any great harm in retaining a pointer,
>> albeit a redundant one.
> Well, no, there is no hard reason why we *need* to exclude it. The
> point is that, given we call these non-overriable procedure in a
> "non-vptr" way, there is no need to have them in the vtable either ;)
> .... except for "historical" / ABI-compatibility reasons.
[...]

> In fact I'm not sure whether it is so easy to produce wrong behavior
> at all, combining code parts that were compiled with different
> gfortran versions (4.6/4.7). Since the vtabs are supposed to be
> 'global', usually there should be only one version of them generated
> in the main program (at least that is how it's supposed to be, but
> probably that does not work in *every* situation yet). I will try to
> see if I can find with a failing code example. (The issue is of course
> that if the vtabs change their contents between 4.6 and 4.7, a
> type-bound call could end up calling the wrong procedure).
>
> Moreover, I have the feeling that NON_OVERRIDABLE is not so widely
> used "in the wild" (but that is just a feeling, of course).

I concur that it is not that simple to produce a failing example. The 
only solid example where it really matters is a closed-source library 
which comes with a .f90 file which contains the module information, 
which is needed to to produce the .mod files.

All other ideas are based on some careful recompilation and relinking, 
which avoids touching the .mod files, which is possible but either 
nontrivial or unlikely. Example: Recompiling the library and using it 
with an old program - for instance as shared library or by mere relinking.

Note that the issue won't go away soon. We will have for years 
installations which have 4.6 and some which have 4.7 or later (e.g. with 
some OS update). Thus, one should be careful with the argument that 
there are not so many non_overridable programs in the wild as that might 
change in the next years. Though, it might take a while longer until the 
users realize that non_overridable provides a performance advantage. (By 
the way, PSBLAS does not include non_overridable.) -- Additionally, I 
*do* see a difference between GCC 4.5->4.6 and 4.6->4.7. There were many 
issues with OOP in 4.5, which made it difficult to use it. By contrast, 
OOP in 4.6 is usable - there are some issues (operators, polymorphic 
arrays, constructors, final) but they are not preventing its usage.

Unfortunately, having a different vtable will not produce compile- or 
link-time failures but will often cause that the wrong TB procedure is 
started (possibly with all kind of argument-value/presence issues) or 
might segfault immediately.

Thus, I believe it is rather difficult to run into the ABI issue in the 
real world (due to technical but also due to usage reasons), but it is 
possible. Hence, I wanted to point out that there is an ABI issue, but I 
also don't oppose the committal as it is difficult to encounter the 
issue - both in practise but also with an constructed example.

Tobias
Janus Weil Nov. 7, 2011, 12:49 p.m. UTC | #5
> Thus, I believe it is rather difficult to run into the ABI issue in the real
> world (due to technical but also due to usage reasons), but it is possible.
> Hence, I wanted to point out that there is an ABI issue, but I also don't
> oppose the committal as it is difficult to encounter the issue - both in
> practise but also with an constructed example.

Thanks for the feedback, everyone. Since we all seem to agree more or
less that the whole patch is ok for trunk, I'll commit tonight (if no
further protests appear).

Cheers,
Janus
Janus Weil Nov. 7, 2011, 6:44 p.m. UTC | #6
2011/11/7 Janus Weil <janus@gcc.gnu.org>:
>> Thus, I believe it is rather difficult to run into the ABI issue in the real
>> world (due to technical but also due to usage reasons), but it is possible.
>> Hence, I wanted to point out that there is an ABI issue, but I also don't
>> oppose the committal as it is difficult to encounter the issue - both in
>> practise but also with an constructed example.
>
> Thanks for the feedback, everyone. Since we all seem to agree more or
> less that the whole patch is ok for trunk, I'll commit tonight (if no
> further protests appear).

Committed as r181107.

Cheers,
Janus
diff mbox

Patch

Index: gcc/fortran/class.c
===================================================================
--- gcc/fortran/class.c	(revision 181043)
+++ gcc/fortran/class.c	(working copy)
@@ -288,6 +288,10 @@  static void
 add_proc_comp (gfc_symbol *vtype, const char *name, gfc_typebound_proc *tb)
 {
   gfc_component *c;
+
+  if (tb->non_overridable)
+    return;
+  
   c = gfc_find_component (vtype, name, true, true);
 
   if (c == NULL)
Index: gcc/fortran/resolve.c
===================================================================
--- gcc/fortran/resolve.c	(revision 181044)
+++ gcc/fortran/resolve.c	(working copy)
@@ -5868,11 +5868,13 @@  resolve_typebound_function (gfc_expr* e)
   const char *name;
   gfc_typespec ts;
   gfc_expr *expr;
+  bool overridable;
 
   st = e->symtree;
 
   /* Deal with typebound operators for CLASS objects.  */
   expr = e->value.compcall.base_object;
+  overridable = !e->value.compcall.tbp->non_overridable;
   if (expr && expr->ts.type == BT_CLASS && e->value.compcall.name)
     {
       /* Since the typebound operators are generic, we have to ensure
@@ -5923,22 +5925,26 @@  resolve_typebound_function (gfc_expr* e)
     return FAILURE;
   ts = e->ts;
 
-  /* Then convert the expression to a procedure pointer component call.  */
-  e->value.function.esym = NULL;
-  e->symtree = st;
+  if (overridable)
+    {
+      /* Convert the expression to a procedure pointer component call.  */
+      e->value.function.esym = NULL;
+      e->symtree = st;
 
-  if (new_ref)  
-    e->ref = new_ref;
+      if (new_ref)  
+	e->ref = new_ref;
 
-  /* '_vptr' points to the vtab, which contains the procedure pointers.  */
-  gfc_add_vptr_component (e);
-  gfc_add_component_ref (e, name);
+      /* '_vptr' points to the vtab, which contains the procedure pointers.  */
+      gfc_add_vptr_component (e);
+      gfc_add_component_ref (e, name);
 
-  /* Recover the typespec for the expression.  This is really only
-     necessary for generic procedures, where the additional call
-     to gfc_add_component_ref seems to throw the collection of the
-     correct typespec.  */
-  e->ts = ts;
+      /* Recover the typespec for the expression.  This is really only
+	necessary for generic procedures, where the additional call
+	to gfc_add_component_ref seems to throw the collection of the
+	correct typespec.  */
+      e->ts = ts;
+    }
+
   return SUCCESS;
 }
 
@@ -5957,11 +5963,13 @@  resolve_typebound_subroutine (gfc_code *code)
   const char *name;
   gfc_typespec ts;
   gfc_expr *expr;
+  bool overridable;
 
   st = code->expr1->symtree;
 
   /* Deal with typebound operators for CLASS objects.  */
   expr = code->expr1->value.compcall.base_object;
+  overridable = !code->expr1->value.compcall.tbp->non_overridable;
   if (expr && expr->ts.type == BT_CLASS && code->expr1->value.compcall.name)
     {
       /* Since the typebound operators are generic, we have to ensure
@@ -6006,22 +6014,26 @@  resolve_typebound_subroutine (gfc_code *code)
     return FAILURE;
   ts = code->expr1->ts;
 
-  /* Then convert the expression to a procedure pointer component call.  */
-  code->expr1->value.function.esym = NULL;
-  code->expr1->symtree = st;
+  if (overridable)
+    {
+      /* Convert the expression to a procedure pointer component call.  */
+      code->expr1->value.function.esym = NULL;
+      code->expr1->symtree = st;
 
-  if (new_ref)
-    code->expr1->ref = new_ref;
+      if (new_ref)
+	code->expr1->ref = new_ref;
 
-  /* '_vptr' points to the vtab, which contains the procedure pointers.  */
-  gfc_add_vptr_component (code->expr1);
-  gfc_add_component_ref (code->expr1, name);
+      /* '_vptr' points to the vtab, which contains the procedure pointers.  */
+      gfc_add_vptr_component (code->expr1);
+      gfc_add_component_ref (code->expr1, name);
 
-  /* Recover the typespec for the expression.  This is really only
-     necessary for generic procedures, where the additional call
-     to gfc_add_component_ref seems to throw the collection of the
-     correct typespec.  */
-  code->expr1->ts = ts;
+      /* Recover the typespec for the expression.  This is really only
+	necessary for generic procedures, where the additional call
+	to gfc_add_component_ref seems to throw the collection of the
+	correct typespec.  */
+      code->expr1->ts = ts;
+    }
+
   return SUCCESS;
 }