Commit 6e8a1f2f authored by Lyude Paul's avatar Lyude Paul

wip bf assembler

parent 65e5f1b6
#!/usr/bin/python3
"""
Copyright ©2018 Lyude Paul
Copyright ©2018 The Panfrost community
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
import enum
import struct
import re
import argparse
from abc import ABC
try:
import bitarray
except e:
from sys import stderr
print("The bitarray module for python3 is needed to run this!", file=stderr)
import sys
sys.exit(-1)
class ParsingException(Exception):
pass
class Parser:
INLINE_COMMENT = re.compile(r'/\*[^\(\*/\)]*\*/')
def __init__(self, asm_file):
self._file = asm_file
@property
def lines(self):
for i, s in enumerate(self._file.readlines()):
if s.strip().startswith('#'):
continue
# Strip in-line comments /* like this */
s = self.INLINE_COMMENT.sub('', s).rstrip()
if s != '':
yield (i, s)
CLAUSE_START = re.compile(r'^clause_([0-9]+):$')
def scan_clause_start(string):
string = string.strip()
try:
return int(CLAUSE_START.match(string).group(1))
except Exception:
raise ParsingException('%s is not a valid start to a clause' % string)
CLAUSE_HEADER_TOKEN = re.compile(r'([^\s(]+)(?:\(([^(]+)\))?')
def scan_clause_header_tokens(string):
string = string.strip()
matches = list(CLAUSE_HEADER_TOKEN.finditer(string, 0))
# Make sure the line didn't have any invalid non-tokens on it
if (matches[-1].end()) != len(string):
raise ParsingException('Invalid clause header: %s' % string)
ret = dict()
for m in matches:
key, val = m.groups()
ret[key] = val
return ret
OP_TOKEN = re.compile(r'^(\S+)\s+((?:\{[^}]+\})|\S+),\s*(.*)$')
DST_WITH_OUT = re.compile(r'^\{(\S+)\s*,\s*(T[01])\}$')
DST_NO_OUT = re.compile(r'^T[01]$')
def scan_op_tokens(string):
try:
inst, dst, args = OP_TOKEN.match(string).groups()
except Exception:
raise ParsingException('%s is not in a valid instruction format' % string)
# Extract inst name and modifiers
inst_parts = inst.split('.', maxsplit=2)
if len(inst_parts) > 1:
inst = (inst_parts[0], inst_parts[1].split('.'))
else:
inst = (inst_parts[0], [])
# Scan dst tokens
match = DST_WITH_OUT.match(dst)
if match:
dst = match.groups()
elif DST_NO_OUT.match(dst):
dst = (None, dst)
else:
raise ParsingException('In %s: %s is not a valid dst argument' % (
string, dst))
# Split args
try:
args = [a.strip() for a in args.split(',')]
except Exception:
raise ParsingException('In %s: %s is not a valid set of arguments' % (
string, args))
return inst, dst, args
# TODO: Each inst class should handle the base instruction + modifiers
class OpParser(ABC):
def __init__(self, modifiers, dst, srcs):
self.modifiers = modifiers
self.dst = dst
self.srcs = srcs
@abstract
def parse(self):
raise NotImplemented()
@abstract
def encode(self):
def ModifierException(Exception):
def __init__(self, mods):
super().__init__('Invalid modifier(s): %s' % str(mods))
def ModifierCountException(Exception):
def __init__(self):
super().__init__('Too many modifiers given')
def check_src_cnt(self, want):
got = len(self.srcs)
if got != want:
raise ParsingException('Invalid src count, expected %d but got %d' % (
got, want))
def check_no_mods(self):
if self.modifiers:
raise ParsingException('Instruction does not take modifiers')
def check_mod_opcode_map(self, modifier):
try:
self.opcode = self.MOD_TO_OPCODE[modifier]
except KeyError:
raise ModifierException(modifier)
# Base Parsers/encoders for FMA operations
class fma:
class OneSrcOpParser(OpParser):
def parse(self):
self.check_src_cnt(1)
def encode(self):
ret = self.op_code;
class TwoSrcOpParser(OpParser):
def parse(self):
self.check_src_cnt(2)
class TwoSrcFmodOpParser(fma.TwoSrcOpParser):
def parse(self):
raise NotImplemented()
class FCmpOpParser(OpParser):
def parse(self):
raise NotImplemented()
class ThreeSrcOpParser(OpParser):
def parse(self):
self.check_src_cnt(3)
class ThreeSrcFmodOpParser(fma.ThreeSrcOpParser):
def parse(self):
raise NotImplemented()
class FourSrcOpParser(OpParser):
def parse(self):
self.check_src_cnt(4)
# Instruction parsers
class NopOp(fma.OneSrcOpParser):
op_code = 0xe032c
def parse(self):
super().parse()
self.check_no_mods()
class MovOp(fma.OneSrcOpParser):
op_code = 0xe032d
def parse(self):
super().parse()
self.check_no_mods()
class CselOp(OpParser):
OPCODE_MAP = {
'f32': {
"FEQ": 0x5c000,
"FGT": 0x5c200,
"FGE": 0x5c400,
"IEQ": 0x5c600,
},
'i32': {
"IGT": 0x5c800,
"IGE": 0x5ca00,
"UGT": 0x5cc00,
"UGE": 0x5ce00,
},
'v2f16': {
"FEQ": 0xdc000,
"FGT": 0xdc200,
"FGE": 0xdc400,
"IEQ": 0xdc600,
},
'v2i16': {
"IGT": 0xdc800,
"IGE": 0xdca00,
"UGT": 0xdcc00,
"UGE": 0xdce00,
},
}
def parse(self):
super().parse()
if not self.modifiers:
self.check_src_cnt(3)
self.op_code = 0xe0f40
elif len(modifiers) == 2:
self.check_src_cnt(4)
try:
self.op_code = self.OPCODE_MAP[modifier[1]][modifier[0]]
except KeyError:
raise self.ModifierException(self.modifiers)
else:
raise self.ModifierCountException(2)
# TODO wip: finish these when we need them, but focus on getting parsers
# for simple ops first
# class FMAOp(fma.ThreeSrcFmodOpParser):
# MOD_TO_OPCODE = {
# 'f32' : 0x00000,
# 'v2f16' : 0x80000
# }
# def __init__(self, modifiers, dst, src):
# super().__init__(modifiers, dst, src)
# if len(modifiers) != 1:
# raise OpParser.ModifierCountException(1)
# self.check_mod_opcode_map(modifiers[0])
# class MAXOp(fma.ThreeSrcOpParser):
parser = argparse.ArgumentParser('Bifrost Assembler')
parser.add_argument('file', type=argparse.FileType())
args = parser.parse_args()
COOKIE = re.compile(r'^([A-Z0-9_]{4}|\t+.*)$')
# COOKIE = re.compile(r'^(\s+.*|[A-Z0-9]{4})$')
# FIXME Do we even need this to be an object?
p = Parser(args.file)
itr = iter(p.lines)
try:
unexpected_eof = False
for i, l in itr:
# We don't parse cookies
while COOKIE.match(l):
i, l = next(itr)
clause_id = scan_clause_start(l)
i, l = next(itr)
header_tokens = scan_clause_header_tokens(l)
print(header_tokens)
unexpected_eof = True
i, l = next(itr)
if l != '{':
raise ParsingException('Unexpected line after clause header: ' + l)
print('Clause #%s contents:' % clause_id)
for i, l in itr:
if l == '}':
print('End clause')
break
print(scan_op_tokens(l))
unexpected_eof = False
except StopIteration:
pass
except Exception as e:
unexpected_eof = False
raise e
finally:
if unexpected_eof:
raise ParsingException('Unexpected EOF')
# class Clause:
# class Header
# unk: 18
# register: 6
# scoreboard deps: 8
# scoreboard entry: 3
# instruction type: 4
# unk: 1
# next clause instr type: 4
# unk: 1
# class FMAOneSrc():
# def __init__(self, src0, opcode):
# self.src0 = src0
# self.opcode = opcode
# class FMAInst:
# def __init__(self, src0, op):
# self.src0 = src0
# self.op = op
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment