[gnu.g++] address of virtual function

tiemann@YAHI.STANFORD.EDU (Michael Tiemann) (05/10/89)

   So, for example, right now, with my combination of cfront and
   back end compiler, Jerry's approach, where you violate the encapsulation
   of an object, access its instance variables directly, and lock
   future derived classes into using one approach to returning
   "size", and force a syntax of p->size rather than p->size() --
   then generates code like such:
   
	   movel	a2@,sp@-
   
   Where as if one takes the "Object Oriented" approach of using
   virtual functions to return these values, so that some future
   class -- a linked list maybe -- can have a virtual function
   that calculates "p->size()" on the fly, then the [minimum]
   code that gets generated is:
   
	   movel	a3@(4),a0
	   addqw	#8,a0
	   addw	a0@,a3
	   movel	a3,sp@-
	   movel	a0@(4),a0
	   jbsr	a0@
	    |
	    |
	    V
	   link	a6,#0
	   movel	a6@(8),a0
	   movel	a0@,d0
	   unlk	a6
	   rts
	    |
	    |
	    V
	   movel	d0,sp@-
      
For crying out loud!  If you are so concerned about performance that
you would be willing to rape your code if it saved you a few cycles,
how about this radical approach: write in the object oriented style,
AND GET A REAL MACHINE.  On a RISC machine, such as the Sparc, your
code example compiles into this:

	ld	[%i0+4],%o0	// coax vtable out of `this'
				// with GNU C++, this may
				// already be in a register
	ld	[%o0+8],%o1	// get address of virtual function
   |	call	%o1		// start call to function
   |	mov	%i0,%o0		// put `this' in first parameter
   V |	ret			// trivial function, start return
     |	ld	[%o0+8],%o0
     V	mov	%o0,%o3		// `push' parameter

Note that this sequence of instructions takes about as long on a
SparcStation1 as this one:

	   movel	a2@,sp@-

on a 68020 machine costing about the same money, yet it came from
using code "written in the object oriented style".

Let's think about the future before we write a bunch of code which
will only deserve the name "dusty deck" when it finally makes it to
market.  RISC machines are now cheap.  Let's take advantage of their
power and scalability to write code which will last longer (or at
least hold up better) than our predecessors' did.  Reusable code isn't
reusable if it's not usable...

Michael

grunwald@flute.cs.uiuc.edu (05/10/89)

furthermore, remember that other optimizations are allowed, depending on
the knowledge of the type.

E.g., if you are using an instance of the function, and not a pointer
to the function, you can inline the virtual.

There are some possible language extensions that would allow you to do
load/stores a little cheaper. Examples can be found by looking at CLU.

Not using their syntax, the basic idea would be to automatically
declare functions for variables. Given this, and knowledge about the
size of the object and the semantics, you could generate simple
loads & stores. You'd need to introduce a keyword, e.g., ``mutable''
and/or ``immutable'' to distinguish read members from read/write members.

e.g.,
class Foo {
  immutable int frob;
  mutable double bar;
};
Foo foo;

is a class with 2 data variables & two functions: frob is read only, bar
is writable. Users could specify ``foo -> bar(10)'' or whatever syntax
you desire.

If I subclassd Foo, and hide the definition of variable `bar' with another
variable `bar', I'd still be able to generate the code via:

	load foo,a0
	add  #4,a0
	load @a0,d0

(optimize it for you machine).

But what does all this buy you? How often do you need to return data values
without additional work? If you don't have additioanl work to do, why are
they virtual data items?

I think I'm missing the range of applicability of the feature, although
it's simple to add.
--
Dirk Grunwald
Univ. of Illinois
grunwald@flute.cs.uiuc.edu