[sv-ec] class extension across hierarchical boundaries

From: Arturo Salz <Arturo.Salz_at_.....>
Date: Fri Oct 19 2007 - 01:08:38 PDT
In the last meeting I agreed to write down the problems associated with
using parameter types to extend classes across the instantiation
hierarchy. Then I realized that I had already described the problems in
a prior mailing to the committee as part of a related discussion. My
previous message can be found via the URL:

http://www.eda-stds.org/sv-ec/hm/2420.html

 

And the ensuing discussion is filed under:

http://www.eda-stds.org/sv-ec/hm/2423.html

http://www.eda-stds.org/sv-ec/hm/2429.html

 

The first issue I discussed in my previous message has now been settled
- in fact differently from the way in which I originally argued.
However, the second issue, which is the topic of this posting, still
applies. I repeat that part of my message below.

 

2) Extending classes across hierarchical scopes

 

We believe that class inheritance should only be allowed in of the
following situations:

 a) The base class and the extended class both reside in the same
hierarchical scope (program, interface, or module)

 b) The base class is declared in $root

 c) The base class is declared in a package

 

Any other relationship between the base class and the extended class
should be disallowed.

The reasons for restricting class inheritance in this manner is twofold:
Confusion regarding type equivalence, and implementation complexity and
performance.

 

Type confusion: Currently the LRM defines two identical types in
different scopes as non-equivalent types. This means that a class
declared in a module represents distinct types in different instances of
that module. In general, a class in a different module instance needs to
be a different type because a class may depend on module parameters
(making it distinct). But even if a class does not depend on parameters,
its behavior may indirectly depend on parameters --- by a method calling
some other task that is instantiated with different parameter settings
--- or by accessing different (instance specific) data. All these
non-local properties of a class make equivalence analysis difficult and
complex. Allowing classes to be extended across scopes introduces
additional non-local properties to each derived class. This, in turn,
leads to a situation in which the same base class is instantiated
multiple times (each in a different instance of the module) and where
each base class may have multiple identical-looking extended classes
that are nonetheless not type-equivalent with one another. Classes
extended across scopes would have a common ancestor (and hence they
would be of the same base type), but they can only be down-cast into
classes that reside in the same module instance in which the objects
were created. These restrictions can be very confusing and may obviate
any benefits of having shared base classes in different hierarchical
scopes.

 

The second problem, compiler complexity and performance, is due to the
existence of static (or instance specific) variables. These instance
specific variables may be created directly by static class properties,
or indirectly by having a class method access instance-specific
variables or call instance specific functions. The existence of these
static variables require implementations that allows classes to be
extended across hierarchical scopes to either generate different code
for all methods of each instance or else provide some form of "static
variable table" coupled with an extra level of indirection to reference
all static variables. The first option makes separate compilation very
difficult, and the second incurs a performance penalty. Consider the
following example. Module M contains a base class B, module N extends
class B into class C, and module P extends class C into class D. This
leads to the following functional (class) hierarchy:

    B -> C -> D

But in addition, each derived class is also subjected to a structural
hierarchy that depends on the instantiation of modules M, N, and P.
Consider the situation when for example, module M is instantiated 10
times, module N is instantiated 5 times inside module M, and module P is
instantiated 2 times inside module N. In this case there are 100
distinct D types resulting from the simple 2 class derivations. This
means that a compiler would have to generate 100 versions of each method
in D, 50 different versions of each method in C, and 10 different
versions of each method in B. Alternatively, each instantiated object
can maintain a "static variable table" containing "pointers" to the each
of their corresponding hierarchical scopes. This is similar to the
"virtual method table", but it differs in that it depends not only on
the declaration, but also on the instantiation path of all its base
classes.

 

An additional limitation that complicates this feature is the lack of
hierarchical type expressions. Thus, in order to extend a class across
hierarchical scopes, the extended classes must reside in nested modules
(an uncommon situation) , or the base classes must be passed as
parameters, which complicate separate compilation and debug,

 

Given all this complexity, we wonder what is the programming model that
requires classes, which provide functional hierarchy, to also include
structural hierarchy? No other object-oriented language such as C++ or
Java include structural hierarchy. Yet, these languages are perfectly
capable of handling the most demanding modeling problems with ease,
using only functional hierarchy.

 

If a rational, coherent, and well understood programming model and
methodology are developed then these restrictions could be lifted in a
future release. But at present, we feel that it is best to limit P1800
to the more restrictive, but well understood methodology.

 

The first issue raised, type confusion, is not a fundamental problem,
and Gord argued in his response that the same rules apply to classes
extended from packages. This is true, however, it does highlight the
issue that a class extended across the hierarchy needs to carry the
"instance information" as part of its type, something that may not be
immediately obvious.

 

The second problem, "complexity and performance", is the issue to which
I was referring in the meeting. One thing that my prior posting does not
mention is the associated problem of virtual methods that access
instance specific or class static data or functions. For example, If
such a virtual method is called from the context of the base class then
the called method needs o be able to resolve the static references to
the appropriate (extended) instance. This requires (as stated above)
either that different code be generated for each (otherwise identical)
hierarchical class, or that some sort of "static table" is associated
with the each object. This is not a different issue, but it does
highlight the issue which can result in either code-bloat or additional
memory (to store the instance information) and slower run-time code (die
to additional indirection).

 

I stand by my previous assertion that mixing structural and functional
hierarchies represents a new paradigm that does not exist in other
mainstream OO languages, hence, I find it difficult to understand the
motivation for explicitly introducing such a feature into SystemVerilog.
It is a good practice to restrict a problematic feature until the issues
associated with such a feature are better understood and hashed out. We
can always loosen a previous restriction without creating backward
compatibility issues or divergent implementations.

 

Finally, I'd like to emphasize that my message mentions that this
feature would be problematic for separate compilation. And now we are
debating some of the issues created when these "opaque types" represent
base classes that are passed as parameters and extended across the
hierarchy.

 

            Arturo

 


-- 
This message has been scanned for viruses and
dangerous content by MailScanner, and is
believed to be clean.
Received on Fri Oct 19 01:09:21 2007

This archive was generated by hypermail 2.1.8 : Fri Oct 19 2007 - 01:09:45 PDT