add new (experimental) ffmadds and ffmsubs, for FFT twin mul-accumulate
[openpower-isa.git] / src / openpower / sv / trans / svp64.py
index ee3bcb05d88cf636824c0bff24cac89a29191bc9..64888594edca1f737010a5f7faa7335f03083a82 100644 (file)
@@ -4,13 +4,14 @@
 
 """SVP64 OpenPOWER v3.0B assembly translator
 
-This class takes raw svp64 assembly mnemonics (aliases excluded) and
-creates an EXT001-encoded "svp64 prefix" followed by a v3.0B opcode.
+This class takes raw svp64 assembly mnemonics (aliases excluded) and creates
+an EXT001-encoded "svp64 prefix" (as a .long) followed by a v3.0B opcode.
 
 It is very simple and straightforward, the only weirdness being the
 extraction of the register information and conversion to v3.0B numbering.
 
 Encoding format of svp64: https://libre-soc.org/openpower/sv/svp64/
+Encoding format of LDST: https://libre-soc.org/openpower/sv/ldst/
 Bugtracker: https://bugs.libre-soc.org/show_bug.cgi?id=578
 """
 
@@ -144,9 +145,13 @@ def decode_imm(field):
     else:
         return None, 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
@@ -156,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
@@ -164,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."]:
@@ -197,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))
@@ -240,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
@@ -265,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] == ')'
@@ -379,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
@@ -406,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(
@@ -416,6 +479,30 @@ class SVP64Asm:
                 svp64_rm.extra3[idx].eq(
                     SelectableInt(sv_extra, SVP64RM_EXTRA3_SPEC_SIZE))
 
+        # identify if the op is a LD/ST. the "blegh" way. copied
+        # from power_enums.  TODO, split the list _insns down.
+        is_ld = v30b_op in [
+        "lbarx", "lbz", "lbzu", "lbzux", "lbzx",            # load byte
+        "ld", "ldarx", "ldbrx", "ldu", "ldux", "ldx",       # load double
+        "lfs", "lfsx", "lfsu", "lfsux",                     # FP load single
+        "lfd", "lfdx", "lfdu", "lfdux", "lfiwzx", "lfiwax", # FP load double
+        "lha", "lharx", "lhau", "lhaux", "lhax",            # load half
+        "lhbrx", "lhz", "lhzu", "lhzux", "lhzx",            # more load half
+        "lwa", "lwarx", "lwaux", "lwax", "lwbrx",           # load word
+        "lwz", "lwzcix", "lwzu", "lwzux", "lwzx",           # more load word
+        ]
+        is_st = v30b_op in [
+        "stb", "stbcix", "stbcx", "stbu", "stbux", "stbx",
+        "std", "stdbrx", "stdcx", "stdu", "stdux", "stdx",
+        "stfs", "stfsx", "stfsu", "stfux",                  # FP store single
+        "stfd", "stfdx", "stfdu", "stfdux", "stfiwx",       # FP store double
+        "sth", "sthbrx", "sthcx", "sthu", "sthux", "sthx",
+        "stw", "stwbrx", "stwcx", "stwu", "stwux", "stwx",
+        ]
+        # use this to determine if the SVP64 RM format is different.
+        # see https://libre-soc.org/openpower/sv/ldst/
+        is_ldst = is_ld or is_st
+
         # parts of svp64_rm
         mmode = 0  # bit 0
         pmask = 0  # bits 1-3
@@ -435,11 +522,13 @@ class SVP64Asm:
         sv_mode = None
 
         mapreduce = False
+        reverse_gear = False
         mapreduce_crm = False
         mapreduce_svm = False
 
         predresult = False
         failfirst = False
+        ldst_elstride = 0
 
         # ok let's start identifying opcode augmentation fields
         for encmode in opmodes:
@@ -462,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:])
@@ -470,6 +562,9 @@ class SVP64Asm:
                 destwid = decode_elwidth(encmode[3:])
             elif encmode.startswith("sw="):
                 srcwid = decode_elwidth(encmode[3:])
+            # element-strided LD/ST
+            elif encmode == 'els':
+                ldst_elstride = 1
             # saturation
             elif encmode == 'sats':
                 assert sv_mode is None
@@ -494,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
@@ -539,7 +640,6 @@ class SVP64Asm:
                 "dest-mask can only be specified on Twin-predicate ops"
 
         # construct the mode field, doing sanity-checking along the way
-
         if mapreduce_svm:
             assert sv_mode == 0b00, "sub-vector mode in mapreduce only"
             assert subvl != 0, "sub-vector mode not possible on SUBVL=1"
@@ -551,16 +651,36 @@ 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:
             mode |= src_zero << SVP64MODE.SZ # predicate zeroing
             mode |= dst_zero << SVP64MODE.DZ # predicate zeroing
+            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
+                # 00  1   SVM CRM subvector reduce mode, SUBVL>1
+                pass
             sv_mode = 0b00
 
+        ######################################
         # "mapreduce" modes
         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"
@@ -571,6 +691,7 @@ class SVP64Asm:
             elif mapreduce_svm:
                 mode |= (0b1<<SVP64MODE.SVM) # sets SVM mode
 
+        ######################################
         # "failfirst" modes
         elif sv_mode == 0b01:
             assert src_zero == 0, "dest-zero not allowed in failfirst mode"
@@ -588,12 +709,14 @@ class SVP64Asm:
                 assert rc_mode, "ffirst BO only possible when Rc=1"
                 mode |= (failfirst << SVP64MODE.BO_LSB) # set BO
 
+        ######################################
         # "saturation" modes
         elif sv_mode == 0b10:
             mode |= src_zero << SVP64MODE.SZ # predicate zeroing
             mode |= dst_zero << SVP64MODE.DZ # predicate zeroing
             mode |= (saturation << SVP64MODE.N) # signed/unsigned saturation
 
+        ######################################
         # "predicate-result" modes.  err... code-duplication from ffirst
         elif sv_mode == 0b11:
             assert src_zero == 0, "dest-zero not allowed in predresult mode"
@@ -648,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
@@ -666,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):
@@ -674,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
@@ -681,6 +866,8 @@ def asm_process():
     if len(args) == 0:
         infile = sys.stdin
         outfile = sys.stdout
+        # read the whole lot in advance in case of in-place
+        lines = list(infile.readlines())
     elif len(args) != 2:
         print ("pysvp64asm [infile | -] [outfile | -]")
         exit(0)
@@ -689,15 +876,30 @@ def asm_process():
             infile = sys.stdin
         else:
             infile = open(args[0], "r")
+        # read the whole lot in advance in case of in-place overwrite
+        lines = list(infile.readlines())
+
         if args[1] == '--':
             outfile = sys.stdout
         else:
             outfile = open(args[1], "w")
 
     # read the line, look for "sv", process it
+    macros = {} # macros which start ".set"
     isa = SVP64Asm([])
-    for line in infile.readlines():
+    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
@@ -705,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))
 
@@ -744,7 +940,21 @@ if __name__ == '__main__':
     lst = [
             "sv.stfsu 0.v, 16(4.v)",
     ]
-    isa = SVP64Asm(lst)
+    lst = [
+            "sv.stfsu/els 0.v, 16(4)",
+    ]
+    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()
+    #asm_process()