"""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
"""
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
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
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."]:
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))
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
# 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] == ')'
(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
# 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(
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
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:
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:])
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
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
"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"
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"
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"
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"
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
# 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):
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
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)
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
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))
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()