[sv-ec] Issue 240

From: Arturo Salz <Arturo.Salz_at_.....>
Date: Fri Apr 15 2005 - 02:06:46 PDT
I was asked to describe what I understand to be the limitations regarding extending classes across hierarchical scopes, and in particular extending classes across the module/program boundaries. These are related to issue 240.

I'd like to divide the description into two distinct threads. One, the issue of extending classes across program/module
boundaries. And two, the more general issue of extending classes across hierarchical scopes.

1) Extending classes across program/module boundaries

As currently defined, SystemVerilog uses the syntactical scope that encloses a class declaration to determine the execution semantics of its methods (the same holds for regular tasks and functions). Thus, classes declared within a program are subject to testbench (Reactive region) scheduling and classes declared within a module are subject to design (Active region) scheduling. This organization leads to a clear separation of concerns; procedural code declared within a program context exhibits program execution and procedural code declared within a module context exhibits classical Verilog execution. A consequence of this organization is that identical code may behave differently depending on the syntactical context in which it is declared. Therefore, a class declared in a module context is inherently a different type than the same class declared in a program context. If a class declared in a module were allowed to be extended in a program then different parts of the same (extended) object would exhibit different semantics. This situation is  further complicated when virtual tasks in one context are overridden by tasks in another context. Therefore, allowing such extensions will create needless races among the various execution regions, and invalidate the primary reason for  the creation of the program context and the Reactive region.

We recognize that multiple scopes may need to share or even extend the same class, which would be difficult without violating the separation of concerns outlined above. For that reason the committee adopted the "anonymous program" construct. The anonymous program was created precisely to allow users to create a syntactical scope around a set of procedural declarations (classes, tasks, and functions) in either a package or in $root. The anonymous program does not declare a hierarchical scope, only a syntactical context that modifies the scheduling semantics of the code declared within it. This, in turn, allows base classes with program semantics to be extended in other programs (but not in modules).

Some people have expressed dislike for the anonymous program syntax and would have preferred a construct that does not resemble the program (which does accept a name and creates a hierarchical scope). Such a construct was not proposed to avoid creating an additional keyword for a very similar semantical construct. Other people have expressed the desire to use pragmas (such 'Reactive), but that is strictly a syntactical/lexical discussion, not a desire to loosen the semantical restrictions that disallow extending classes across the module/program boundary.

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.

    Arturo
Received on Fri Apr 15 02:07:21 2005

This archive was generated by hypermail 2.1.8 : Fri Apr 15 2005 - 02:09:10 PDT