| 1 | #! /usr/bin/env python3 |
| 2 | |
| 3 | # Generate test-avx.h from x86.csv |
| 4 | |
| 5 | import csv |
| 6 | import sys |
| 7 | from fnmatch import fnmatch |
| 8 | |
| 9 | ignore = set(["EMMS", "FEMMS", "FISTTP", |
| 10 | "LDMXCSR", "VLDMXCSR", "STMXCSR", "VSTMXCSR"]) |
| 11 | |
| 12 | imask = { |
| 13 | 'PALIGNR': 0x3f, |
| 14 | 'PEXTRB': 0x0f, |
| 15 | 'PEXTRW': 0x07, |
| 16 | 'PEXTRD': 0x03, |
| 17 | 'PEXTRQ': 0x01, |
| 18 | 'PINSRB': 0x0f, |
| 19 | 'PINSRW': 0x07, |
| 20 | 'PINSRD': 0x03, |
| 21 | 'PINSRQ': 0x01, |
| 22 | 'PSHUF[DW]': 0xff, |
| 23 | 'PSHUF[LH]W': 0xff, |
| 24 | 'PS[LR][AL][WDQ]': 0x3f, |
| 25 | } |
| 26 | |
| 27 | def strip_comments(x): |
| 28 | for l in x: |
| 29 | if l != '' and l[0] != '#': |
| 30 | yield l |
| 31 | |
| 32 | def reg_w(w): |
| 33 | if w == 8: |
| 34 | return 'al' |
| 35 | elif w == 16: |
| 36 | return 'ax' |
| 37 | elif w == 32: |
| 38 | return 'eax' |
| 39 | elif w == 64: |
| 40 | return 'rax' |
| 41 | raise Exception("bad reg_w %d" % w) |
| 42 | |
| 43 | def mem_w(w): |
| 44 | if w == 8: |
| 45 | t = "BYTE" |
| 46 | elif w == 16: |
| 47 | t = "WORD" |
| 48 | elif w == 32: |
| 49 | t = "DWORD" |
| 50 | elif w == 64: |
| 51 | t = "QWORD" |
| 52 | else: |
| 53 | raise Exception() |
| 54 | |
| 55 | return t + " PTR 32[rdx]" |
| 56 | |
| 57 | class MMArg(): |
| 58 | isxmm = True |
| 59 | |
| 60 | def __init__(self, mw): |
| 61 | if mw not in [0, 32, 64]: |
| 62 | raise Exception("Bad /m width: %s" % w) |
| 63 | self.mw = mw |
| 64 | self.ismem = mw != 0 |
| 65 | def regstr(self, n): |
| 66 | if n < 0: |
| 67 | return mem_w(self.mw) |
| 68 | else: |
| 69 | return "mm%d" % (n, ) |
| 70 | |
| 71 | def match(op, pattern): |
| 72 | return fnmatch(op, pattern) |
| 73 | |
| 74 | class ArgImm8u(): |
| 75 | isxmm = False |
| 76 | ismem = False |
| 77 | def __init__(self, op): |
| 78 | for k, v in imask.items(): |
| 79 | if match(op, k): |
| 80 | self.mask = imask[k]; |
| 81 | return |
| 82 | raise Exception("Unknown immediate") |
| 83 | def vals(self): |
| 84 | mask = self.mask |
| 85 | yield 0 |
| 86 | n = 0 |
| 87 | while n != mask: |
| 88 | n += 1 |
| 89 | while (n & ~mask) != 0: |
| 90 | n += (n & ~mask) |
| 91 | yield n |
| 92 | |
| 93 | class ArgRM(): |
| 94 | isxmm = False |
| 95 | def __init__(self, rw, mw): |
| 96 | if rw not in [8, 16, 32, 64]: |
| 97 | raise Exception("Bad r/w width: %s" % w) |
| 98 | if mw not in [0, 8, 16, 32, 64]: |
| 99 | raise Exception("Bad r/w width: %s" % w) |
| 100 | self.rw = rw |
| 101 | self.mw = mw |
| 102 | self.ismem = mw != 0 |
| 103 | def regstr(self, n): |
| 104 | if n < 0: |
| 105 | return mem_w(self.mw) |
| 106 | else: |
| 107 | return reg_w(self.rw) |
| 108 | |
| 109 | class ArgMem(): |
| 110 | isxmm = False |
| 111 | ismem = True |
| 112 | def __init__(self, w): |
| 113 | if w not in [8, 16, 32, 64, 128, 256]: |
| 114 | raise Exception("Bad mem width: %s" % w) |
| 115 | self.w = w |
| 116 | def regstr(self, n): |
| 117 | return mem_w(self.w) |
| 118 | |
| 119 | class SkipInstruction(Exception): |
| 120 | pass |
| 121 | |
| 122 | def ArgGenerator(arg, op): |
| 123 | if arg[:2] == 'mm': |
| 124 | if "/" in arg: |
| 125 | r, m = arg.split('/') |
| 126 | if (m[0] != 'm'): |
| 127 | raise Exception("Expected /m: %s", arg) |
| 128 | return MMArg(int(m[1:])); |
| 129 | else: |
| 130 | return MMArg(0); |
| 131 | elif arg[:4] == 'imm8': |
| 132 | return ArgImm8u(op); |
| 133 | elif arg[0] == 'r': |
| 134 | if '/m' in arg: |
| 135 | r, m = arg.split('/') |
| 136 | if (m[0] != 'm'): |
| 137 | raise Exception("Expected /m: %s", arg) |
| 138 | mw = int(m[1:]) |
| 139 | if r == 'r': |
| 140 | rw = mw |
| 141 | else: |
| 142 | rw = int(r[1:]) |
| 143 | return ArgRM(rw, mw) |
| 144 | |
| 145 | return ArgRM(int(arg[1:]), 0); |
| 146 | elif arg[0] == 'm': |
| 147 | return ArgMem(int(arg[1:])) |
| 148 | else: |
| 149 | raise SkipInstruction |
| 150 | |
| 151 | class InsnGenerator: |
| 152 | def __init__(self, op, args): |
| 153 | self.op = op |
| 154 | if op[0:2] == "PF": |
| 155 | self.optype = 'F32' |
| 156 | else: |
| 157 | self.optype = 'I' |
| 158 | |
| 159 | try: |
| 160 | self.args = list(ArgGenerator(a, op) for a in args) |
| 161 | if len(self.args) > 0 and self.args[-1] is None: |
| 162 | self.args = self.args[:-1] |
| 163 | except SkipInstruction: |
| 164 | raise |
| 165 | except Exception as e: |
| 166 | raise Exception("Bad arg %s: %s" % (op, e)) |
| 167 | |
| 168 | def gen(self): |
| 169 | regs = (5, 6, 7) |
| 170 | dest = 4 |
| 171 | |
| 172 | nreg = len(self.args) |
| 173 | if nreg == 0: |
| 174 | yield self.op |
| 175 | return |
| 176 | if isinstance(self.args[-1], ArgImm8u): |
| 177 | nreg -= 1 |
| 178 | immarg = self.args[-1] |
| 179 | else: |
| 180 | immarg = None |
| 181 | memarg = -1 |
| 182 | for n, arg in enumerate(self.args): |
| 183 | if arg.ismem: |
| 184 | memarg = n |
| 185 | |
| 186 | if nreg == 1: |
| 187 | regset = [(regs[0],)] |
| 188 | if memarg == 0: |
| 189 | regset += [(-1,)] |
| 190 | elif nreg == 2: |
| 191 | regset = [ |
| 192 | (regs[0], regs[1]), |
| 193 | (regs[0], regs[0]), |
| 194 | ] |
| 195 | if memarg == 0: |
| 196 | regset += [(-1, regs[0])] |
| 197 | elif memarg == 1: |
| 198 | regset += [(dest, -1)] |
| 199 | else: |
| 200 | raise Exception("Too many regs: %s(%d)" % (self.op, nreg)) |
| 201 | |
| 202 | for regv in regset: |
| 203 | argstr = [] |
| 204 | for i in range(nreg): |
| 205 | arg = self.args[i] |
| 206 | argstr.append(arg.regstr(regv[i])) |
| 207 | if immarg is None: |
| 208 | yield self.op + ' ' + ','.join(argstr) |
| 209 | else: |
| 210 | for immval in immarg.vals(): |
| 211 | yield self.op + ' ' + ','.join(argstr) + ',' + str(immval) |
| 212 | |
| 213 | def split0(s): |
| 214 | if s == '': |
| 215 | return [] |
| 216 | return s.split(',') |
| 217 | |
| 218 | def main(): |
| 219 | n = 0 |
| 220 | if len(sys.argv) <= 3: |
| 221 | print("Usage: test-mmx.py x86.csv test-mmx.h CPUID...") |
| 222 | exit(1) |
| 223 | csvfile = open(sys.argv[1], 'r', newline='') |
| 224 | archs = sys.argv[3:] |
| 225 | with open(sys.argv[2], "w") as outf: |
| 226 | outf.write("// Generated by test-mmx.py. Do not edit.\n") |
| 227 | for row in csv.reader(strip_comments(csvfile)): |
| 228 | insn = row[0].replace(',', '').split() |
| 229 | if insn[0] in ignore: |
| 230 | continue |
| 231 | cpuid = row[6] |
| 232 | if cpuid in archs: |
| 233 | try: |
| 234 | g = InsnGenerator(insn[0], insn[1:]) |
| 235 | for insn in g.gen(): |
| 236 | outf.write('TEST(%d, "%s", %s)\n' % (n, insn, g.optype)) |
| 237 | n += 1 |
| 238 | except SkipInstruction: |
| 239 | pass |
| 240 | outf.write("#undef TEST\n") |
| 241 | csvfile.close() |
| 242 | |
| 243 | if __name__ == "__main__": |
| 244 | main() |