Rollback

From Msim

Jump to: navigation, search

A bug in the rollback code allows speculative instructions to alter simulator memory and not be corrected. Clashes between how quadwords are read caused additional problems. An overhaul, where we store data sizes has been implemented rather than grabbing entire quadword blocks and attempting to restore them.

Contents

Problem

Quadword accesses that are not aligned on quadword boundaries used repair information that was generated from a quadword boundary (and the repairs were made to a quadword boundary). This meant that a quadword access that crossed two aligned quadwords could not be recovered properly.

stq r0, 4(r31) - store quadword r0 to address 4

This is technically ok (alignment checks only cared about word boundaries).
The rollback code forced aligned and stored the following:

previous_mem = memory[4 & ~7] - stored the quadword at address 0

Then repaired the incorrect address on a rollback:

memory[4 & ~7] = previous_mem

In this case, the word at address 8 was not repaired.

Restoration Issues

Storage that was not aligned to quadword, would generate quadword restoration data that would be recovered to the non-aligned address. For example, we write a byte to address 1. We generate a recovery block from address 0-8. When we restore it, it is placed in addresses 1-9.

Changes

rob.h

A data_size variable is added to the ROB entries in order to retain the size of the data type that we need to recover.

sim-outorder.c

Rollback code is now as follows:

               //If this is a store, retain the old memory data so that we can rollback.
               //This must be done before the "switch" below.
               //We only need data sizes for stores, loads always replace the full register on rollback
               qword_t previous_mem(0);
               if((MD_OP_FLAGS(op) & (F_MEM|F_STORE)) == (F_MEM|F_STORE))
               {
                       enum md_fault_type prev_fault;
                       md_addr_t addr = (GPR(RB) + SEXT(OFS));
                       qword_t temp_qword;

                       switch(op)
                       {
                       case STB:
                               data_size = 1;
                               previous_mem = READ_BYTE(addr, prev_fault);
                               break;
                       case STW:
                               data_size = 2;
                               previous_mem = READ_HALF(addr, prev_fault);
                               break;
                       case STS:
                       case STL:
                       case STL_C:
                               data_size = 4;
                               previous_mem = READ_WORD(addr, prev_fault);
                               break;
                       case STQ_U:
                               addr &= ~7;
                       case STT:
                       case STQ:
                       case STQ_C:
                               data_size = 8;
                               previous_mem = READ_QWORD(addr, prev_fault);
                               break;
                       default:
                               fatal("Store without matching opcode!");
                       }
                       //Change this to remove the fault, we don't need to check for a fault
               }

Also, the data_size variable must be initialized:

               //Get information needed for rollback
               //Just copy it all, during rollback we can decide to use regs_R or regs_F.
               int Rlist[3] = {RA,RB,RC};
               qword_t regs_R[3] = {GPR(RA), GPR(RB), GPR(RC)};
               md_fpr_t regs_F[3] = {regs->regs_F[RA], regs->regs_F[RB], regs->regs_F[RC]};
               md_ctrl_t regs_C = regs->regs_C;
               size_t data_size = 0;

And be given the to LSQ entry:

                               lsq->previous_mem = previous_mem;
                               lsq->data_size = data_size;
                               lsq->is_store = ((MD_OP_FLAGS(op) & (F_MEM|F_STORE)) == (F_MEM|F_STORE));

cmp.c

The code where we perform the actual rollback is now:

                       //Here is where we perform memory rollback
                       if(target.LSQ[LSQ_index].is_store)
                       {
                               qword_t temp = MD_SWAPQ(target.LSQ[LSQ_index].previous_mem);
                               md_addr_t addr = target.LSQ[LSQ_index].addr;

                               switch(target.LSQ[LSQ_index].data_size)
                               {
                                       case 8:
                                               target.mem->mem_access(Write,addr,&temp,8);
                                               break;
                                       case 4:
                                               target.mem->mem_access(Write,addr,&temp,4);
                                               break;
                                       case 2:
                                               target.mem->mem_access(Write,addr,&temp,2);
                                               break;
                                       case 1:
                                               target.mem->mem_access(Write,addr,&temp,1);
                                               break;
                                       default:
                                               fatal("Bad data_size value!");
                               }
                       }


Fix - No Longer Relevant

The forced alignment only mattered in the case of quadwords.
Old code: sim-outorder.c

               //If this is a store, retain the old memory data so that we can rollback.
               //This must be done before the "switch" below.
               qword_t previous_mem(0);
               if((MD_OP_FLAGS(op) & (F_MEM|F_STORE)) == (F_MEM|F_STORE))
               {
                       enum md_fault_type prev_fault;
                       previous_mem = READ_QWORD((GPR(RB) + SEXT(OFS)) & (~7), prev_fault);
                       //Change this to remove the fault, we don't need to check for a fault
               }

New code:

               //If this is a store, retain the old memory data so that we can rollback.
               //This must be done before the "switch" below.
               qword_t previous_mem(0);
               if((MD_OP_FLAGS(op) & (F_MEM|F_STORE)) == (F_MEM|F_STORE))
               {
                       enum md_fault_type prev_fault;
                       md_addr_t addr = (GPR(RB) + SEXT(OFS));
 
                       if((op==LDQ_U) || (op==STQ_U))
                       {
                               addr &= ~7;
                       }
                       previous_mem = READ_QWORD(addr, prev_fault);
                       //Change this to remove the fault, we don't need to check for a fault
               }

Then, the fix in the rollback code itself:
Old code: cmp.c

                       //Here is where we perform memory rollback
                       if(target.LSQ[LSQ_index].is_store)
                       {
                               qword_t temp = MD_SWAPQ(target.LSQ[LSQ_index].previous_mem);
                               mem_access(target.mem,Write,target.LSQ[LSQ_index].addr & ~7,&temp,sizeof(qword_t));
                       }

New code:

                       //Here is where we perform memory rollback
                       if(target.LSQ[LSQ_index].is_store)
                       {
                               qword_t temp = MD_SWAPQ(target.LSQ[LSQ_index].previous_mem);
                               md_addr_t addr = target.LSQ[LSQ_index].addr;
                               if((target.LSQ[LSQ_index].op == LDQ_U) || (target.LSQ[LSQ_index].op == STQ_U))
                               {
                                       addr &= ~7;
                               }
                               mem_access(target.mem,Write,addr,&temp,sizeof(qword_t));
                       }
Personal tools