Commit 78332e98 authored by Lyude Paul's avatar Lyude Paul

wip: Get constant assignments fully working

This seems to match up with the compiler's output, hooray!
parent 10d2ddb9
......@@ -604,6 +604,12 @@ class ImmediateToken:
else:
return 64
def __eq__(self, other):
if not isinstance(other, type(self)):
return False
return self.bitlen == other.bitlen and self.value == other.value
def __repr__(self):
return '<ImmediateToken at 0x%x 0x%x; type=%s>' % (
id(self), self.value, self.read_type.name)
......@@ -697,7 +703,8 @@ class RegisterFile:
if self.__const_port == value:
return
elif self.__const_port is not None:
if self.__const_port is Clause.Instruction.PendingImmediateSlot:
if isinstance(self.__const_port,
Clause.Instruction.PendingImmediateSlot):
if not isinstance(value, ImmediateSlot) and \
value is not ImmediateZeroSlot:
raise self.ConstPortInUse(self.__const_port, value)
......@@ -801,6 +808,10 @@ class ConstantSrc:
return bitstring.pack('uint:3', src)
def __repr__(self):
return '<ConstantSrc (%s) at 0x%x>' % (
'high' if self.high32 else 'low', id(self))
class ImmediateZeroSlot:
@classmethod
def encode(cls):
......@@ -855,9 +866,6 @@ class Clause:
return '<Data register %d at %s>' % (self.idx, hex(id(self)))
class Instruction:
class PendingImmediateSlot:
pass
class ImmediateCountError(ParsingException):
def __new__(self, token):
if token.read_type == ImmediateToken.ReadType.FULL32:
......@@ -868,13 +876,35 @@ class Clause:
super().__init__("No space left for immediate 0x%x%s" % (token.value,
mod_str))
class PendingImmediateSlot:
def __init__(self):
self.__contents = []
@property
def contents(self):
return self.__contents
@property
def bitlen(self):
return sum(t.bitlen for t in self.contents)
def __iter__(self):
return self.__contents.__iter__()
def __len__(self):
return self.__contents.__len__()
def add_immediate(self, token):
if self.bitlen + token.bitlen > 64:
raise ParsingException('Too many constants for one instruction cycle')
self.__contents.append(token)
def __init__(self, first):
self.first = first
self.reg_file = RegisterFile(self)
self.fma = None
self.add = None
self.writes = {fma: None, add: None}
self.__pending_immeds = []
def __repr__(self):
return '<Instruction at %s; fma=%s, add=%s, first=%s, writes=%s, reg_file=%s>' % (
......@@ -887,32 +917,29 @@ class Clause:
Add a pending immediate token this instruction and check that we
will still be able to encode this instruction.
"""
self.reg_file.const_port = self.PendingImmediateSlot
immed_cnt = len(self.__pending_immeds)
if token.bitlen == 64:
if immed_cnt == 1:
raise self.ImmediateCountError(token)
elif immed_cnt == 2:
raise self.ImmediateCountError(token)
self.__pending_immeds.append(token)
pending_slot = self.reg_file.const_port
if not self.has_pending_immediates():
pending_slot = self.PendingImmediateSlot()
self.reg_file.const_port = pending_slot
def resolve_immediates(self, clause):
pending = self.__pending_immeds
pending_slot.add_immediate(token)
if not pending:
return
def resolve_immediates(self, clause, slot=None):
if not slot:
slot = clause._get_immediate_slot(self.reg_file.const_port.contents)
if all(t.value == 0 for t in pending):
const_port = ImmediateZeroSlot
else:
const_port = clause._get_immediate_slot(pending)
self.reg_file.const_port = const_port
self.reg_file.const_port = slot
# Replace ImmediateTokens in our src list with proper immediate
# sources
for stage in self.stages:
for idx, src in enumerate(stage.srcs):
if isinstance(src, ImmediateToken):
stage.srcs[idx] = const_port.get_src(src)
stage.srcs[idx] = slot.get_src(src)
def has_pending_immediates(self):
return isinstance(self.reg_file.const_port,
self.PendingImmediateSlot)
@property
def pending_stage(self):
......@@ -962,7 +989,33 @@ class Clause:
if inst_reads in slot:
return slot
new_slot = ImmediateSlot(inst_reads, len(self.immediate_slots))
new_slot = None
if inst_reads[0].bitlen == 32 and len(inst_reads) == 2:
# No slot shares the exact same contents as ours. We can still
# however, reuse the immediate slot of another instruction if the
# slot is still pending, and it's bottom 32 bits contain one of the
# two 32 bit immediates in our own slots
for inst in clause.instructions:
if not inst.has_pending_immediates():
continue
pending_slot = inst.reg_file.const_port
bottom_half = pending_slot.contents[0]
assert bottom_half.bitlen != 64
if bottom_half not in inst_reads:
continue
inst_reads.remove(bottom_half)
new_slot = ImmediateSlot((bottom_half, inst_reads[0]),
len(self.immediate_slots))
inst.resolve_immediates(self, new_slot)
break
if not new_slot:
new_slot = ImmediateSlot(inst_reads, len(self.immediate_slots))
self.immediate_slots.append(new_slot)
return new_slot
......@@ -998,7 +1051,21 @@ class Clause:
inst.fma = parser.parse_op(inst.reg_file, srcs)
else:
inst.add = parser.parse_op(inst.reg_file, srcs)
inst.resolve_immediates(clause)
# We attempt to match what the compiler does here. Basically: If
# this instruction uses enough immediates to fill an entire
# immediate slot, we resolve it's immediates immediately. If
# there's space for one more immediate, we wait until the end of
# the clause to resolve the immediates.
# The only exception we make to this is if we can use the special
# immediate zero slot.
if inst.has_pending_immediates():
pending = inst.reg_file.const_port
if all(t.value == 0 for t in pending):
inst.resolve_immediates(self, ImmediateZeroSlot)
elif pending.bitlen == 64:
inst.resolve_immediates(self)
self.instructions.append(inst)
self.__pending_inst = None
......@@ -1055,6 +1122,10 @@ class Clause:
for inst in self.instructions:
inst.reg_file.assign_reg_ports()
const_port = inst.reg_file.const_port
if isinstance(const_port, inst.PendingImmediateSlot):
inst.resolve_immediates(self)
CLAUSE_START = re.compile(r'^clause_([0-9]+):$')
def scan_clause_start(string):
string = string.strip()
......
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