reorganise twin-predication
[riscv-isa-sim.git] / id_regs.py
1 #!/usr/bin/env python
2 # Copyright (C) 2018 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
3
4 """ identify registers used in riscv/insns/*.h and create code
5 that can be used in spike at runtime
6
7 the design of spike assumes that once an opcode is identified,
8 the role of decoding the instruction is implicitly rolled into
9 and included inside the function that emulates that opcode.
10
11 however there may be circumstances where the behaviour of an
12 instruction has to change depending on "tags" associated with
13 the registers (security extensions, simple-v extension).
14
15 therefore this code walks the instruction implementations
16 in riscv/insns/*.h looking for register usage patterns.
17 the resultant table can be used *prior* to the emulation,
18 without having to manually maintain such a table.
19 """
20
21 import os
22 import sys
23
24 insns_dir = "./riscv/insns"
25 def list_insns():
26 if len(sys.argv) == 2:
27 fullfname = sys.argv[1]
28 pth, fname = os.path.split(fullfname)
29 insn = fname[:-2]
30 return [(fullfname, insn)]
31
32 res = []
33 for fname in os.listdir(insns_dir):
34 if not fname.endswith(".h"):
35 continue
36 if fname.startswith("regs_"):
37 continue
38 insn = fname[:-2]
39 res.append((os.path.join(insns_dir, fname), insn))
40 return res
41
42 cintpatterns = [ 'WRITE_RVC_RS1S', 'WRITE_RVC_RS2S',
43 'RVC_RS1', 'RVC_RS2', 'RVC_RS1S', 'RVC_RS2S', ]
44 cfloatpatterns = [ 'WRITE_RVC_FRS2S', 'RVC_FRS2 ', 'RVC_FRS2S ']
45 intpatterns = ['WRITE_RD' , 'RS1', 'RS2', 'RS3']
46 floatpatterns = ['WRITE_FRD', 'FRS1', 'FRS2', 'FRS3']
47 patterns = intpatterns + floatpatterns
48 patterns += cintpatterns
49 patterns += cfloatpatterns
50
51 allfloats = floatpatterns + cfloatpatterns
52 floatmask = (1<<len(allfloats)-1)
53 allints = intpatterns + cintpatterns[2:]
54
55 skip = '#define USING_NOREGS\n' \
56 '#define REGS_PATTERN 0x0\n'
57
58 # this matches the order of the 4 predication arguments to
59 drlookup = { 'rd': 0, 'frd': 0, 'rs1': 1, 'rs2': 2, 'rs3': 3,
60 'rvc_rs1': 1, 'rvc_rs1s': 1,
61 'rvc_rs2': 2, 'rvc_rs2s': 2,
62 'rvc_frs2': 2, 'rvc_frs2s': 2,
63 }
64
65 def find_registers(fname, twin_predication):
66 # HACK! macro-skipping of instructions too painful
67 for notparallel in ['csr', 'lui', 'c_j', 'wfi', 'auipc',
68 'dret', 'uret', 'mret', 'sret',
69 'lr_d', 'lr_w', 'sc_d', 'sc_w']:
70 if notparallel in fname:
71 return skip
72 res = []
73 regs = []
74 isintfloat = 0x0 + floatmask << len(allints)
75 with open(fname) as f:
76 f = f.read()
77 dest_reg = None
78 for pattern in patterns:
79 x = f.find(pattern)
80 if x == -1:
81 continue
82 if pattern.startswith('R') and x != 0 and f[x-1] == 'F':
83 # botch-job/hack: RS1 also matches against FRS1 (etc.)
84 # check letter before match: if "F", skip it.
85 continue
86 if pattern.startswith('R') and x != 0 and f[x-1] == '_':
87 # RS1 also matches against RVC_RS1 (etc.)
88 # check letter before match: if "_", skip it.
89 continue
90 if pattern.startswith('FR') and x != 0 and f[x-1] == '_':
91 # RS1 also matches against RVC_FRS1 (etc.)
92 # check letter before match: if "_", skip it.
93 continue
94 if 'RVC_' in pattern and f[x+len(pattern)] == 'S':
95 # RVC_RS2S also matches against RVC_RS2 (etc.)
96 # check letter at end of match: if "S", skip it.
97 continue
98 p = pattern
99 if p.startswith('WRITE_'):
100 p = p[6:]
101 dest_reg = p
102 if pattern in allints:
103 idx = allints.index(pattern)
104 isintfloat += 1 << idx
105 if pattern in allfloats:
106 idx = allfloats.index(pattern)
107 isintfloat &= ~(1 << (idx+len(allints)))
108 regs.append(p)
109 res.append('#define USING_REG_%s' % p)
110 if dest_reg:
111 dr = dest_reg
112 fdest = False
113 if dest_reg.startswith('RVC_F'):
114 fdest = True
115 dr = 'RVC_' + dest_reg[5:]
116 if dest_reg == 'FRD':
117 fdest = True
118 dr = 'RD'
119 dridx = drlookup[dest_reg.lower()]
120 res.append('#define DEST_REG %s' % dr.lower())
121 res.append('#define _DEST_REG _%s' % dr.lower())
122 res.append('#define DEST_PREDINT %d' % (0 if fdest else 1))
123 if not res:
124 return skip
125 res.append('#define REGS_PATTERN 0x%x' % isintfloat)
126
127 predargs = ['dest_pred'] * 4
128 if twin_predication:
129 found = None
130 for search in ['rs1', 'rs2', 'rs3', 'rvc_rs1', 'rvc_rs1s',
131 'rvc_rs2', 'rvc_rs2s',
132 'frs1', 'frs2', 'frs3',
133 'rvc_frs2', 'rvc_frs2s']:
134 if search.upper() in regs:
135 found = search
136 if found:
137 predargs[drlookup[found]] = 'src_pred'
138 fsrc = 'f' in found
139 found = found.replace('f', '')
140 res.append('#define SRC_PREDINT %d' % (0 if fsrc else 1))
141 res.append('#define SRC_REG %s' % found)
142
143 res.append('#define PRED_ARGS %s' % ','.join(predargs))
144 offsargs = []
145 for i in range(4):
146 offsargs.append(predargs[i].replace('pred', 'offs'))
147 res.append('#define OFFS_ARGS %s' % ','.join(offsargs))
148
149 return '\n'.join(res)
150
151 if __name__ == '__main__':
152 files = list_insns()
153 for (fname, insn) in files:
154 regsname = "regs_%s.h" % insn
155 regsname = os.path.join(insns_dir, regsname)
156 twin_predication = False
157 with open(regsname, "w") as f:
158 txt = "\n#define INSN_%s\n" % insn.upper()
159 # help identify type of register
160 if insn in ['beq', 'bne', 'blt', 'bltu', 'bge', 'bgeu']:
161 txt += "#define INSN_TYPE_BRANCH\n"
162 if insn in ['lb', 'lbu', 'lw', 'lwu', 'ld', 'ldu']:
163 twin_predication = True
164 txt += "#define INSN_TYPE_LOAD\n"
165 elif insn in ['c_lwsp', 'c_ldsp', 'c_lqsp', 'c_flwsp', 'c_fldsp']:
166 txt += "\n#define INSN_TYPE_C_STACK_LD\n"
167 elif insn in ['c_swsp', 'c_sdsp', 'c_sqsp', 'c_fswsp', 'c_fsdsp']:
168 txt += "\n#define INSN_TYPE_C_STACK_ST\n"
169 elif insn in ['c_lw', 'c_ld', 'c_lq', 'c_flw', 'c_fld']:
170 txt += "\n#define INSN_TYPE_C_LD\n"
171 twin_predication = True
172 elif insn in ['c_sw', 'c_sd', 'c_sq', 'c_fsw', 'c_fsd']:
173 txt += "\n#define INSN_TYPE_C_ST\n"
174 twin_predication = True
175 elif insn in ['c_beqz', 'c_bnez']:
176 txt += "\n#define INSN_TYPE_C_BRANCH\n"
177 elif insn in ['c_mv']:
178 twin_predication = True
179 elif insn.startswith("c_"):
180 txt += "#define INSN_TYPE_C\n"
181 elif insn.startswith("fmv") or \
182 insn.startswith("fcvt") or \
183 insn.startswith("fsgn"):
184 txt += "#define INSN_TYPE_FP_DUALOP\n"
185 elif insn.startswith("feq") or \
186 insn.startswith("flt") or \
187 insn.startswith("fle"):
188 txt += "#define INSN_TYPE_FP_BRANCH\n"
189 if twin_predication:
190 txt += "\n#define INSN_CATEGORY_TWINPREDICATION\n"
191 txt += find_registers(fname, twin_predication)
192 f.write(txt)