@@ -23,7 +23,7 @@ using namespace hcm;
2323// To use lanes evenly, the lane depends on some bits XL of B1's address via XORing.
2424
2525
26- template <u64 LOGG =19 , u64 GHIST =10 , u64 N=4 >
26+ template <u64 LOGG =19 , u64 GHIST =12 , u64 N=4 >
2727struct gshareN_ahead : predictor {
2828 // gshare with 2^LOGG entries, single prediction level (no overriding)
2929 // global history of GHIST bits
@@ -44,63 +44,97 @@ struct gshareN_ahead : predictor {
4444
4545 // pipelined over 2 cycles ([0]=current block, [1]=previous block)
4646 reg<index_bits> index[2 ];
47- reg<LOGBANKS > XB [2 ]; // address bits for selecting the bank
48- reg<LOGLANES > XL ; // address bits for selecting the lane (current block)
49- reg<LOGBANKS > path; // path out of previous block
47+ reg<LOGBANKS > XB [2 ]; // for using banks evenly
48+ reg<LANES > XL ; // for using lanes evenly
49+ reg<LOGBANKS > path[ 2 ] ; // path out of previous block
5050
5151 arr<reg<LANES >,BANKS > block_pred[2 ]; // read block predictions
52- arr< reg<1 >, LANES > unordered_pred; // read prediction bits for the current block, unordered
52+ reg<LANES > unordered_pred; // read prediction bits for the current block, unordered
5353 arr<reg<1 >,LANES > pred; // read prediction bits for the current block, ordered
5454
55- ram<arr<val<LANES >,BANKS >,(1 <<index_bits)> ctr_hi; // prediction bits
55+ // a true block is a block whose length is the same whether or not there is a mispredict
56+ reg<1 > true_block = 1 ;
57+ reg<1 > condbr_taken = 1 ;
58+
59+ // for detecting the line boundary and the last available prediction
60+ reg<LINEINST > block_entry; // one-hot vector pointing to entry point in the line
61+ reg<N> rank; // one-hot bit vector telling the rank of the current branch in the block
5662
5763 // simulation artifacts, hardware cost not modeled accurately
58- reg<1 > true_block = 1 ;
59- reg<LINEINST > block_entry; // one-hot vector
6064 u64 num_branch = 0 ; // number of conditional branches in block so far
6165 u64 block_size = 0 ; // instructions in current block so far
6266 arr<reg<1 >,N> branch_dir; // actual branch direction
6367
64- zone UPDATE_ONLY ;
68+ // RAMs
69+ ram<arr<val<LANES >,BANKS >,(1 <<index_bits)> ctr_hi; // prediction bits
6570 ram<val<1 >,(BANKS <<index_bits)> ctr_lo[LANES ]; // hysteresis bit (1=weak, 0=strong)
6671
72+ val<1 > line_end ()
73+ {
74+ return block_entry >> (LINEINST -block_size);
75+ }
76+
77+ val<1 > last_pred ()
78+ {
79+ return rank.rotate_left (num_branch);
80+ }
81+
6782 val<1 > predict1 ([[maybe_unused]] val<64 > inst_pc)
6883 {
69- // new block
70- assert (num_branch==0 );
71- block_size = 1 ;
72- index[1 ] = index[0 ];
73- val<index_bits> pc_bits = inst_pc >> (LOGBANKS +2 );
74- if constexpr (GHIST <= index_bits) {
75- index[0 ] = pc_bits.fo1 () ^ (val<index_bits>{global_history}<<(index_bits-GHIST ));
76- } else {
77- index[0 ] = global_history.make_array (val<index_bits>{}).append (pc_bits.fo1 ()).fold_xor ();
78- }
79- index[0 ].fanout (hard<2 >{});
80- XL = inst_pc >> 2 ;
84+ inst_pc.fanout (hard<3 >{});
85+ global_history.fanout (hard<2 >{});
86+ true_block.fanout (hard<6 >{});
87+
88+ // if the previous block was not a true block, we continue using the previous block predictions
89+ // (golden rule: never make a predictor's inputs depend on its outputs)
90+
91+ block_entry = select (true_block,
92+ val<LOGLINEINST >{inst_pc>>2 }.decode ().concat (),
93+ block_entry<<block_size);
94+ block_entry.fanout (hard<LINEINST +N+1 >{});
95+
96+ rank = select (true_block, val<N>{1 }, rank.rotate_left (num_branch));
97+
98+ XL = select (true_block,
99+ val<LOGLANES >{inst_pc>>2 }.decode ().concat (),
100+ XL .rotate_left (num_branch));
81101 XL .fanout (hard<LANES >{});
82- XB [1 ] = XB [0 ].fo1 ();
83- XB [0 ] = inst_pc >> 2 ;
84- XB [1 ].fanout (hard<2 >{});
85- block_pred[1 ] = block_pred[0 ].fo1 ();
86- block_pred[0 ] = ctr_hi.read (index[0 ]);
87- block_pred[1 ].fanout (hard<2 >{});
88- unordered_pred = block_pred[1 ].select (path^XB [1 ]).make_array (val<1 >{});
89- unordered_pred.fanout (hard<LANES >{});
102+
103+ execute_if (true_block, [&](){
104+ index[1 ] = index[0 ];
105+ val<index_bits> pc_bits = inst_pc >> (LOGBANKS +2 );
106+ if constexpr (GHIST <= index_bits) {
107+ index[0 ] = pc_bits.fo1 () ^ (val<index_bits>{global_history}<<(index_bits-GHIST ));
108+ } else {
109+ index[0 ] = global_history.make_array (val<index_bits>{}).append (pc_bits.fo1 ()).fold_xor ();
110+ }
111+ block_pred[1 ] = block_pred[0 ].fo1 ();
112+ block_pred[0 ] = ctr_hi.read (index[0 ]);
113+ XB [1 ] = XB [0 ].fo1 ();
114+ XB [0 ] = inst_pc >> 2 ;
115+ path[1 ] = (path[0 ] + num_branch) & condbr_taken.replicate (hard<LOGBANKS >{}).concat ();
116+ path[1 ].fanout (hard<3 >{});
117+ unordered_pred = block_pred[1 ].select (path[1 ]^XB [1 ]);
118+ unordered_pred.fanout (hard<LANES >{});
119+ });
120+
121+ path[0 ] = select (true_block, val<LOGBANKS >{0 }, val<LOGBANKS >{path[0 ]+num_branch});
122+ path[0 ].fanout (hard<2 >{});
123+
90124 for (u64 i=0 ; i<LANES ; i++) {
91- pred[i] = unordered_pred. select (i^ XL ) ;
125+ pred[i] = ( unordered_pred & XL . rotate_left (i)) != hard< 0 >{} ;
92126 }
93127 pred.fanout (hard<LINEINST *2 >{});
94- block_entry = val< LOGLINEINST >{inst_pc>> 2 }. decode (). concat () ;
95- block_entry. fanout (hard< LINEINST >{}) ;
96- reuse_prediction (~val< 1 >{block_entry>>( LINEINST - 1 )} );
97- return pred[0 ];
128+ block_size = 1 ;
129+ num_branch = 0 ;
130+ reuse_prediction (~line_end () );
131+ return pred[num_branch ];
98132 };
99133
100134 val<1 > reuse_predict1 ([[maybe_unused]] val<64 > inst_pc)
101135 {
102- reuse_prediction (~val<1 >{block_entry>>(LINEINST -1 -block_size)});
103136 block_size++;
137+ reuse_prediction (~line_end ());
104138 return pred[num_branch];
105139 };
106140
@@ -119,10 +153,7 @@ struct gshareN_ahead : predictor {
119153 assert (num_branch<N);
120154 branch_dir[num_branch] = taken.fo1 ();
121155 num_branch++;
122- if (num_branch == N) {
123- // this was the last branch for which we have a prediction: end the block
124- reuse_prediction (0 );
125- }
156+ reuse_prediction (~(line_end () | last_pred ()));
126157 }
127158
128159 void update_cycle ([[maybe_unused]] instruction_info &block_end_info)
@@ -131,15 +162,9 @@ struct gshareN_ahead : predictor {
131162 val<64 > &next_pc = block_end_info.next_pc ;
132163 if (num_branch == 0 ) {
133164 // no conditional branch in this block
134- // update global history if previous block ended on a mispredicted not-taken branch
135- // (we are still in the same line, this is the last chunk)
136- // or if the block ends on an unconditional jump
137- val<1 > uncond_jump = block_end_info.is_taken & ~block_end_info.is_conditional ;
138- execute_if (~true_block | uncond_jump.fo1 (), [&](){
139- global_history = (global_history << 1 ) ^ val<GHIST >{next_pc.fo1 ()>>2 };
140- true_block = 1 ;
141- });
142- path = 0 ;
165+ global_history = (global_history << 1 ) ^ val<GHIST >{next_pc.fo1 ()>>2 };
166+ condbr_taken = 0 ;
167+ true_block = 1 ;
143168 return ; // stop here
144169 }
145170 static_assert (LANES <=64 );
@@ -148,41 +173,38 @@ struct gshareN_ahead : predictor {
148173 branch_dir[num_branch-1 ].fanout (hard<LANES +LOGBANKS +1 >{});
149174 mispredict.fanout (hard<4 >{});
150175
151- // determine the lanes that are accessed
152- arr<val<LANES >,LANES > lane = [&](u64 i) -> val<LANES > {
153- return (XL ^i).decode ().concat ();
154- };
155- lane.fanout (hard<2 >{});
156- arr<val<1 >,LANES > access = [&](u64 i) -> val<1 > {
157- return (lane[i]>>num_branch) == hard<0 >{};
158- };
176+ // access = mask telling which lanes are accessed by branches in the block
177+ arr<val<1 >,LANES > access = arr<val<LANES >,LANES > { [&](u64 i){
178+ return XL .rotate_left (i) & val<LANES >{-(i<num_branch)};
179+ }}.fold_or ().make_array (val<1 >{});
159180
160- // determine the lane corresponding to the mispredicted branch
161- val<LANES > misp_lane = execute_if (mispredict, [&]() -> val<LANES > {
162- return lane[num_branch-1 ];
163- });
164- arr<val<1 >,LANES > mispredicted = misp_lane.fo1 ().make_array (val<1 >{});
181+ // misp bank = bit vector pointing to the lane accessed by the mispredicted branch
182+ // (all zero if no mispredict)
183+ val<LANES > misp_bank = XL .rotate_left (num_branch-1 ) & mispredict.replicate (hard<LANES >{}).concat ();
184+ arr<val<1 >,LANES > mispredicted = misp_bank.fo1 ().make_array (val<1 >{});
165185 mispredicted.fanout (hard<2 >{});
166186
167187 // determine the bank to update
168- val<LOGBANKS > bank = path ^ XB [1 ];
188+ val<LOGBANKS > bank = path[ 1 ] ^ XB [1 ];
169189 bank.fanout (hard<2 *LANES +BANKS >{});
170190
171- // read hysteresis bit if lane corresponds to mispredicted branch
191+ // read hysteresis bit iff mispredict
192+ // weak[i] = 1 iff bank #i corresponds to mispredicted branch and hysteresis is weak
172193 arr<val<1 >,LANES > weak = [&](u64 i){
173194 return execute_if (mispredicted[i], [&](){
174- // return 1 iff mispredict and hysteresis is weak
175195 return ctr_lo[i].read (concat (index[1 ],bank));
176196 });
177197 };
178198
179199 // we need an extra cycle if there is a mispredict
180200 need_extra_cycle (mispredict);
201+
181202 // update prediction if mispredict and the hysteresis bit is weak
182203 execute_if (mispredict, [&](){
204+ arr<val<1 >,N> stored_pred = unordered_pred.make_array (val<1 >{});
183205 val<LANES > block_bundle = arr<val<1 >,LANES >{
184206 [&](u64 i){
185- return select (weak[i].fo1 (),branch_dir[num_branch-1 ],unordered_pred [i]);
207+ return select (weak[i].fo1 (),branch_dir[num_branch-1 ],stored_pred [i]. fo1 () );
186208 }
187209 }.concat ();
188210 block_bundle.fanout (hard<BANKS >{});
@@ -199,17 +221,16 @@ struct gshareN_ahead : predictor {
199221 });
200222 }
201223
202- // update the global history
203- val<1 > line_end = block_entry >> (LINEINST -block_size);
204- true_block = (~mispredict & (num_branch<N)) | branch_dir[num_branch-1 ] | line_end.fo1 ();
224+ condbr_taken = branch_dir[num_branch-1 ];
225+
226+ // update the global history if this is a true block
227+ true_block = arr<val<1 >,4 > {
228+ ~mispredict, condbr_taken, last_pred (), line_end ()
229+ }.fold_or ();
230+
205231 execute_if (true_block, [&](){
206232 global_history = (global_history << 1 ) ^ val<GHIST >{next_pc.fo1 ()>>2 };
207233 });
208234
209- // record the path: if last cond branch not-taken, then path=0, else path=num_branch
210- path = num_branch & branch_dir[num_branch-1 ].replicate (hard<LOGBANKS >{}).concat ();
211- path.fanout (hard<2 >{});
212-
213- num_branch = 0 ;
214235 }
215236};
0 commit comments