add new (experimental) ffmadds and ffmsubs, for FFT twin mul-accumulate
[openpower-isa.git] / src / openpower / sv / trans / svp64.py
index 00a57e827c0e3d5bb54c3d75a1599ac1d88dd492..64888594edca1f737010a5f7faa7335f03083a82 100644 (file)
@@ -148,7 +148,10 @@ def decode_imm(field):
 
 # decodes svp64 assembly listings and creates EXT001 svp64 prefixes
 class SVP64Asm:
-    def __init__(self, lst, bigendian=False):
+    def __init__(self, lst, bigendian=False, macros=None):
+        if macros is None:
+            macros = {}
+        self.macros = macros
         self.lst = lst
         self.trans = self.translate(lst)
         self.isa = ISA() # reads the v3.0B pseudo-code markdown files
@@ -158,7 +161,10 @@ class SVP64Asm:
     def __iter__(self):
         yield from self.trans
 
-    def translate_one(self, insn):
+    def translate_one(self, insn, macros=None):
+        if macros is None:
+            macros = {}
+        macros.update(self.macros)
         isa = self.isa
         svp64 = self.svp64
         # find first space, to get opcode
@@ -166,8 +172,13 @@ class SVP64Asm:
         opcode = ls[0]
         # now find opcode fields
         fields = ''.join(ls[1:]).split(',')
-        fields = list(map(str.strip, fields))
-        log ("opcode, fields", ls, opcode, fields)
+        mfields = list(map(str.strip, fields))
+        log ("opcode, fields", ls, opcode, mfields)
+        fields = []
+        # macro substitution
+        for field in mfields:
+            fields.append(macro_subst(macros, field))
+        log ("opcode, fields substed", ls, opcode, fields)
 
         # sigh have to do setvl here manually for now...
         if opcode in ["setvl", "setvl."]:
@@ -199,9 +210,51 @@ class SVP64Asm:
         if rc_mode:
             v30b_op = v30b_op[:-1]
 
+        # sigh again, have to recognised LD/ST bit-reverse instructions
+        # this has to be "processed" to fit into a v3.0B without the "br"
+        # e.g. ldbr is actually ld
+        ldst_bitreverse = v30b_op.startswith("l") and v30b_op.endswith("br")
+
         if v30b_op not in isa.instr:
             raise Exception("opcode %s of '%s' not supported" % \
                             (v30b_op, insn))
+
+        if ldst_bitreverse:
+            # okaay we need to process the fields and make this:
+            #     ldbr RT, SVD(RA), RC  - 11 bits for SVD, 5 for RC
+            # into this:
+            #     ld RT, D(RA)          - 16 bits
+            # likewise same for SVDS (9 bits for SVDS, 5 for RC, 14 bits for DS)
+            form = isa.instr[v30b_op].form # get form (SVD-Form, SVDS-Form)
+
+            newfields = []
+            for field in fields:
+                # identify if this is a ld/st immediate(reg) thing
+                ldst_imm = "(" in field and field[-1] == ')'
+                if ldst_imm:
+                    newfields.append(field[:-1].split("("))
+                else:
+                    newfields.append(field)
+
+            immed, RA = newfields[1]
+            immed = int(immed)
+            RC = int(newfields.pop(2)) # better be an integer number!
+            if form == 'SVD': # 16 bit: immed 11 bits, RC shift up 11
+                immed = (immed & 0b11111111111) | (RC<<11)
+                if immed & (1<<15): # should be negative
+                    immed -= 1<<16
+            if form == 'SVDS': # 14 bit: immed 9 bits, RC shift up 9
+                immed = (immed & 0b111111111) | (RC<<9)
+                if immed & (1<<13): # should be negative
+                    immed -= 1<<14
+            newfields[1] = "%d(%s)" % (immed, RA)
+            fields = newfields
+
+            # and strip off "br" from end, and add "br" to opmodes, instead
+            v30b_op = v30b_op[:-2]
+            opmodes.append("br")
+            log ("rewritten", v30b_op, opmodes, fields)
+
         if v30b_op not in svp64.instrs:
             raise Exception("opcode %s of '%s' not an svp64 instruction" % \
                             (v30b_op, insn))
@@ -242,7 +295,11 @@ class SVP64Asm:
         for idx, (field, regname) in enumerate(opregfields):
             imm, regname = decode_imm(regname)
             rtype = get_regtype(regname)
-            log ("    idx find", idx, field, regname, imm)
+            log ("    idx find", rtype, idx, field, regname, imm)
+            if rtype is None:
+                # probably an immediate field, append it straight
+                extras[('imm', idx, False)] = (idx, field, None, None, None)
+                continue
             extra = svp64_src.get(regname, None)
             if extra is not None:
                 extra = ('s', extra, False) # not a duplicate
