Garbage Collection

Proposal Editing Information

  • Who Updates: JimLewis, CliffordWalinsky, <Add -- DavidKoontz - 2014-09-25>, ...
  • Date Proposed: 2013-04-11
  • Date Last Updated: 2014-09-24
  • Priority:
  • Complexity:
  • Focus: Testbench

Requirement Summary

The VHDL standard mandates that dynamically allocated objects, created by calling access-type allocators, shall have their storage reclaimed by explicitly calling deallocators. Experience has shown that the presence of concurrent processing in VHDL makes the task of determining when access values can be deallocated quite burdensome and error-prone. We therefore propose that Section 5.4.3 "Allocation and deallocation of objects" be expanded to state that allocated objects will be reclaimed automatically when there are no longer references to them. There is some debate about whether to deprecate the deallocate procedure, since it would be unnecessary, given automatic reclamation of access values. If the deallocate procedure is deprecated, there is the possibility that a large number of existing designs will no longer compile.

It is the responsibility of the simulator to determine when access values can be reclaimed. There are two prevalent schemes: 1) Marking all accessible objects from variables and signals in the design, and then reclaiming those that are not accessible; and 2) Keeping reference counts on all access values, reclaiming those whose counters reach zero. The mark-sweep strategy is viewed as the most desirable, especially when circular references can be created within the language.

Related and/or Competing Issues: None

Use Model:

The following example contains a useful function definition that relies on an internally declared access value. As written, however, the function does not reclaim the access value, and therefore leaks allocated memory. Determining that this function has a memory leak is often quite difficult in most simulators. Automatic garbage collection will remove the requirement to rewrite the function in order to insert a call to deallocate.

Example 
  function to_string (
    value      : in integer_vector
  ) return string is
    variable L : line; 
  begin
    write(L, value) ;    
    return  L.all ;
  end function to_string ; 

Questions

-- DavidKoontz - 2014-09-25

Is you're example valid? Variable L is dynamically elaborated - 14.6 Dynamic elaboration, b. Execution of a subprogram call ... "Next ...", "...Finally, if the designator of the subprogram is not decorated with the 'FOREIGN attribute defined in package STANDARD, the declarative part of the corresponding subprogram body is elaborated and the sequence of statements in the subprogram body is executed". and the following NOTE:

"NOTE 1—It is a consequence of these rules that declarative items appearing within the declarative part of a subprogram body are elaborated each time the corresponding subprogram is called; thus, successive elaborations of a given declarative item appearing in such a place may create items with different characteristics. For example, successive elaborations of the same subtype declaration appearing in a subprogram body may create subtypes with different constraints."

The Line declaration's elaboration occurs as the final step of dynamic elaboration of a function call and should be on the stack. You shouldn't be able to declare a persistant object here. Return values are immediate, used in chained expressions they will be copied by the next function call/operator, used in assignment they persist until the current statement is done by virtue of no further function call dynamic elaboration being possible until the next statement.

Unless DEALLOCATE is explicitly called within the subprogram, it's a case of a tree falling in the forest with no one to hear.

5.4.3 Allocation and deallocation of objects "An object designated by an access value is allocated by an allocator for that type. An allocator is a primary of an expression; allocators are described in 9.3.7. For each access type, a deallocation operation is implicitly declared immediately following the full type declaration for the type. This deallocation operation makes it possible to deallocate explicitly the storage occupied by a designated object."

This is a case of not needing to explicitly deallocate the storage occupied by the designated object, "makes it possible" doesn't imply shall, here.

9.3.7 Allocators, "In the absence of explicit deallocation, an implementation shall guarantee that any object created by the evaluation of an allocator remains allocated for as long as this object or one of its subelements is accessible directly or indirectly; that is, as long as it can be denoted by some name. "

And this is exactly the case for your example, declared as a subprogram declaritive item. After the Return L can no longer be used to access the string.

Also see 4.2 Subprogram declarations, 4.2.1 General: "It is an error if the result subtype of a function denotes either a file type or a protected type. Moreover, it is an error if the result subtype of a pure function denotes an access type or a subtype that has a subelement of an access type."

And the reason for the restriction on the access type or subtype is because those declarations are dynamic.

-- DavidKoontz - 2014-12-17

Jim Lewis mentions another case for OSVVM™ 2014.07a: Protected Types, Initialized Pointers, and Memory Leaks.

The final ones I found are more subtle. They result from a collision of creating a coverage model within a temporary object and initializing pointers within a protected type. I took to initializing pointers so I could avoid having to do NULL checks. For example, in NamePkg, I did:

variable NamePtr : line := new string'("") ;

And then when I did a “Get” on the string value, I can skip the NULL check on NamePtr:

impure function Get return string is
begin
  return NamePtr.all ;
end function Get ;

Cool, but that means that the protected type needs a deallocate/destructor that only runs if you are never going to call a method on the protected type object again – such as a the end of a subprogram that declares an object of the protected type. Without something that happens automatically as the object is being destroyed, that is a hard use mode to remember. I instead added a check to return “” on NULL to Get. This issue is in revisions 2014.07 and 2014.01.

The above quoted paragraph in LRM subclause 9.3.7 holds true in this case too. It's due to the nature of where these allocated objects are created which also points out this is an implementation issue. And that may be worth repeating.

An implementation that is not dynamically allocating and automatically retiring stack objects or their equivalent may not be suitable for purpose and shouldn't require a directive in the standard to require it to be. It's an implementation detail.

-- JimLewis - 2014-12-17 While 9.3.7 Allocator may permit garbage, it certainly does not require it. The LRM needs to require garbage collection so that the code we write can depend on garbage collection being done and if not, we can report it to a vendor as a bug - an expect it to get fixed.

If your thinking is that since the standard allows garbage collection, then we don't need to require it, consider that the standard also allows any integer representation to be anything larger than 32 bits, and while a bignum integer representation would be a valid implementation, a model cannot expect integers to be implemented as bignums.

-- DavidKoontz - 2015-02-24

VHDL has limited elements of OOP (abstraction, encapsulation, polymorphisim, concurrency, inheritance) in large part due to protected types. It turns out VHDL also presently share's Ada's perspective on Garbage Collection ( The Ada Programming Language, Object-Oriented Programming (OOP), "Ada 95 does not require automatic garbage collection but rather supplies definitional features allowing the developer to supply type-specific storage reclamation operations (“finalization”)", finalization like deallocate.

There's an interesting question, just where in the VHDL standard would you supply a requirement for garbage collection, which should be an implementation detail. The near obsfucation in 9.7.3 (and 14.6) avoiding addressing implementation should give pause.

You could note your OSVVM takes advantage of finalization:

variable NamePtr  : line ;
    ------------------------------------------------------------
    procedure Set (NameIn : String) is
    ------------------------------------------------------------
    begin
      deallocate(NamePtr) ;
      NamePtr := new string'(NameIn) ;
    end procedure Set ;

Which also brings up the idea of simply calling garbage collection a basic operation, just like an allocator.

General Comments

Supporters

Add your signature here to indicate your support for the proposal

-- CliffordWalinsky

-- JimLewis

-- MortenZilmer - 2015-01-21

Topic revision: r9 - 2020-02-17 - 15:34:54 - JimLewis
 
Copyright © 2008-2024 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback