331 lines
13 KiB
Python
331 lines
13 KiB
Python
import sys
|
|
import re
|
|
|
|
class Assembly():
|
|
def __init__(self, src_path, dst_path):
|
|
self.src_path = src_path
|
|
self.dst_path = dst_path
|
|
# instructions
|
|
self.ops = ['sdot', 'udot', 'smmla', 'bfmmla', 'mov', 'smopa', 'fmopa', 'luti4', 'ldr']
|
|
|
|
def assembly(self):
|
|
self.dst_content = []
|
|
src = open(self.src_path, 'rt')
|
|
for line in src.readlines():
|
|
code = line
|
|
cmd = code.strip().split(' ')
|
|
for op in self.ops:
|
|
if cmd[0] == op:
|
|
if op == 'mov':
|
|
code = getattr(self, op)(code, cmd[1], cmd[2])
|
|
elif op == 'smopa' or op == 'fmopa' or op == 'luti4':
|
|
inst = getattr(self, op)(code)
|
|
code = code[:code.find(op)] + inst + ' // ' + code.strip(' ')
|
|
elif op == 'ldr':
|
|
if cmd[1] != 'zt0,':
|
|
continue
|
|
inst = getattr(self, op)(code)
|
|
code = code[:code.find(op)] + inst + ' // ' + code.strip(' ')
|
|
else:
|
|
inst = getattr(self, op)(cmd[1], cmd[2], cmd[3])
|
|
code = code[:code.find(op)] + inst + ' // ' + code.strip(' ')
|
|
self.dst_content.append(code)
|
|
src.close()
|
|
self.write()
|
|
|
|
def write(self):
|
|
dst = open(self.dst_path, 'wt')
|
|
dst.writelines(self.dst_content)
|
|
dst.close()
|
|
|
|
# asm parse helper function
|
|
def gen_inst(self, opcode, flag, r1, r2, r3):
|
|
cmd = opcode + r1 + flag + r2 + r3
|
|
inst = '.inst ' + str(hex(int(cmd, 2)))
|
|
return inst
|
|
|
|
def register_to_bin(self, register):
|
|
assert(register[0] == 'v')
|
|
id = str(bin(int(register[1:])))[2:]
|
|
id = '0' * (5 - len(id)) + id
|
|
return id
|
|
|
|
def operand_spilt(self, operand):
|
|
v, t = operand.split('.')
|
|
return self.register_to_bin(v), t
|
|
|
|
def operand_to_bin(self, operand):
|
|
r, _ = self.operand_spilt(operand)
|
|
return r
|
|
|
|
def t_split(self, t):
|
|
idx = None
|
|
if t[-1] == ']':
|
|
t, offset = t[:-1].split('[')
|
|
return t, int(offset)
|
|
|
|
# instruction code gen function
|
|
def sdot(self, operand1, operand2, operand3):
|
|
# SDOT <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tc>[offset]
|
|
Vd, Ta = self.operand_spilt(operand1)
|
|
Vn, Tb = self.operand_spilt(operand2)
|
|
Vm, Tc = self.operand_spilt(operand3)
|
|
if "[" in Tc:
|
|
# other flag:
|
|
# offset = flag[4] * 2 + opcode[-1]
|
|
# dst == '4s' ? opcode[1] = 1 : opcode[1] = 0
|
|
Tc, offset = self.t_split(Tc)
|
|
opcode = list('01001111100')
|
|
flag = list('111000')
|
|
# set Q
|
|
if Ta == '2s' and Tb == '8b':
|
|
opcode[1] = '0'
|
|
# set offset
|
|
if offset == 1 or offset == 3:
|
|
opcode[-1] = '1'
|
|
if offset == 2 or offset == 3:
|
|
flag[4] = '1'
|
|
opcode = ''.join(opcode)
|
|
flag = ''.join(flag)
|
|
return self.gen_inst(opcode, flag, Vm, Vn, Vd)
|
|
else:
|
|
opcode = list('01001110100') # different from the case with offset.
|
|
flag = list('100101')
|
|
# set Q
|
|
if "2s" in Ta and "8b" in Tb:
|
|
opcode[1] = '0'
|
|
opcode = ''.join(opcode)
|
|
flag = ''.join(flag)
|
|
return self.gen_inst(opcode, flag, Vm, Vn, Vd)
|
|
|
|
def udot(self, operand1, operand2, operand3):
|
|
# UDOT <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tc>[offset]
|
|
Vd, Ta = self.operand_spilt(operand1)
|
|
Vn, Tb = self.operand_spilt(operand2)
|
|
Vm, Tc = self.operand_spilt(operand3)
|
|
if "[" in Tc:
|
|
# other flag:
|
|
# offset = flag[4] * 2 + opcode[-1]
|
|
# dst == '4s' ? opcode[1] = 1 : opcode[1] = 0
|
|
Tc, offset = self.t_split(Tc)
|
|
opcode = list('01101111100')
|
|
flag = list('111000')
|
|
# set Q
|
|
if Ta == '2s' and Tb == '8b':
|
|
opcode[1] = '0'
|
|
# set offset
|
|
if offset == 1 or offset == 3:
|
|
opcode[-1] = '1'
|
|
if offset == 2 or offset == 3:
|
|
flag[4] = '1'
|
|
opcode = ''.join(opcode)
|
|
flag = ''.join(flag)
|
|
return self.gen_inst(opcode, flag, Vm, Vn, Vd)
|
|
else:
|
|
opcode = list('01101110100') # different from the case with offset.
|
|
flag = list('100101')
|
|
# set Q
|
|
if "2s" in Ta and "8b" in Tb:
|
|
opcode[1] = '0'
|
|
opcode = ''.join(opcode)
|
|
flag = ''.join(flag)
|
|
return self.gen_inst(opcode, flag, Vm, Vn, Vd)
|
|
|
|
def smmla(self, operand1, operand2, operand3):
|
|
# SMMLA <Vd>.4S, <Vn>.16B, <Vm>.16B
|
|
opcode = '01001110100'
|
|
flag = '101001'
|
|
Vd = self.operand_to_bin(operand1)
|
|
Vn = self.operand_to_bin(operand2)
|
|
Vm = self.operand_to_bin(operand3)
|
|
return self.gen_inst(opcode, flag, Vm, Vn, Vd)
|
|
|
|
def bfmmla(self, operand1, operand2, operand3):
|
|
# BFMMLA <Vd>.4S, <Vn>.8H, <Vm>.8H
|
|
opcode = '01101110010'
|
|
flag = '111011'
|
|
Vd = self.operand_to_bin(operand1)
|
|
Vn = self.operand_to_bin(operand2)
|
|
Vm = self.operand_to_bin(operand3)
|
|
return self.gen_inst(opcode, flag, Vm, Vn, Vd)
|
|
|
|
def mov(self, code, operand1, operand2):
|
|
# compile failed using `mov v1.8h, v2.8h`
|
|
# change to `mov v1.16b, v2.16b`
|
|
if '.8h' not in operand1 or '.8h' not in operand2:
|
|
return code
|
|
operand1 = operand1.replace('8h', '16b')
|
|
operand2 = operand2.replace('8h', '16b')
|
|
new_mov = f'mov {operand1} {operand2}'
|
|
new_code = code[:code.find('mov')] + new_mov + ' // ' + code.strip(' ')
|
|
return new_code
|
|
|
|
def smopa(self, instruction):
|
|
"""
|
|
SMOPA <ZAda>.S, <Pn>/M, <Pm>/M, <Zn>.B, <Zm>.B 32bit 4-way
|
|
SMOPA <ZAda>.D, <Pn>/M, <Pm>/M, <Zn>.H, <Zm>.H 64bit 4-way
|
|
"""
|
|
try:
|
|
parts = instruction.replace(' ', '').split(',')
|
|
if len(parts) != 5:
|
|
raise ValueError("smopa 指令格式错误")
|
|
|
|
zda = int(parts[0].split('za')[1].split('.')[0])
|
|
pn = int(parts[1].split('p')[1].split('/')[0])
|
|
pm = int(parts[2].split('p')[1].split('/')[0])
|
|
zn = int(parts[3].split('z')[1].split('.')[0])
|
|
zm = int(parts[4].split('z')[1].split('.')[0])
|
|
|
|
zmDataType = parts[4].split('z')[1].split('.')[1][0]
|
|
|
|
if not (0 <= zda <= 15):
|
|
raise ValueError("zda必须在0-15范围内")
|
|
if not (0 <= pn <= 7):
|
|
raise ValueError("pg必须在0-7范围内")
|
|
if not (0 <= pn <= 7):
|
|
raise ValueError("pn必须在0-7范围内")
|
|
if not (0 <= zm <= 31):
|
|
raise ValueError("zm必须在0-31范围内")
|
|
if not (0 <= zn <= 31):
|
|
raise ValueError("zn必须在0-31范围内")
|
|
|
|
# smopa za0.s, p3/m, p4/m, z0.b, z1.b
|
|
is32Bit4way = (parts[0].split('za')[1].split('.')[1] == "s") and (parts[3].split('z')[1].split('.')[1] == 'b') and (zmDataType == 'b')
|
|
# smopa za0.d, p3/m, p4/m, z0.h, z1.h
|
|
is64Bit4way = (parts[0].split('za')[1].split('.')[1] == "d") and (parts[3].split('z')[1].split('.')[1] == 'h') and (zmDataType == 'h')
|
|
# smopa za0.s, p3/m, p4/m, z0.h, z1.h
|
|
is2way = (parts[0].split('za')[1].split('.')[1] == "s") and (parts[3].split('z')[1].split('.')[1] == 'h') and (zmDataType == 'h')
|
|
if (is32Bit4way == False) and (is64Bit4way == False) and (is2way):
|
|
raise ValueError("smopa 指令格式错误")
|
|
|
|
# is32Bit4way
|
|
opcode = "10100000100" #[31, 21]
|
|
zmCode = format(zm, '05b') # zm register has '5' bit,[20, 16]
|
|
pmCode = format(pm, '03b') # pm register has '3' bit,[15,13]
|
|
pnCode = format(pn, '03b') # pn register has '3' bit,[12,10]
|
|
znCode = format(zn, '05b') # zn register has '5' bit, [9,5]
|
|
fixCode = "000" # fixed encode
|
|
zaCode = format(zda, '02b') # za register has '2' bit, [1,0]
|
|
|
|
if is64Bit4way == True:
|
|
opcode = "10100000110"
|
|
fixCode = "00"
|
|
zaCode = format(zda, '03b')
|
|
elif is2way == True:
|
|
opcode = "10100000100"
|
|
fixCode = "010"
|
|
|
|
# concact
|
|
binary = opcode + zmCode + pmCode + pnCode + znCode + fixCode + zaCode
|
|
inst = '.inst ' + str(hex(int(binary, 2)))
|
|
return inst
|
|
|
|
except Exception as e:
|
|
raise ValueError(f"smopa 指令解析错误: {str(e)}")
|
|
|
|
def fmopa(self, instruction):
|
|
'''
|
|
FMOPA <ZAda>.S, <Pn>/M, <Pm>/M, <Zn>.S, <Zm>.S
|
|
'''
|
|
try:
|
|
parts = instruction.replace(' ', '').split(',')
|
|
if len(parts) != 5:
|
|
raise ValueError("fmopa 指令格式错误")
|
|
|
|
zda = int(parts[0].split('za')[1].split('.')[0])
|
|
pn = int(parts[1].split('p')[1].split('/')[0])
|
|
pm = int(parts[2].split('p')[1].split('/')[0])
|
|
zn = int(parts[3].split('z')[1].split('.')[0])
|
|
zm = int(parts[4].split('z')[1].split('.')[0])
|
|
|
|
zmDataType = parts[4].split('z')[1].split('.')[1][0]
|
|
|
|
# fmopa za0.s, p3/m, p4/m, z0.s, z1.s
|
|
singlePrecisionNotWidening = (parts[0].split('za')[1].split('.')[1] == "s") and (parts[3].split('z')[1].split('.')[1] == 's') and (zmDataType == 's')
|
|
# fmopa za0.s, p3/m, p3/m, z0.h, z1.h
|
|
fp16Tofp32 = (parts[0].split('za')[1].split('.')[1] == "s") and (parts[3].split('z')[1].split('.')[1] == 'h') and (zmDataType == 'h')
|
|
|
|
if not singlePrecisionNotWidening and not fp16Tofp32:
|
|
raise ValueError("Not implement yet\n")
|
|
|
|
opcode = "10000000100"
|
|
zmCode = format(zm, '05b') # zm register has '5' bit,[20, 16]
|
|
pmCode = format(pm, '03b') # pm register has '3' bit,[15,13]
|
|
pnCode = format(pn, '03b') # pn register has '3' bit,[12,10]
|
|
znCode = format(zn, '05b') # zn register has '5' bit, [9,5]
|
|
fixCode = "000" # fixed encode
|
|
zaCode = format(zda, '02b') # za register has '2' bit, [1,0]
|
|
|
|
if fp16Tofp32 == True:
|
|
opcode = "10000001101"
|
|
|
|
binary = opcode + zmCode + pmCode + pnCode + znCode + fixCode + zaCode
|
|
inst = '.inst ' + str(hex(int(binary, 2)))
|
|
return inst
|
|
|
|
except Exception as e:
|
|
raise ValueError(f"fmopa 指令解析错误: {str(e)}")
|
|
|
|
def luti4(self, instruction):
|
|
'''
|
|
luti4 {z2.b-z3.b}, zt0, z1[0]
|
|
'''
|
|
try:
|
|
parts = instruction.replace(' ', '').split(',')
|
|
if len(parts) != 3:
|
|
raise ValueError("luti4 指令格式错误")
|
|
|
|
# 解析目标寄存器
|
|
zd = int(parts[0].split('z')[1].split('.')[0])
|
|
T = parts[0].split('.')[1].split('.')[0][0]
|
|
if T != 'b':
|
|
raise ValueError("Not implement yet\n")
|
|
|
|
# 解析查找表寄存器
|
|
zt = int(parts[1].split('zt')[1])
|
|
|
|
# 解析源寄存器
|
|
zn = int(parts[2].split('z')[1].split('[')[0])
|
|
i2 = int(parts[2].split('z')[1].split('[')[1][0])
|
|
|
|
opcode = "110000001000101"
|
|
i2code = format(i2, '02b')
|
|
constcode0 = "1"
|
|
sizecode = "00" # b
|
|
constcode1 = "00"
|
|
zncode = format(zn, '05b')
|
|
zdcode = format(zd, '05b')
|
|
|
|
binary = opcode + i2code + constcode0 + sizecode + constcode1 + zncode + zdcode
|
|
inst = '.inst ' + str(hex(int(binary, 2)))
|
|
return inst
|
|
|
|
except Exception as e:
|
|
raise ValueError(f"luti4 指令解析错误: {str(e)}")
|
|
|
|
def ldr(self, instruction):
|
|
'''
|
|
.inst 0xe11f8100 // ldr zt0, [x8]
|
|
'''
|
|
i0 = instruction.find('[')
|
|
i1 = instruction.find(']')
|
|
x = int(instruction[i0 + 2: i1])
|
|
opcode = "1110000100011111100000"
|
|
rn = format(x, '05b')
|
|
fixcode = "00000"
|
|
binary = opcode + rn + fixcode
|
|
inst = '.inst ' + str(hex(int(binary, 2)))
|
|
return inst
|
|
|
|
|
|
if __name__ == '__main__':
|
|
if len(sys.argv) < 2:
|
|
print('Usage: python arm_asselmbly.py src.asm [dst.asm]')
|
|
src_file = sys.argv[1]
|
|
if len(sys.argv) > 2:
|
|
dst_file = sys.argv[2]
|
|
else:
|
|
dst_file = src_file
|
|
a = Assembly(src_file, dst_file)
|
|
a.assembly()
|