This ticket will track the implementation of Hardware Transactional Memory in gem5 (HTM) with the Ruby memory system.
HTM (see overview) is an architectural feature that enables speculative concurrency in a shared-memory system; groups of instructions known as transactions are executed as an atomic unit. The system allows that transactions be executed concurrently but intervenes if a transaction's atomicity/isolation is jeopardized and takes corrective action.
Currently known commercial ISA extensions which are making use of HTM are
TSX for x86
TME for Arm
In this implementation, corrective active explicitly means rolling back a thread's architectural state and reverting any memory updates to a point just before the transaction began. This relies upon:
A checkpointing mechanism for architectural register state.
Buffering speculative memory updates.
The checkpointing mechanism is architecture dependent. Each ISA leveraging HTM support can define a class HTMCheckpoint inheriting from the generic one (GenericISA::HTMCheckpoint). Those will need to save/restore the architectural state by overriding the virtual HTMCheckpoint::save (when starting a transaction) and HTMCheckpoint::restore (when aborting a transaction). Instances of this class live in O3's ThreadState and Atomic's SimpleThread. It is up to the ISA to populate this instance when executing an instruction that begins a new transaction.
2) Buffering speculative memory updates
The core notifies the L0 cache controller that a new transaction has started and the controller in turn places itself in transactional state (htmTransactionalState := true). When operating in transactional state, the usual MESI protocol changes slightly. Lines loaded or stored are marked as part of a transaction's read and write set respectively. If there is an invalidation request to cache line in the read/write set, the transaction is marked as failed.
Similarly, if there is a read request by another core to a speculatively written cache line, i.e. in the write set, the transaction is marked as failed. If failed, all subsequent loads and stores from the core are made benign, i.e. made into NOPS at the cache controller, and responses are marked to indicate that the transactional state has failed. When the core receives these marked responses, it generates a HtmFailureFault with the reason for the transaction failure.
Servicing this fault does two things:
Restores the architectural checkpoint
Sends an HTM abort signal to the cache controller
The restoration includes all registers in the checkpoint as well as the program counter of the instruction before the transaction started. The abort signal is sent to the L0 cache controller and resets the failed transactional state. It resets the transactional read and write sets and invalidates any speculatively written cache lines. It also exits the transactional state so that the MESI protocol operates as usual. Alternatively, if the instructions within a transaction complete without triggering a HtmFailureFault, the transaction can be committed. The core is responsible for notifying the cache controller that the transaction is complete, and the cache controller makes all speculative writes visible to the rest of the system and exits the transactional state. Notifying the cache controller is done through HtmCmd Requests which are a subtype of Load Requests.
The buffering is implemented for the MESI_Three_Level Ruby protocol only
The implementation will be based on an old review request by Pradip Vallathol who developed HTM and TSX support in Gem5 as part of his master’s thesis at Wisconsin university:
EDIT: It seems like it is not possible to investigate the content of the original pull request in reviews.gem5.org.
Here’s the patch file we downloaded before changes in the gem5 website: