xref: /XiangShan/src/main/scala/xiangshan/backend/decode/FusionDecoder.scala (revision 07596dc67f098c2f5bc472948ed58ce5daff4214)
1/***************************************************************************************
2* Copyright (c) 2020-2021 Institute of Computing Technology, Chinese Academy of Sciences
3* Copyright (c) 2020-2021 Peng Cheng Laboratory
4*
5* XiangShan is licensed under Mulan PSL v2.
6* You can use this software according to the terms and conditions of the Mulan PSL v2.
7* You may obtain a copy of Mulan PSL v2 at:
8*          http://license.coscl.org.cn/MulanPSL2
9*
10* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
11* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
12* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
13*
14* See the Mulan PSL v2 for more details.
15***************************************************************************************/
16
17package xiangshan.backend.decode
18
19import chipsalliance.rocketchip.config.Parameters
20import chisel3._
21import chisel3.util.BitPat.bitPatToUInt
22import chisel3.util._
23import xiangshan._
24import utils._
25
26abstract class BaseFusionCase(pair: Seq[Valid[UInt]], csPair: Option[Seq[CtrlSignals]] = None)(implicit p: Parameters)
27  extends DecodeUnitConstants {
28  require(pair.length == 2)
29
30  protected def instr: Seq[UInt] = pair.map(_.bits)
31  protected def pairValid: Bool = VecInit(pair.map(_.valid)).asUInt().andR()
32  protected def instr1Rs1: UInt = instr(0)(RS1_MSB, RS1_LSB)
33  protected def instr1Rs2: UInt = instr(0)(RS2_MSB, RS2_LSB)
34  protected def instr1Rd: UInt = instr(0)(RD_MSB, RD_LSB)
35  protected def instr2Rs1: UInt = instr(1)(RS1_MSB, RS1_LSB)
36  protected def instr2Rs2: UInt = instr(1)(RS2_MSB, RS2_LSB)
37  protected def instr2Rd: UInt = instr(1)(RD_MSB, RD_LSB)
38  protected def withSameDest: Bool = instr1Rd === instr2Rd
39  protected def destToRs1: Bool = instr1Rd === instr2Rs1
40  protected def destToRs2: Bool = instr1Rd === instr2Rs2
41
42  protected def getBaseCS(pat: BitPat): CtrlSignals = {
43    val allDecodeTable = XDecode.table ++ X64Decode.table ++ BDecode.table
44    val baseTable = allDecodeTable.filter(_._1 == pat).map(_._2).head
45    val cs = Wire(new CtrlSignals)
46    cs := DontCare
47    cs.decode(baseTable)
48    // For simple instruction fusions, we assume their destination registers are the same.
49    cs.ldest := instr1Rd
50    cs
51  }
52
53  def isValid: Bool
54  // TODO: optimize timing
55  def target: CtrlSignals
56  // clear the next instruction
57  // def needClear: Boolean = true
58  def fusionName: String
59}
60
61// Case: clear upper 32 bits / get lower 32 bits
62// Source: `slli r1, r0, 32` + `srli r1, r1, 32`
63// Target: `add.uw r1, r0, zero` (pseudo instruction: `zext.w r1, r0`)
64class FusedAdduw(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
65  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 32.U
66  def inst2Cond = instr(1) === Instructions.SRLI && instr(1)(25, 20) === 32.U
67
68  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
69  def target: CtrlSignals = {
70    val cs = getBaseCS(Instructions.ADDU_W)
71    cs.lsrc(0) := instr1Rs1
72    cs.lsrc(1) := 0.U
73    cs
74  }
75
76  def fusionName: String = "slli32_srli32"
77}
78
79// Case: clear upper 48 bits / get lower 16 bits
80// Source: `slli r1, r0, 48` + `srli r1, r1, 48`
81// Target: `zext.h r1, r0`
82class FusedZexth(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
83  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 48.U
84  def inst2Cond = instr(1) === Instructions.SRLI && instr(1)(25, 20) === 48.U
85
86  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
87  def target: CtrlSignals = {
88    val cs = getBaseCS(Instructions.PACKW)
89    cs.lsrc(0) := instr1Rs1
90    cs.lsrc(1) := 0.U
91    cs
92  }
93
94  def fusionName: String = "slli48_srli48"
95}
96
97// Another case of Zext.h
98// Source: `slliw r1, r0, 16` + `srliw r1, r1, 16`
99// Target: `zext.h r1, r0`
100class FusedZexth1(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends FusedZexth(pair) {
101  override def inst1Cond: Bool = instr(0) === Instructions.SLLIW && instr(0)(24, 20) === 16.U
102  override def inst2Cond: Bool = instr(1) === Instructions.SRLIW && instr(1)(24, 20) === 16.U
103
104  override def fusionName: String = "slliw16_srliw16"
105}
106
107// Case: sign-extend a 16-bit number
108// Source: `slliw r1, r0, 16` + `sraiw r1, r1, 16`
109// Target: `sext.h r1, r0`
110class FusedSexth(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
111  def inst1Cond = instr(0) === Instructions.SLLIW && instr(0)(24, 20) === 16.U
112  def inst2Cond = instr(1) === Instructions.SRAIW && instr(1)(24, 20) === 16.U
113
114  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
115  def target: CtrlSignals = {
116    val cs = getBaseCS(Instructions.SEXT_H)
117    cs.lsrc(0) := instr1Rs1
118    cs
119  }
120
121  def fusionName: String = "slliw16_sraiw16"
122}
123
124// Case: shift left by one and add
125// Source: `slli r1, r0, 1` + `add r1, r1, r2`
126// Target: `sh1add r1, r0, r2`
127class FusedSh1add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
128  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 1.U
129  def inst2Cond = instr(1) === Instructions.ADD
130
131  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
132  def target: CtrlSignals = {
133    val cs = getBaseCS(Instructions.SH1ADD)
134    cs.lsrc(0) := instr1Rs1
135    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
136    cs
137  }
138
139  def fusionName: String = "slli1_add"
140}
141
142// Case: shift left by two and add
143// Source: `slli r1, r0, 2` + `add r1, r1, r2`
144// Target: `sh2add r1, r0, r2`
145class FusedSh2add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
146  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 2.U
147  def inst2Cond = instr(1) === Instructions.ADD
148
149  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
150  def target: CtrlSignals = {
151    val cs = getBaseCS(Instructions.SH2ADD)
152    cs.lsrc(0) := instr1Rs1
153    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
154    cs
155  }
156
157  def fusionName: String = "slli2_add"
158}
159
160// Case: shift left by three and add
161// Source: `slli r1, r0, 3` + `add r1, r1, r2`
162// Target: `sh3add r1, r0, r2`
163class FusedSh3add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
164  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 3.U
165  def inst2Cond = instr(1) === Instructions.ADD
166
167  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
168  def target: CtrlSignals = {
169    val cs = getBaseCS(Instructions.SH3ADD)
170    cs.lsrc(0) := instr1Rs1
171    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
172    cs
173  }
174
175  def fusionName: String = "slli3_add"
176}
177
178// Case: shift zero-extended word left by one
179// Source: `slli r1, r0, 32` + `srli r1, r0, 31`
180// Target: `szewl1 r1, r0` (customized internal opcode)
181class FusedSzewl1(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
182  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 32.U
183  def inst2Cond = instr(1) === Instructions.SRLI && instr(1)(25, 20) === 31.U
184
185  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
186  def target: CtrlSignals = {
187    val cs = getBaseCS(Instructions.SEXT_H)
188    // replace the fuOpType with szewl1
189    cs.fuOpType := ALUOpType.szewl1
190    cs.lsrc(0) := instr1Rs1
191    cs
192  }
193
194  def fusionName: String = "slli32_srli31"
195}
196
197// Case: shift zero-extended word left by two
198// Source: `slli r1, r0, 32` + `srli r1, r0, 30`
199// Target: `szewl2 r1, r0` (customized internal opcode)
200class FusedSzewl2(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
201  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 32.U
202  def inst2Cond = instr(1) === Instructions.SRLI && instr(1)(25, 20) === 30.U
203
204  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
205  def target: CtrlSignals = {
206    val cs = getBaseCS(Instructions.SEXT_H)
207    // replace the fuOpType with szewl2
208    cs.fuOpType := ALUOpType.szewl2
209    cs.lsrc(0) := instr1Rs1
210    cs
211  }
212
213  def fusionName: String = "slli32_srli30"
214}
215
216// Case: shift zero-extended word left by three
217// Source: `slli r1, r0, 32` + `srli r1, r0, 29`
218// Target: `szewl3 r1, r0` (customized internal opcode)
219class FusedSzewl3(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
220  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 32.U
221  def inst2Cond = instr(1) === Instructions.SRLI && instr(1)(25, 20) === 29.U
222
223  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
224  def target: CtrlSignals = {
225    val cs = getBaseCS(Instructions.SEXT_H)
226    // replace the fuOpType with szewl3
227    cs.fuOpType := ALUOpType.szewl3
228    cs.lsrc(0) := instr1Rs1
229    cs
230  }
231
232  def fusionName: String = "slli32_srli29"
233}
234
235// Case: get the second byte
236// Source: `srli r1, r0, 8` + `andi r1, r1, 255`
237// Target: `byte2 r1, r0` (customized internal opcode)
238class FusedByte2(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
239  def inst1Cond = instr(0) === Instructions.SRLI && instr(0)(25, 20) === 8.U
240  def inst2Cond = instr(1) === Instructions.ANDI && instr(1)(31, 20) === 255.U
241
242  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
243  def target: CtrlSignals = {
244    val cs = getBaseCS(Instructions.SEXT_H)
245    // replace the fuOpType with byte2
246    cs.fuOpType := ALUOpType.byte2
247    cs.lsrc(0) := instr1Rs1
248    cs
249  }
250
251  def fusionName: String = "srli8_andi255"
252}
253
254// Case: shift left by four and add
255// Source: `slli r1, r0, 4` + `add r1, r1, r2`
256// Target: `sh4add r1, r0, r2` (customized internal opcode)
257class FusedSh4add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
258  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 4.U
259  def inst2Cond = instr(1) === Instructions.ADD
260
261  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
262  def target: CtrlSignals = {
263    val cs = getBaseCS(Instructions.SH3ADD)
264    // replace the fuOpType with sh4add
265    cs.fuOpType := ALUOpType.sh4add
266    cs.lsrc(0) := instr1Rs1
267    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
268    cs
269  }
270
271  def fusionName: String = "slli4_add"
272}
273
274// Case: shift right by 29 and add
275// Source: `srli r1, r0, 29` + `add r1, r1, r2`
276// Target: `sr29add r1, r0, r2` (customized internal opcode)
277class FusedSr29add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
278  def inst1Cond = instr(0) === Instructions.SRLI && instr(0)(25, 20) === 29.U
279  def inst2Cond = instr(1) === Instructions.ADD
280
281  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
282  def target: CtrlSignals = {
283    val cs = getBaseCS(Instructions.SH3ADD)
284    // replace the fuOpType with sr29add
285    cs.fuOpType := ALUOpType.sr29add
286    cs.lsrc(0) := instr1Rs1
287    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
288    cs
289  }
290
291  def fusionName: String = "srli29_add"
292}
293
294// Case: shift right by 30 and add
295// Source: `srli r1, r0, 30` + `add r1, r1, r2`
296// Target: `sr30add r1, r0, r2` (customized internal opcode)
297class FusedSr30add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
298  def inst1Cond = instr(0) === Instructions.SRLI && instr(0)(25, 20) === 30.U
299  def inst2Cond = instr(1) === Instructions.ADD
300
301  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
302  def target: CtrlSignals = {
303    val cs = getBaseCS(Instructions.SH3ADD)
304    // replace the fuOpType with sr30add
305    cs.fuOpType := ALUOpType.sr30add
306    cs.lsrc(0) := instr1Rs1
307    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
308    cs
309  }
310
311  def fusionName: String = "srli30_add"
312}
313
314// Case: shift right by 31 and add
315// Source: `srli r1, r0, 31` + `add r1, r1, r2`
316// Target: `sr31add r1, r0, r2` (customized internal opcode)
317class FusedSr31add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
318  def inst1Cond = instr(0) === Instructions.SRLI && instr(0)(25, 20) === 31.U
319  def inst2Cond = instr(1) === Instructions.ADD
320
321  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
322  def target: CtrlSignals = {
323    val cs = getBaseCS(Instructions.SH3ADD)
324    // replace the fuOpType with sr31add
325    cs.fuOpType := ALUOpType.sr31add
326    cs.lsrc(0) := instr1Rs1
327    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
328    cs
329  }
330
331  def fusionName: String = "srli31_add"
332}
333
334// Case: shift right by 32 and add
335// Source: `srli r1, r0, 32` + `add r1, r1, r2`
336// Target: `sr32add r1, r0, r2` (customized internal opcode)
337class FusedSr32add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
338  def inst1Cond = instr(0) === Instructions.SRLI && instr(0)(25, 20) === 32.U
339  def inst2Cond = instr(1) === Instructions.ADD
340
341  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
342  def target: CtrlSignals = {
343    val cs = getBaseCS(Instructions.SH3ADD)
344    // replace the fuOpType with sr32add
345    cs.fuOpType := ALUOpType.sr32add
346    cs.lsrc(0) := instr1Rs1
347    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
348    cs
349  }
350
351  def fusionName: String = "srli32_add"
352}
353
354// Case: add one if odd, otherwise unchanged
355// Source: `andi r1, r0, 1`` + `add r1, r1, r2`
356// Target: `oddadd r1, r0, r2` (customized internal opcode)
357class FusedOddadd(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
358  def inst1Cond = instr(0) === Instructions.ANDI && instr(0)(31, 20) === 1.U
359  def inst2Cond = instr(1) === Instructions.ADD
360
361  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
362  def target: CtrlSignals = {
363    val cs = getBaseCS(Instructions.SH3ADD)
364    // replace the fuOpType with oddadd
365    cs.fuOpType := ALUOpType.oddadd
366    cs.lsrc(0) := instr1Rs1
367    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
368    cs
369  }
370
371  def fusionName: String = "andi1_add"
372}
373
374// Case: add one if odd (in word format), otherwise unchanged
375// Source: `andi r1, r0, 1`` + `addw r1, r1, r2`
376// Target: `oddaddw r1, r0, r2` (customized internal opcode)
377class FusedOddaddw(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
378  def inst1Cond = instr(0) === Instructions.ANDI && instr(0)(31, 20) === 1.U
379  def inst2Cond = instr(1) === Instructions.ADDW
380
381  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
382  def target: CtrlSignals = {
383    val cs = getBaseCS(Instructions.SH3ADD)
384    // replace the fuOpType with oddaddw
385    cs.fuOpType := ALUOpType.oddaddw
386    cs.lsrc(0) := instr1Rs1
387    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
388    cs
389  }
390
391  def fusionName: String = "andi1_addw"
392}
393
394// Case: addw and extract its lower 8 bits (fused into addwbyte)
395class FusedAddwbyte(pair: Seq[Valid[UInt]], csPair: Option[Seq[CtrlSignals]])(implicit p: Parameters)
396  extends BaseFusionCase(pair, csPair) {
397  require(csPair.isDefined)
398
399  // the first instruction is a addw
400  def inst1Cond = csPair.get(0).fuType === FuType.alu && ALUOpType.isAddw(csPair.get(0).fuOpType)
401  def inst2Cond = instr(1) === Instructions.ANDI && instr(1)(31, 20) === 0xff.U
402
403  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
404  def target: CtrlSignals = {
405    val cs = WireInit(csPair.get(0))
406    // replace the fuOpType with addwbyte
407    cs.fuOpType := ALUOpType.addwbyte
408    cs
409  }
410
411  def fusionName: String = "andw_andi255"
412}
413
414// Case: addw and extract its lower 1 bit (fused into addwbit)
415class FusedAddwbit(pair: Seq[Valid[UInt]], csPair: Option[Seq[CtrlSignals]])(implicit p: Parameters)
416  extends FusedAddwbyte(pair, csPair) {
417  override def inst2Cond = instr(1) === Instructions.ANDI && instr(1)(31, 20) === 0x1.U
418  override def target: CtrlSignals = {
419    val cs = WireInit(csPair.get(0))
420    // replace the fuOpType with addwbit
421    cs.fuOpType := ALUOpType.addwbit
422    cs
423  }
424  override def fusionName: String = "andw_andi1"
425}
426
427// Case: logic operation and extract its LSB
428class FusedLogiclsb(pair: Seq[Valid[UInt]], csPair: Option[Seq[CtrlSignals]])(implicit p: Parameters)
429  extends BaseFusionCase(pair, csPair) {
430  require(csPair.isDefined)
431
432  // the first instruction is a logic
433  def inst1Cond = csPair.get(0).fuType === FuType.alu && ALUOpType.isLogic(csPair.get(0).fuOpType)
434  def inst2Cond = instr(1) === Instructions.ANDI && instr(1)(31, 20) === 1.U
435
436  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
437  def target: CtrlSignals = {
438    val cs = WireInit(csPair.get(0))
439    // change the opType to lsb format
440    cs.fuOpType := ALUOpType.logicToLSB(csPair.get(0).fuOpType)
441    cs
442  }
443
444  def fusionName: String = "logic_andi1"
445}
446
447// Case: OR(Cat(src1(63, 8), 0.U(8.W)), src2)
448// Source: `andi r1, r0, -256`` + `or r1, r1, r2`
449class FusedOrh48(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
450  def inst1Cond = instr(0) === Instructions.ANDI && instr(0)(31, 20) === 0xf00.U
451  def inst2Cond = instr(1) === Instructions.OR
452
453  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
454  def target: CtrlSignals = {
455    val cs = getBaseCS(Instructions.OR)
456    // replace the fuOpType with orh48
457    cs.fuOpType := ALUOpType.orh48
458    cs.lsrc(0) := instr1Rs1
459    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
460    cs
461  }
462
463  def fusionName: String = "andi_f00_or"
464}
465
466// Case: mul 7bit data with 32-bit data
467// Source: `andi r1, r0, 127`` + `mulw r1, r1, r2`
468// Target: `mulw7 r1, r0, r2`
469class FusedMulw7(pair: Seq[Valid[UInt]], csPair: Option[Seq[CtrlSignals]])(implicit p: Parameters)
470  extends BaseFusionCase(pair, csPair) {
471  require(csPair.isDefined)
472
473  def inst1Cond = instr(0) === Instructions.ANDI && instr(0)(31, 20) === 127.U
474  def inst2Cond = instr(1) === Instructions.MULW
475
476  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
477  def target: CtrlSignals = {
478    // use MULW as the base
479    val cs = WireInit(csPair.get(1))
480    // replace the fuOpType with mulw7
481    cs.fuOpType := MDUOpType.mulw7
482    cs.lsrc(0) := instr1Rs1
483    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
484    cs
485  }
486
487  def fusionName: String = "andi127_mulw"
488}
489
490class FusionDecoder(implicit p: Parameters) extends XSModule {
491  val io = IO(new Bundle {
492    // detect instruction fusions in these instructions
493    val in = Vec(DecodeWidth, Flipped(ValidIO(UInt(32.W))))
494    val dec = Vec(DecodeWidth, Input(new CtrlSignals()))
495    // whether an instruction fusion is found
496    val out = Vec(DecodeWidth - 1, DecoupledIO(new CtrlSignals))
497    // fused instruction needs to be cleared
498    val clear = Vec(DecodeWidth, Output(Bool()))
499  })
500
501  io.clear.head := false.B
502
503  val instrPairs = io.in.dropRight(1).zip(io.in.drop(1)).map(x => Seq(x._1, x._2))
504  val csPairs = io.dec.dropRight(1).zip(io.dec.drop(1)).map(x => Seq(x._1, x._2))
505  instrPairs.zip(csPairs).zip(io.out).zipWithIndex.foreach{ case (((pair, cs), out), i) =>
506    val fusionList = Seq(
507      new FusedAdduw(pair),
508      new FusedZexth(pair),
509      new FusedZexth1(pair),
510      new FusedSexth(pair),
511      new FusedSh1add(pair),
512      new FusedSh2add(pair),
513      new FusedSh3add(pair),
514      new FusedSzewl1(pair),
515      new FusedSzewl2(pair),
516      new FusedSzewl3(pair),
517      new FusedByte2(pair),
518      new FusedSh4add(pair),
519      new FusedSr29add(pair),
520      new FusedSr30add(pair),
521      new FusedSr31add(pair),
522      new FusedSr32add(pair),
523      new FusedOddadd(pair),
524      new FusedOddaddw(pair),
525      new FusedAddwbyte(pair, Some(cs)),
526      new FusedAddwbit(pair, Some(cs)),
527      new FusedLogiclsb(pair, Some(cs)),
528      new FusedOrh48(pair),
529      new FusedMulw7(pair, Some(cs))
530    )
531    val pairValid = VecInit(pair.map(_.valid)).asUInt().andR
532    val thisCleared = io.clear(i)
533    val fusionVec = VecInit(fusionList.map(_.isValid))
534    out.valid := pairValid && !thisCleared && fusionVec.asUInt().orR()
535    XSError(PopCount(fusionVec) > 1.U, "more then one fusion matched\n")
536    out.bits := Mux1H(fusionVec, fusionList.map(_.target))
537    // TODO: assume every instruction fusion clears the second instruction now
538    io.clear(i + 1) := out.valid
539    fusionList.zip(fusionVec).foreach { case (f, v) =>
540      XSPerfAccumulate(s"case_${f.fusionName}_$i", pairValid && !thisCleared && v && out.ready)
541    }
542    XSPerfAccumulate(s"conflict_fusion_$i", pairValid && thisCleared && fusionVec.asUInt().orR() && out.ready)
543  }
544
545  XSPerfAccumulate("fused_instr", PopCount(io.out.map(_.fire)))
546}
547