📄 bpred.c
字号:
stat_reg_counter(sdb, buf, "total number of address popped off of ret-addr stack", &pred->retstack_pops, 0, NULL);}void bpred_after_priming(struct bpred *bpred){ if (bpred == NULL) return; bpred->lookups = 0; bpred->addr_hits = 0; bpred->dir_hits = 0; bpred->used_bimod = 0; bpred->used_2lev = 0; bpred->jr_hits = 0; bpred->jr_seen = 0; bpred->misses = 0; bpred->retstack_pops = 0; bpred->retstack_pushes = 0;}#define BIMOD_HASH(PRED, ADDR) \ ((((ADDR) >> 19) ^ ((ADDR) >> 3)) & \ ((PRED)->config.bimod.size-1)) /* was: ((baddr >> 16) ^ baddr) & (pred->dirpred.bimod.size-1) *//* predicts a branch direction */char * /* pointer to counter */bpred_dir_lookup ( struct bpred_dir *pred_dir, /* branch direction predictor instance */ SS_ADDR_TYPE baddr) /* branch address */{ 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 >> 3) & (pred_dir->config.two.l1size - 1); l2index = pred_dir->config.two.shiftregs[l1index]; if (pred_dir->config.two.xor) { l2index = l2index ^ (baddr>>3); } else { l2index = l2index | ((baddr>>3) << 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 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). */SS_ADDR_TYPE /* predicted branch target addr */bpred_lookup(struct bpred *pred, /* branch predictor instance */ SS_ADDR_TYPE baddr, /* branch address */ SS_ADDR_TYPE btarget, /* branch target if taken */ enum ss_opcode op, /* opcode of instruction */ int r31p, /* is this using r31? */ struct bpred_update *dir_update_ptr,/* predictor state pointer */ int *stack_recover_idx) /* Non-speculative top-of-stack; * used on mispredict recovery */{ struct bpred_btb_ent *pbtb = NULL; int index, i; if (!dir_update_ptr) panic("no bpred update record"); /* if this is not a branch, return not-taken */ if (!(SS_OP_FLAGS(op) & F_CTRL)) return 0; pred->lookups++; 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 ((SS_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 ((SS_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 ((SS_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 ((SS_OP_FLAGS(op) & (F_CTRL|F_UNCOND)) != (F_CTRL|F_UNCOND)) { return baddr + sizeof(SS_INST_TYPE); } 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 (op == JR && r31p && pred->retstack.size) { SS_ADDR_TYPE target = pred->retstack.stack[pred->retstack.tos].target; pred->retstack.tos = (pred->retstack.tos + pred->retstack.size - 1) % pred->retstack.size; pred->retstack_pops++; return target; } /* not a return. Get a pointer into the BTB */ index = (baddr >> 3) & (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 ((SS_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 *pred, /* branch predictor instance */ SS_ADDR_TYPE 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 *pred, /* branch predictor instance */ SS_ADDR_TYPE baddr, /* branch address */ SS_ADDR_TYPE 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 ss_opcode op, /* opcode of instruction */ int r31p, /* is this using r31? */ struct bpred_update *dir_update_ptr)/* predictor state pointer */{ struct bpred_btb_ent *pbtb = NULL; struct bpred_btb_ent *lruhead = NULL, *lruitem = NULL; int index, i; /* don't change bpred state for non-branch instructions or if this * is a stateless predictor*/ if (!(SS_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.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 (op == JR) { pred->jr_seen++; if (correct) pred->jr_hits++; if (r31p && pred->retstack.size) /* 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 */ /* If this is a function call, push return-address onto return-address * stack */ if ((SS_OP_FLAGS(op) & (F_CTRL|F_CALL)) == (F_CTRL|F_CALL) && pred->retstack.size) { pred->retstack.tos = (pred->retstack.tos + 1)% pred->retstack.size; pred->retstack.stack[pred->retstack.tos].target = baddr + sizeof(SS_INST_TYPE); pred->retstack_pushes++; } /* update L1 table if appropriate */ /* L1 table is updated unconditionally for combining predictor too */ if ((SS_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 >> 3) & (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 >> 3) & (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 + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -