bpred.c

来自「一个很有名的硬件模拟器。可以模拟CPU」· C语言 代码 · 共 986 行 · 第 1/2 页

C
986
字号
{  unsigned char *p = NULL;  /* Except for jumps, get a pointer to direction-prediction bits */  switch (pred_dir->class) {    case BPred2Level:      {	int l1index, l2index;        /* traverse 2-level tables */        l1index = (baddr >> MD_BR_SHIFT) & (pred_dir->config.two.l1size - 1);        l2index = pred_dir->config.two.shiftregs[l1index];        if (pred_dir->config.two.xor)	  {#if 1	    /* this L2 index computation is more "compatible" to McFarling's	       verison of it, i.e., if the PC xor address component is only	       part of the index, take the lower order address bits for the	       other part of the index, rather than the higher order ones */	    l2index = (((l2index ^ (baddr >> MD_BR_SHIFT))			& ((1 << pred_dir->config.two.shift_width) - 1))		       | ((baddr >> MD_BR_SHIFT)			  << pred_dir->config.two.shift_width));#else	    l2index = l2index ^ (baddr >> MD_BR_SHIFT);#endif	  }	else	  {	    l2index =	      l2index		| ((baddr >> MD_BR_SHIFT) << pred_dir->config.two.shift_width);	  }        l2index = l2index & (pred_dir->config.two.l2size - 1);        /* get a pointer to prediction state information */        p = &pred_dir->config.two.l2table[l2index];      }      break;    case BPred2bit:      p = &pred_dir->config.bimod.table[BIMOD_HASH(pred_dir, baddr)];      break;    case BPredTaken:    case BPredNotTaken:      break;    default:      panic("bogus branch direction predictor class");    }  return (char *)p;}/* probe a predictor for a next fetch address, the predictor is probed   with branch address BADDR, the branch target is BTARGET (used for   static predictors), and OP is the instruction opcode (used to simulate   predecode bits; a pointer to the predictor state entry (or null for jumps)   is returned in *DIR_UPDATE_PTR (used for updating predictor state),   and the non-speculative top-of-stack is returned in stack_recover_idx    (used for recovering ret-addr stack after mis-predict).  */md_addr_t				/* predicted branch target addr */bpred_lookup(struct bpred_t *pred,	/* branch predictor instance */	     md_addr_t baddr,		/* branch address */	     md_addr_t btarget,		/* branch target if taken */	     enum md_opcode op,		/* opcode of instruction */	     int is_call,		/* non-zero if inst is fn call */	     int is_return,		/* non-zero if inst is fn return */	     struct bpred_update_t *dir_update_ptr, /* pred state pointer */	     int *stack_recover_idx)	/* Non-speculative top-of-stack;					 * used on mispredict recovery */{  struct bpred_btb_ent_t *pbtb = NULL;  int index, i;  if (!dir_update_ptr)    panic("no bpred update record");  /* if this is not a branch, return not-taken */  if (!(MD_OP_FLAGS(op) & F_CTRL))    return 0;  pred->lookups++;  dir_update_ptr->dir.ras = FALSE;  dir_update_ptr->pdir1 = NULL;  dir_update_ptr->pdir2 = NULL;  dir_update_ptr->pmeta = NULL;  /* Except for jumps, get a pointer to direction-prediction bits */  switch (pred->class) {    case BPredComb:      if ((MD_OP_FLAGS(op) & (F_CTRL|F_UNCOND)) != (F_CTRL|F_UNCOND))	{	  char *bimod, *twolev, *meta;	  bimod = bpred_dir_lookup (pred->dirpred.bimod, baddr);	  twolev = bpred_dir_lookup (pred->dirpred.twolev, baddr);	  meta = bpred_dir_lookup (pred->dirpred.meta, baddr);	  dir_update_ptr->pmeta = meta;	  dir_update_ptr->dir.meta  = (*meta >= 2);	  dir_update_ptr->dir.bimod = (*bimod >= 2);	  dir_update_ptr->dir.twolev  = (*twolev >= 2);	  if (*meta >= 2)	    {	      dir_update_ptr->pdir1 = twolev;	      dir_update_ptr->pdir2 = bimod;	    }	  else	    {	      dir_update_ptr->pdir1 = bimod;	      dir_update_ptr->pdir2 = twolev;	    }	}      break;    case BPred2Level:      if ((MD_OP_FLAGS(op) & (F_CTRL|F_UNCOND)) != (F_CTRL|F_UNCOND))	{	  dir_update_ptr->pdir1 =	    bpred_dir_lookup (pred->dirpred.twolev, baddr);	}      break;    case BPred2bit:      if ((MD_OP_FLAGS(op) & (F_CTRL|F_UNCOND)) != (F_CTRL|F_UNCOND))	{	  dir_update_ptr->pdir1 =	    bpred_dir_lookup (pred->dirpred.bimod, baddr);	}      break;    case BPredTaken:      return btarget;    case BPredNotTaken:      if ((MD_OP_FLAGS(op) & (F_CTRL|F_UNCOND)) != (F_CTRL|F_UNCOND))	{	  return baddr + sizeof(md_inst_t);	}      else	{	  return btarget;	}    default:      panic("bogus predictor class");  }  /*   * We have a stateful predictor, and have gotten a pointer into the   * direction predictor (except for jumps, for which the ptr is null)   */  /* record pre-pop TOS; if this branch is executed speculatively   * and is squashed, we'll restore the TOS and hope the data   * wasn't corrupted in the meantime. */  if (pred->retstack.size)    *stack_recover_idx = pred->retstack.tos;  else    *stack_recover_idx = 0;  /* if this is a return, pop return-address stack */  if (is_return && pred->retstack.size)    {      md_addr_t target = pred->retstack.stack[pred->retstack.tos].target;      pred->retstack.tos = (pred->retstack.tos + pred->retstack.size - 1)	                   % pred->retstack.size;      pred->retstack_pops++;      dir_update_ptr->dir.ras = TRUE; /* using RAS here */      return target;    }#ifndef RAS_BUG_COMPATIBLE  /* if function call, push return-address onto return-address stack */  if (is_call && pred->retstack.size)    {      pred->retstack.tos = (pred->retstack.tos + 1)% pred->retstack.size;      pred->retstack.stack[pred->retstack.tos].target = 	baddr + sizeof(md_inst_t);      pred->retstack_pushes++;    }#endif /* !RAS_BUG_COMPATIBLE */    /* not a return. Get a pointer into the BTB */  index = (baddr >> MD_BR_SHIFT) & (pred->btb.sets - 1);  if (pred->btb.assoc > 1)    {      index *= pred->btb.assoc;      /* Now we know the set; look for a PC match */      for (i = index; i < (index+pred->btb.assoc) ; i++)	if (pred->btb.btb_data[i].addr == baddr)	  {	    /* match */	    pbtb = &pred->btb.btb_data[i];	    break;	  }    }	  else    {      pbtb = &pred->btb.btb_data[index];      if (pbtb->addr != baddr)	pbtb = NULL;    }  /*   * We now also have a pointer into the BTB for a hit, or NULL otherwise   */  /* if this is a jump, ignore predicted direction; we know it's taken. */  if ((MD_OP_FLAGS(op) & (F_CTRL|F_UNCOND)) == (F_CTRL|F_UNCOND))    {      return (pbtb ? pbtb->target : 1);    }  /* otherwise we have a conditional branch */  if (pbtb == NULL)    {      /* BTB miss -- just return a predicted direction */      return ((*(dir_update_ptr->pdir1) >= 2)	      ? /* taken */ 1	      : /* not taken */ 0);    }  else    {      /* BTB hit, so return target if it's a predicted-taken branch */      return ((*(dir_update_ptr->pdir1) >= 2)	      ? /* taken */ pbtb->target	      : /* not taken */ 0);    }}/* Speculative execution can corrupt the ret-addr stack.  So for each * lookup we return the top-of-stack (TOS) at that point; a mispredicted * branch, as part of its recovery, restores the TOS using this value -- * hopefully this uncorrupts the stack. */voidbpred_recover(struct bpred_t *pred,	/* branch predictor instance */	      md_addr_t baddr,		/* branch address */	      int stack_recover_idx)	/* Non-speculative top-of-stack;					 * used on mispredict recovery */{  if (pred == NULL)    return;  pred->retstack.tos = stack_recover_idx;}/* update the branch predictor, only useful for stateful predictors; updates   entry for instruction type OP at address BADDR.  BTB only gets updated   for branches which are taken.  Inst was determined to jump to   address BTARGET and was taken if TAKEN is non-zero.  Predictor    statistics are updated with result of prediction, indicated by CORRECT and    PRED_TAKEN, predictor state to be updated is indicated by *DIR_UPDATE_PTR    (may be NULL for jumps, which shouldn't modify state bits).  Note if   bpred_update is done speculatively, branch-prediction may get polluted. */voidbpred_update(struct bpred_t *pred,	/* branch predictor instance */	     md_addr_t baddr,		/* branch address */	     md_addr_t btarget,		/* resolved branch target */	     int taken,			/* non-zero if branch was taken */	     int pred_taken,		/* non-zero if branch was pred taken */	     int correct,		/* was earlier addr prediction ok? */	     enum md_opcode op,		/* opcode of instruction */	     struct bpred_update_t *dir_update_ptr)/* pred state pointer */{  struct bpred_btb_ent_t *pbtb = NULL;  struct bpred_btb_ent_t *lruhead = NULL, *lruitem = NULL;  int index, i;  /* don't change bpred state for non-branch instructions or if this   * is a stateless predictor*/  if (!(MD_OP_FLAGS(op) & F_CTRL))    return;  /* Have a branch here */  if (correct)    pred->addr_hits++;  if (!!pred_taken == !!taken)    pred->dir_hits++;  else    pred->misses++;  if (dir_update_ptr->dir.ras)    {      pred->used_ras++;      if (correct)	pred->ras_hits++;    }  else if ((MD_OP_FLAGS(op) & (F_CTRL|F_COND)) == (F_CTRL|F_COND))    {      if (dir_update_ptr->dir.meta)	pred->used_2lev++;      else	pred->used_bimod++;    }  /* keep stats about JR's; also, but don't change any bpred state for JR's   * which are returns unless there's no retstack */  if (MD_IS_INDIR(op))    {      pred->jr_seen++;      if (correct)	pred->jr_hits++;            if (!dir_update_ptr->dir.ras)	{	  pred->jr_non_ras_seen++;	  if (correct)	    pred->jr_non_ras_hits++;	}      else	{	  /* return that used the ret-addr stack; no further work to do */	  return;	}    }  /* Can exit now if this is a stateless predictor */  if (pred->class == BPredNotTaken || pred->class == BPredTaken)    return;  /*    * Now we know the branch didn't use the ret-addr stack, and that this   * is a stateful predictor    */#ifdef RAS_BUG_COMPATIBLE  /* if function call, push return-address onto return-address stack */  if (MD_IS_CALL(op) && pred->retstack.size)    {      pred->retstack.tos = (pred->retstack.tos + 1)% pred->retstack.size;      pred->retstack.stack[pred->retstack.tos].target = 	baddr + sizeof(md_inst_t);      pred->retstack_pushes++;    }#endif /* RAS_BUG_COMPATIBLE */  /* update L1 table if appropriate */  /* L1 table is updated unconditionally for combining predictor too */  if ((MD_OP_FLAGS(op) & (F_CTRL|F_UNCOND)) != (F_CTRL|F_UNCOND) &&      (pred->class == BPred2Level || pred->class == BPredComb))    {      int l1index, shift_reg;            /* also update appropriate L1 history register */      l1index =	(baddr >> MD_BR_SHIFT) & (pred->dirpred.twolev->config.two.l1size - 1);      shift_reg =	(pred->dirpred.twolev->config.two.shiftregs[l1index] << 1) | (!!taken);      pred->dirpred.twolev->config.two.shiftregs[l1index] =	shift_reg & ((1 << pred->dirpred.twolev->config.two.shift_width) - 1);    }  /* find BTB entry if it's a taken branch (don't allocate for non-taken) */  if (taken)    {      index = (baddr >> MD_BR_SHIFT) & (pred->btb.sets - 1);            if (pred->btb.assoc > 1)	{	  index *= pred->btb.assoc;	  	  /* Now we know the set; look for a PC match; also identify	   * MRU and LRU items */	  for (i = index; i < (index+pred->btb.assoc) ; i++)	    {	      if (pred->btb.btb_data[i].addr == baddr)		{		  /* match */		  assert(!pbtb);		  pbtb = &pred->btb.btb_data[i];		}	      	      dassert(pred->btb.btb_data[i].prev 		      != pred->btb.btb_data[i].next);	      if (pred->btb.btb_data[i].prev == NULL)		{		  /* this is the head of the lru list, ie current MRU item */		  dassert(lruhead == NULL);		  lruhead = &pred->btb.btb_data[i];		}	      if (pred->btb.btb_data[i].next == NULL)		{		  /* this is the tail of the lru list, ie the LRU item */		  dassert(lruitem == NULL);		  lruitem = &pred->btb.btb_data[i];		}	    }	  dassert(lruhead && lruitem);	  	  if (!pbtb)	    /* missed in BTB; choose the LRU item in this set as the victim */	    pbtb = lruitem;		  /* else hit, and pbtb points to matching BTB entry */	  	  /* Update LRU state: selected item, whether selected because it	   * matched or because it was LRU and selected as a victim, becomes 	   * MRU */	  if (pbtb != lruhead)	    {	      /* this splices out the matched entry... */	      if (pbtb->prev)		pbtb->prev->next = pbtb->next;	      if (pbtb->next)		pbtb->next->prev = pbtb->prev;	      /* ...and this puts the matched entry at the head of the list */	      pbtb->next = lruhead;	      pbtb->prev = NULL;	      lruhead->prev = pbtb;	      dassert(pbtb->prev || pbtb->next);	      dassert(pbtb->prev != pbtb->next);	    }	  /* else pbtb is already MRU item; do nothing */	}      else	pbtb = &pred->btb.btb_data[index];    }        /*    * Now 'p' is a possibly null pointer into the direction prediction table,    * and 'pbtb' is a possibly null pointer into the BTB (either to a    * matched-on entry or a victim which was LRU in its set)   */  /* update state (but not for jumps) */  if (dir_update_ptr->pdir1)    {      if (taken)	{	  if (*dir_update_ptr->pdir1 < 3)	    ++*dir_update_ptr->pdir1;	}      else	{ /* not taken */	  if (*dir_update_ptr->pdir1 > 0)	    --*dir_update_ptr->pdir1;	}    }  /* combining predictor also updates second predictor and meta predictor */  /* second direction predictor */  if (dir_update_ptr->pdir2)    {      if (taken)	{	  if (*dir_update_ptr->pdir2 < 3)	    ++*dir_update_ptr->pdir2;	}      else	{ /* not taken */	  if (*dir_update_ptr->pdir2 > 0)	    --*dir_update_ptr->pdir2;	}    }  /* meta predictor */  if (dir_update_ptr->pmeta)    {      if (dir_update_ptr->dir.bimod != dir_update_ptr->dir.twolev)	{	  /* we only update meta predictor if directions were different */	  if (dir_update_ptr->dir.twolev == (unsigned int)taken)	    {	      /* 2-level predictor was correct */	      if (*dir_update_ptr->pmeta < 3)		++*dir_update_ptr->pmeta;	    }	  else	    {	      /* bimodal predictor was correct */	      if (*dir_update_ptr->pmeta > 0)		--*dir_update_ptr->pmeta;	    }	}    }  /* update BTB (but only for taken branches) */  if (pbtb)    {      /* update current information */      dassert(taken);      if (pbtb->addr == baddr)	{	  if (!correct)	    pbtb->target = btarget;	}      else	{	  /* enter a new branch in the table */	  pbtb->addr = baddr;	  pbtb->op = op;	  pbtb->target = btarget;	}    }}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?