Skip to content

back.rtlil: Opportunistically trim zero and sign extension on operands. #1152

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 69 additions & 1 deletion amaranth/back/rtlil.py
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,16 @@ def emit_assignments(case, cond):
emit_assignments(proc, _nir.Net.from_const(1))
assert pos == len(cell.assignments)

def shorten_operand(self, value, *, signed):
value = list(value)
if signed:
while len(value) > 1 and value[-1] == value[-2]:
value.pop()
else:
while len(value) > 0 and value[-1] == _nir.Net.from_const(0):
value.pop()
return _nir.Value(value)

def emit_operator(self, cell_idx, cell):
UNARY_OPERATORS = {
"-": "$neg",
Expand Down Expand Up @@ -782,17 +792,75 @@ def emit_operator(self, cell_idx, cell):
if len(cell.inputs) == 1:
cell_type = UNARY_OPERATORS[cell.operator]
operand, = cell.inputs
signed = False
if cell.operator == "-":
# For arithmetic operands, we trim the extra sign or zero extension on the operands
# to make the output prettier, and to fix inference problems in some not very smart
# synthesis tools.
operand_u = self.shorten_operand(operand, signed=False)
operand_s = self.shorten_operand(operand, signed=True)
# The operator will work when lowered with either signedness. Pick whichever
# is prettier.
if len(operand_s) < len(operand_u):
signed = True
operand = operand_s
else:
signed = False
operand = operand_u
self.builder.cell(cell_type, ports={
"A": self.sigspec(operand),
"Y": self.cell_wires[cell_idx].name
}, parameters={
"A_SIGNED": False,
"A_SIGNED": signed,
"A_WIDTH": len(operand),
"Y_WIDTH": cell.width,
}, src_loc=cell.src_loc)
elif len(cell.inputs) == 2:
cell_type, a_signed, b_signed = BINARY_OPERATORS[cell.operator]
operand_a, operand_b = cell.inputs
if cell.operator in ("+", "-", "*", "==", "!="):
# Arithmetic operators that will work with any signedness, but we have to choose
# a common one for both operands. Prefer signed in case of mixed signedness.
operand_a_u = self.shorten_operand(operand_a, signed=False)
operand_b_u = self.shorten_operand(operand_b, signed=False)
operand_a_s = self.shorten_operand(operand_a, signed=True)
operand_b_s = self.shorten_operand(operand_b, signed=True)
if operand_a.is_const:
# In case of constant operand, choose whichever shortens the other one better.
signed = len(operand_b_s) < len(operand_b_u)
elif operand_b.is_const:
signed = len(operand_a_s) < len(operand_a_u)
elif (len(operand_a_s) < len(operand_a) and len(operand_a_u) == len(operand_a)):
# Operand A can only be shortened by signed. Pick it.
signed = True
elif (len(operand_b_s) < len(operand_b) and len(operand_b_u) == len(operand_b)):
# Operand B can only be shortened by signed. Pick it.
signed = True
else:
# Otherwise, use unsigned shortening.
signed = False
if signed:
operand_a = operand_a_s
operand_b = operand_b_s
else:
operand_a = operand_a_u
operand_b = operand_b_u
a_signed = b_signed = signed
if cell.operator[0] in "us":
# Signedness forced, just shorten.
operand_a = self.shorten_operand(operand_a, signed=a_signed)
operand_b = self.shorten_operand(operand_b, signed=b_signed)
if cell.operator == "<<":
# We can pick the signedness for left operand, but right is fixed.
operand_a_u = self.shorten_operand(operand_a, signed=False)
operand_a_s = self.shorten_operand(operand_a, signed=True)
if len(operand_a_s) < len(operand_a_u):
a_signed = True
operand_a = operand_a_s
else:
a_signed = False
operand_a = operand_a_u
operand_b = self.shorten_operand(operand_b, signed=b_signed)
if cell.operator in ("u//", "s//", "u%", "s%"):
result = self.builder.wire(cell.width)
self.builder.cell(cell_type, ports={
Expand Down
4 changes: 4 additions & 0 deletions amaranth/hdl/_nir.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ def __repr__(self):
else:
return f"(cat {' '.join(chunks)})"

@property
def is_const(self):
return all(net.is_const for net in self)

__str__ = __repr__


Expand Down
Loading
Loading