xref: /XiangShan/src/main/scala/xiangshan/backend/decode/FusionDecoder.scala (revision a792bcf1a0e095a9bceade5a3f5b481df101a247)
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.ZEXT_H)
89    cs.lsrc(0) := instr1Rs1
90    cs
91  }
92
93  def fusionName: String = "slli48_srli48"
94}
95
96// Another case of Zext.h
97// Source: `slliw r1, r0, 16` + `srliw r1, r1, 16`
98// Target: `zext.h r1, r0`
99class FusedZexth1(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends FusedZexth(pair) {
100  override def inst1Cond: Bool = instr(0) === Instructions.SLLIW && instr(0)(24, 20) === 16.U
101  override def inst2Cond: Bool = instr(1) === Instructions.SRLIW && instr(1)(24, 20) === 16.U
102
103  override def fusionName: String = "slliw16_srliw16"
104}
105
106// Case: sign-extend a 16-bit number
107// Source: `slliw r1, r0, 16` + `sraiw r1, r1, 16`
108// Target: `sext.h r1, r0`
109class FusedSexth(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
110  def inst1Cond = instr(0) === Instructions.SLLIW && instr(0)(24, 20) === 16.U
111  def inst2Cond = instr(1) === Instructions.SRAIW && instr(1)(24, 20) === 16.U
112
113  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
114  def target: CtrlSignals = {
115    val cs = getBaseCS(Instructions.SEXT_H)
116    cs.lsrc(0) := instr1Rs1
117    cs
118  }
119
120  def fusionName: String = "slliw16_sraiw16"
121}
122
123// Case: shift left by one and add
124// Source: `slli r1, r0, 1` + `add r1, r1, r2`
125// Target: `sh1add r1, r0, r2`
126class FusedSh1add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
127  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 1.U
128  def inst2Cond = instr(1) === Instructions.ADD
129
130  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
131  def target: CtrlSignals = {
132    val cs = getBaseCS(Instructions.SH1ADD)
133    cs.lsrc(0) := instr1Rs1
134    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
135    cs
136  }
137
138  def fusionName: String = "slli1_add"
139}
140
141// Case: shift left by two and add
142// Source: `slli r1, r0, 2` + `add r1, r1, r2`
143// Target: `sh2add r1, r0, r2`
144class FusedSh2add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
145  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 2.U
146  def inst2Cond = instr(1) === Instructions.ADD
147
148  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
149  def target: CtrlSignals = {
150    val cs = getBaseCS(Instructions.SH2ADD)
151    cs.lsrc(0) := instr1Rs1
152    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
153    cs
154  }
155
156  def fusionName: String = "slli2_add"
157}
158
159// Case: shift left by three and add
160// Source: `slli r1, r0, 3` + `add r1, r1, r2`
161// Target: `sh3add r1, r0, r2`
162class FusedSh3add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
163  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 3.U
164  def inst2Cond = instr(1) === Instructions.ADD
165
166  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
167  def target: CtrlSignals = {
168    val cs = getBaseCS(Instructions.SH3ADD)
169    cs.lsrc(0) := instr1Rs1
170    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
171    cs
172  }
173
174  def fusionName: String = "slli3_add"
175}
176
177// Case: shift zero-extended word left by one
178// Source: `slli r1, r0, 32` + `srli r1, r0, 31`
179// Target: `szewl1 r1, r0` (customized internal opcode)
180class FusedSzewl1(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
181  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 32.U
182  def inst2Cond = instr(1) === Instructions.SRLI && instr(1)(25, 20) === 31.U
183
184  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
185  def target: CtrlSignals = {
186    val cs = getBaseCS(Instructions.ZEXT_H)
187    // replace the fuOpType with szewl1
188    cs.fuOpType := ALUOpType.szewl1
189    cs.lsrc(0) := instr1Rs1
190    cs
191  }
192
193  def fusionName: String = "slli32_srli31"
194}
195
196// Case: shift zero-extended word left by two
197// Source: `slli r1, r0, 32` + `srli r1, r0, 30`
198// Target: `szewl2 r1, r0` (customized internal opcode)
199class FusedSzewl2(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
200  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 32.U
201  def inst2Cond = instr(1) === Instructions.SRLI && instr(1)(25, 20) === 30.U
202
203  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
204  def target: CtrlSignals = {
205    val cs = getBaseCS(Instructions.ZEXT_H)
206    // replace the fuOpType with szewl2
207    cs.fuOpType := ALUOpType.szewl2
208    cs.lsrc(0) := instr1Rs1
209    cs
210  }
211
212  def fusionName: String = "slli32_srli30"
213}
214
215// Case: shift zero-extended word left by three
216// Source: `slli r1, r0, 32` + `srli r1, r0, 29`
217// Target: `szewl3 r1, r0` (customized internal opcode)
218class FusedSzewl3(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
219  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 32.U
220  def inst2Cond = instr(1) === Instructions.SRLI && instr(1)(25, 20) === 29.U
221
222  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
223  def target: CtrlSignals = {
224    val cs = getBaseCS(Instructions.ZEXT_H)
225    // replace the fuOpType with szewl3
226    cs.fuOpType := ALUOpType.szewl3
227    cs.lsrc(0) := instr1Rs1
228    cs
229  }
230
231  def fusionName: String = "slli32_srli29"
232}
233
234// Case: get the second byte
235// Source: `srli r1, r0, 8` + `andi r1, r1, 255`
236// Target: `byte2 r1, r0` (customized internal opcode)
237class FusedByte2(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
238  def inst1Cond = instr(0) === Instructions.SRLI && instr(0)(25, 20) === 8.U
239  def inst2Cond = instr(1) === Instructions.ANDI && instr(1)(31, 20) === 255.U
240
241  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
242  def target: CtrlSignals = {
243    val cs = getBaseCS(Instructions.ZEXT_H)
244    // replace the fuOpType with byte2
245    cs.fuOpType := ALUOpType.byte2
246    cs.lsrc(0) := instr1Rs1
247    cs
248  }
249
250  def fusionName: String = "srli8_andi255"
251}
252
253// Case: shift left by four and add
254// Source: `slli r1, r0, 4` + `add r1, r1, r2`
255// Target: `sh4add r1, r0, r2` (customized internal opcode)
256class FusedSh4add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
257  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 4.U
258  def inst2Cond = instr(1) === Instructions.ADD
259
260  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
261  def target: CtrlSignals = {
262    val cs = getBaseCS(Instructions.SH3ADD)
263    // replace the fuOpType with sh4add
264    cs.fuOpType := ALUOpType.sh4add
265    cs.lsrc(0) := instr1Rs1
266    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
267    cs
268  }
269
270  def fusionName: String = "slli4_add"
271}
272
273// Case: shift right by 29 and add
274// Source: `srli r1, r0, 29` + `add r1, r1, r2`
275// Target: `sr29add r1, r0, r2` (customized internal opcode)
276class FusedSr29add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
277  def inst1Cond = instr(0) === Instructions.SRLI && instr(0)(25, 20) === 29.U
278  def inst2Cond = instr(1) === Instructions.ADD
279
280  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
281  def target: CtrlSignals = {
282    val cs = getBaseCS(Instructions.SH3ADD)
283    // replace the fuOpType with sr29add
284    cs.fuOpType := ALUOpType.sr29add
285    cs.lsrc(0) := instr1Rs1
286    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
287    cs
288  }
289
290  def fusionName: String = "srli29_add"
291}
292
293// Case: shift right by 30 and add
294// Source: `srli r1, r0, 30` + `add r1, r1, r2`
295// Target: `sr30add r1, r0, r2` (customized internal opcode)
296class FusedSr30add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
297  def inst1Cond = instr(0) === Instructions.SRLI && instr(0)(25, 20) === 30.U
298  def inst2Cond = instr(1) === Instructions.ADD
299
300  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
301  def target: CtrlSignals = {
302    val cs = getBaseCS(Instructions.SH3ADD)
303    // replace the fuOpType with sr30add
304    cs.fuOpType := ALUOpType.sr30add
305    cs.lsrc(0) := instr1Rs1
306    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
307    cs
308  }
309
310  def fusionName: String = "srli30_add"
311}
312
313// Case: shift right by 31 and add
314// Source: `srli r1, r0, 31` + `add r1, r1, r2`
315// Target: `sr31add r1, r0, r2` (customized internal opcode)
316class FusedSr31add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
317  def inst1Cond = instr(0) === Instructions.SRLI && instr(0)(25, 20) === 31.U
318  def inst2Cond = instr(1) === Instructions.ADD
319
320  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
321  def target: CtrlSignals = {
322    val cs = getBaseCS(Instructions.SH3ADD)
323    // replace the fuOpType with sr31add
324    cs.fuOpType := ALUOpType.sr31add
325    cs.lsrc(0) := instr1Rs1
326    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
327    cs
328  }
329
330  def fusionName: String = "srli31_add"
331}
332
333// Case: shift right by 32 and add
334// Source: `srli r1, r0, 32` + `add r1, r1, r2`
335// Target: `sr32add r1, r0, r2` (customized internal opcode)
336class FusedSr32add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
337  def inst1Cond = instr(0) === Instructions.SRLI && instr(0)(25, 20) === 32.U
338  def inst2Cond = instr(1) === Instructions.ADD
339
340  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
341  def target: CtrlSignals = {
342    val cs = getBaseCS(Instructions.SH3ADD)
343    // replace the fuOpType with sr32add
344    cs.fuOpType := ALUOpType.sr32add
345    cs.lsrc(0) := instr1Rs1
346    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
347    cs
348  }
349
350  def fusionName: String = "srli32_add"
351}
352
353// Case: add one if odd, otherwise unchanged
354// Source: `andi r1, r0, 1`` + `add r1, r1, r2`
355// Target: `oddadd r1, r0, r2` (customized internal opcode)
356class FusedOddadd(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
357  def inst1Cond = instr(0) === Instructions.ANDI && instr(0)(31, 20) === 1.U
358  def inst2Cond = instr(1) === Instructions.ADD
359
360  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
361  def target: CtrlSignals = {
362    val cs = getBaseCS(Instructions.SH3ADD)
363    // replace the fuOpType with oddadd
364    cs.fuOpType := ALUOpType.oddadd
365    cs.lsrc(0) := instr1Rs1
366    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
367    cs
368  }
369
370  def fusionName: String = "andi1_add"
371}
372
373// Case: add one if odd (in word format), otherwise unchanged
374// Source: `andi r1, r0, 1`` + `addw r1, r1, r2`
375// Target: `oddaddw r1, r0, r2` (customized internal opcode)
376class FusedOddaddw(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
377  def inst1Cond = instr(0) === Instructions.ANDI && instr(0)(31, 20) === 1.U
378  def inst2Cond = instr(1) === Instructions.ADDW
379
380  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
381  def target: CtrlSignals = {
382    val cs = getBaseCS(Instructions.SH3ADD)
383    // replace the fuOpType with oddaddw
384    cs.fuOpType := ALUOpType.oddaddw
385    cs.lsrc(0) := instr1Rs1
386    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
387    cs
388  }
389
390  def fusionName: String = "andi1_addw"
391}
392
393// Case: addw and extract its lower 8 bits (fused into addwbyte)
394class FusedAddwbyte(pair: Seq[Valid[UInt]], csPair: Option[Seq[CtrlSignals]])(implicit p: Parameters)
395  extends BaseFusionCase(pair, csPair) {
396  require(csPair.isDefined)
397
398  // the first instruction is a addw
399  def inst1Cond = csPair.get(0).fuType === FuType.alu && ALUOpType.isAddw(csPair.get(0).fuOpType)
400  def inst2Cond = instr(1) === Instructions.ANDI && instr(1)(31, 20) === 0xff.U
401
402  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
403  def target: CtrlSignals = {
404    val cs = WireInit(csPair.get(0))
405    // replace the fuOpType with addwbyte
406    cs.fuOpType := ALUOpType.addwbyte
407    cs
408  }
409
410  def fusionName: String = "andw_andi255"
411}
412
413// Case: addw and extract its lower 1 bit (fused into addwbit)
414class FusedAddwbit(pair: Seq[Valid[UInt]], csPair: Option[Seq[CtrlSignals]])(implicit p: Parameters)
415  extends FusedAddwbyte(pair, csPair) {
416  override def inst2Cond = instr(1) === Instructions.ANDI && instr(1)(31, 20) === 0x1.U
417  override def target: CtrlSignals = {
418    val cs = WireInit(csPair.get(0))
419    // replace the fuOpType with addwbit
420    cs.fuOpType := ALUOpType.addwbit
421    cs
422  }
423  override def fusionName: String = "andw_andi1"
424}
425
426// Case: logic operation and extract its LSB
427class FusedLogiclsb(pair: Seq[Valid[UInt]], csPair: Option[Seq[CtrlSignals]])(implicit p: Parameters)
428  extends BaseFusionCase(pair, csPair) {
429  require(csPair.isDefined)
430
431  // the first instruction is a logic
432  def inst1Cond = csPair.get(0).fuType === FuType.alu && ALUOpType.isLogic(csPair.get(0).fuOpType)
433  def inst2Cond = instr(1) === Instructions.ANDI && instr(1)(31, 20) === 1.U
434
435  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
436  def target: CtrlSignals = {
437    val cs = WireInit(csPair.get(0))
438    // change the opType to lsb format
439    cs.fuOpType := ALUOpType.logicToLSB(csPair.get(0).fuOpType)
440    cs
441  }
442
443  def fusionName: String = "logic_andi1"
444}
445
446// Case: OR(Cat(src1(63, 8), 0.U(8.W)), src2)
447// Source: `andi r1, r0, -256`` + `or r1, r1, r2`
448class FusedOrh48(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
449  def inst1Cond = instr(0) === Instructions.ANDI && instr(0)(31, 20) === 0xf00.U
450  def inst2Cond = instr(1) === Instructions.OR
451
452  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
453  def target: CtrlSignals = {
454    val cs = getBaseCS(Instructions.OR)
455    // replace the fuOpType with orh48
456    cs.fuOpType := ALUOpType.orh48
457    cs.lsrc(0) := instr1Rs1
458    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
459    cs
460  }
461
462  def fusionName: String = "andi_f00_or"
463}
464
465// Case: mul 7bit data with 32-bit data
466// Source: `andi r1, r0, 127`` + `mulw r1, r1, r2`
467// Target: `mulw7 r1, r0, r2`
468class FusedMulw7(pair: Seq[Valid[UInt]], csPair: Option[Seq[CtrlSignals]])(implicit p: Parameters)
469  extends BaseFusionCase(pair, csPair) {
470  require(csPair.isDefined)
471
472  def inst1Cond = instr(0) === Instructions.ANDI && instr(0)(31, 20) === 127.U
473  def inst2Cond = instr(1) === Instructions.MULW
474
475  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
476  def target: CtrlSignals = {
477    // use MULW as the base
478    val cs = WireInit(csPair.get(1))
479    // replace the fuOpType with mulw7
480    cs.fuOpType := MDUOpType.mulw7
481    cs.lsrc(0) := instr1Rs1
482    cs.lsrc(1) := Mux(destToRs1, instr2Rs2, instr2Rs1)
483    cs
484  }
485
486  def fusionName: String = "andi127_mulw"
487}
488
489class FusionDecoder(implicit p: Parameters) extends XSModule {
490  val io = IO(new Bundle {
491    // detect instruction fusions in these instructions
492    val in = Vec(DecodeWidth, Flipped(ValidIO(UInt(32.W))))
493    val dec = Vec(DecodeWidth, Input(new CtrlSignals()))
494    // whether an instruction fusion is found
495    val out = Vec(DecodeWidth - 1, DecoupledIO(new CtrlSignals))
496    // fused instruction needs to be cleared
497    val clear = Vec(DecodeWidth, Output(Bool()))
498  })
499
500  io.clear.head := false.B
501
502  val instrPairs = io.in.dropRight(1).zip(io.in.drop(1)).map(x => Seq(x._1, x._2))
503  val csPairs = io.dec.dropRight(1).zip(io.dec.drop(1)).map(x => Seq(x._1, x._2))
504  instrPairs.zip(csPairs).zip(io.out).zipWithIndex.foreach{ case (((pair, cs), out), i) =>
505    val fusionList = Seq(
506      new FusedAdduw(pair),
507      new FusedZexth(pair),
508      new FusedZexth1(pair),
509      new FusedSexth(pair),
510      new FusedSh1add(pair),
511      new FusedSh2add(pair),
512      new FusedSh3add(pair),
513      new FusedSzewl1(pair),
514      new FusedSzewl2(pair),
515      new FusedSzewl3(pair),
516      new FusedByte2(pair),
517      new FusedSh4add(pair),
518      new FusedSr29add(pair),
519      new FusedSr30add(pair),
520      new FusedSr31add(pair),
521      new FusedSr32add(pair),
522      new FusedOddadd(pair),
523      new FusedOddaddw(pair),
524      new FusedAddwbyte(pair, Some(cs)),
525      new FusedAddwbit(pair, Some(cs)),
526      new FusedLogiclsb(pair, Some(cs)),
527      new FusedOrh48(pair),
528      new FusedMulw7(pair, Some(cs))
529    )
530    val pairValid = VecInit(pair.map(_.valid)).asUInt().andR
531    val thisCleared = io.clear(i)
532    val fusionVec = VecInit(fusionList.map(_.isValid))
533    out.valid := pairValid && !thisCleared && fusionVec.asUInt().orR()
534    XSError(PopCount(fusionVec) > 1.U, "more then one fusion matched\n")
535    out.bits := Mux1H(fusionVec, fusionList.map(_.target))
536    // TODO: assume every instruction fusion clears the second instruction now
537    io.clear(i + 1) := out.valid
538    fusionList.zip(fusionVec).foreach { case (f, v) =>
539      XSPerfAccumulate(s"case_${f.fusionName}_$i", pairValid && !thisCleared && v && out.ready)
540    }
541    XSPerfAccumulate(s"conflict_fusion_$i", pairValid && thisCleared && fusionVec.asUInt().orR() && out.ready)
542  }
543
544  XSPerfAccumulate("fused_instr", PopCount(io.out.map(_.fire)))
545}
546