@@ -267,6 +324,7 @@ class SVP64Asm:
             # into newfields
             if rtype is None:
                 v30b_newfields.append(field)
+                continue
 
             # identify if this is a ld/st immediate(reg) thing
             ldst_imm = "(" in field and field[-1] == ')'
@@ -381,10 +439,12 @@ class SVP64Asm:
                                 (rname, str(extras[extra_idx]))
                         # all good: encode as vector (bit 3 set)
                         sv_extra = 0b100 | (sv_extra >> 2)
-
                 # reconstruct the actual 5-bit CR field
                 field = (field << 2) | cr_subfield
 
+            else:
+                print ("no type match", rtype)
+
             # capture the extra field info
             log ("=>", "%5s" % bin(sv_extra), field)
             extras[extra_idx] = sv_extra
@@ -408,8 +468,9 @@ class SVP64Asm:
 
         # begin with EXTRA fields
         for idx, sv_extra in extras.items():
-            if idx is None: continue
             log (idx)
+            if idx is None: continue
+            if idx[0] == 'imm': continue
             srcdest, idx, duplicate = idx
             if etype == 'EXTRA2':
                 svp64_rm.extra2[idx].eq(
@@ -461,6 +522,7 @@ class SVP64Asm:
         sv_mode = None
 
         mapreduce = False
+        reverse_gear = False
         mapreduce_crm = False
         mapreduce_svm = False
 
@@ -489,6 +551,9 @@ class SVP64Asm:
                 smmode, smask = decode_predicate(encmode[3:])
                 mmode = smmode
                 has_smask = True
+            # bitreverse LD/ST
+            elif encmode.startswith("br"):
+                ldst_bitreverse = True
             # vec2/3/4
             elif encmode.startswith("vec"):
                 subvl = decode_subvl(encmode[3:])
@@ -524,6 +589,12 @@ class SVP64Asm:
                 assert sv_mode is None
                 sv_mode = 0b11
                 predresult = decode_ffirst(encmode[3:])
+            # map-reduce mode, reverse-gear
+            elif encmode == 'mrr':
+                assert sv_mode is None
+                sv_mode = 0b00
+                mapreduce = True
+                reverse_gear = True
             # map-reduce mode
             elif encmode == 'mr':
                 assert sv_mode is None
@@ -580,6 +651,11 @@ class SVP64Asm:
             assert has_pmask or mask_m_specified, \
                 "dest zeroing requires a dest predicate"
 
+        # check LDST bitreverse, only available in "normal" mode
+        if is_ldst and ldst_bitreverse:
+            assert sv_mode is None, \
+                "LD bit-reverse cannot have modes (%s) applied" % sv_mode
+
         ######################################
         # "normal" mode
         if sv_mode is None:
@@ -588,6 +664,9 @@ class SVP64Asm:
             if is_ldst:
                 # TODO: for now, LD/ST-indexed is ignored.
                 mode |= ldst_elstride << SVP64MODE.ELS_NORMAL # element-strided
+                # bitreverse mode
+                if ldst_bitreverse:
+                    mode |= 1 << SVP64MODE.LDST_BITREV
             else:
                 # TODO, reduce and subvector mode
                 # 00  1   dz CRM  reduce mode (mapreduce), SUBVL=1
@@ -600,6 +679,8 @@ class SVP64Asm:
         elif sv_mode == 0b00:
             mode |= (0b1<<SVP64MODE.REDUCE) # sets mapreduce
             assert dst_zero == 0, "dest-zero not allowed in mapreduce mode"
+            if reverse_gear:
+                mode |= (0b1<<SVP64MODE.RG) # sets Reverse-gear mode
             if mapreduce_crm:
                 mode |= (0b1<<SVP64MODE.CRM) # sets CRM mode
                 assert rc_mode, "CRM only allowed when Rc=1"
@@ -690,6 +771,7 @@ class SVP64Asm:
         offs = 2 if etype == 'EXTRA2' else 3 # 2 or 3 bits
         for idx, sv_extra in extras.items():
             if idx is None: continue
+            if idx[0] == 'imm': continue
             srcdest, idx, duplicate = idx
             start = (10+idx*offs)
             end = start + offs-1
@@ -708,7 +790,20 @@ class SVP64Asm:
         # fiinally yield the svp64 prefix and the thingy.  v3.0b opcode
         rc = '.' if rc_mode else ''
         yield ".long 0x%x" % svp64_prefix.insn.value
-        yield "%s %s" % (v30b_op+rc, ", ".join(v30b_newfields))
+        log(v30b_newfields)
+        # argh, sv.fmaddso etc. need to be done manually
+        if v30b_op == 'ffmadds':
+            opcode = 59 << (32-6)    # bits 0..6 (MSB0)
+            opcode |= int(v30b_newfields[0]) << (32-11) # FRT
+            opcode |= int(v30b_newfields[1]) << (32-16) # FRA
+            opcode |= int(v30b_newfields[2]) << (32-21) # FRB
+            opcode |= int(v30b_newfields[3]) << (32-26) # FRC
+            opcode |= 5 << (32-31)   # bits 26-30
+            if rc:
+                opcode |= 1  # Rc, bit 31.
+            yield ".long 0x%x" % opcode
+        else:
+            yield "%s %s" % (v30b_op+rc, ", ".join(v30b_newfields))
         log ("new v3.0B fields", v30b_op, v30b_newfields)
 
     def translate(self, lst):
@@ -716,6 +811,54 @@ class SVP64Asm:
             yield from self.translate_one(insn)
 
 
+def macro_subst(macros, txt):
+    again = True
+    print ("subst", txt, macros)
+    while again:
+        again = False
+        for macro, value in macros.items():
+            if macro == txt:
+                again = True
+                replaced = txt.replace(macro, value)
+                print ("macro", txt, "replaced", replaced, macro, value)
+                txt = replaced
+                continue
+            toreplace = '%s.s' % macro
+            if toreplace == txt:
+                again = True
+                replaced = txt.replace(toreplace, "%s.s" % value)
+                print ("macro", txt, "replaced", replaced, toreplace, value)
+                txt = replaced
+                continue
+            toreplace = '%s.v' % macro
+            if toreplace == txt:
+                again = True
+                replaced = txt.replace(toreplace, "%s.v" % value)
+                print ("macro", txt, "replaced", replaced, toreplace, value)
+                txt = replaced
+                continue
+            toreplace = '(%s)' % macro
+            if toreplace in txt:
+                again = True
+                replaced = txt.replace(toreplace, '(%s)' % value)
+                print ("macro", txt, "replaced", replaced, toreplace, value)
+                txt = replaced
+                continue
+    print ("    processed", txt)
+    return txt
+
+
+def get_ws(line):
+    # find whitespace
+    ws = ''
+    while line:
+        if not line[0].isspace():
+            break
+        ws += line[0]
+        line = line[1:]
+    return ws, line
+
+
 def asm_process():
 
     # get an input file and an output file
@@ -742,9 +885,21 @@ def asm_process():
             outfile = open(args[1], "w")
 
     # read the line, look for "sv", process it
+    macros = {} # macros which start ".set"
     isa = SVP64Asm([])
     for line in lines:
         ls = line.split("#")
+        # identify macros
+        if ls[0].strip().startswith("setvl"):
+            ws, line = get_ws(ls[0])
+            lst = list(isa.translate_one(ls[0].strip(), macros))
+            lst = '; '.join(lst)
+            outfile.write("%s%s # %s\n" % (ws, lst, ls[0]))
+            continue
+        if ls[0].startswith(".set"):
+            macro = ls[0][4:].split(",")
+            macro, value = list(map(str.strip, macro))
+            macros[macro] = value
         if len(ls) != 2:
             outfile.write(line)
             continue
@@ -752,16 +907,10 @@ def asm_process():
         if not potential.startswith("sv."):
             outfile.write(line)
             continue
-        # find whitespace
-        ws = ''
-        while line:
-            if not line[0].isspace():
-                break
-            ws += line[0]
-            line = line[1:]
 
+        ws, line = get_ws(line)
         # SV line indentified
-        lst = list(isa.translate_one(potential))
+        lst = list(isa.translate_one(potential, macros))
         lst = '; '.join(lst)
         outfile.write("%s%s # %s\n" % (ws, lst, potential))
 
@@ -794,7 +943,18 @@ if __name__ == '__main__':
     lst = [
             "sv.stfsu/els 0.v, 16(4)",
     ]
-    isa = SVP64Asm(lst)
+    lst = [
+             'sv.add./mr 5.v, 2.v, 1.v',
+    ]
+    macros = {'win2': '50', 'win': '60'}
+    lst = [
+             'sv.addi win2.v, win.v, -1',
+             'sv.add./mrr 5.v, 2.v, 1.v',
+             #'sv.lhzbr 5.v, 11(9.v), 15',
+             #'sv.lwzbr 5.v, 11(9.v), 15',
+             'sv.ffmadds 6.v, 2.v, 4.v, 6.v',
+    ]
+    isa = SVP64Asm(lst, macros=macros)
     print ("list", list(isa))
     csvs = SVP64RM()
     #asm_process()