- cdsl.ast defines classes representing abstract syntax trees. - cdsl.xform defines classes for instruction transformations. - base.legalize defines legalization patterns.
206 lines
7.5 KiB
Python
206 lines
7.5 KiB
Python
"""
|
|
Generate legalizer transformations.
|
|
|
|
The transformations defined in the `cretonne.legalize` module are all of the
|
|
macro-expansion form where the input pattern is a single instruction. We
|
|
generate a Rust function for each `XFormGroup` which takes a `Cursor` pointing
|
|
at the instruction to be legalized. The expanded destination pattern replaces
|
|
the input instruction.
|
|
"""
|
|
from __future__ import absolute_import
|
|
from srcgen import Formatter
|
|
from base import legalize
|
|
|
|
try:
|
|
from typing import Sequence # noqa
|
|
from cdsl.ast import Def # noqa
|
|
from cdsl.xform import XForm, XFormGroup # noqa
|
|
except ImportError:
|
|
pass
|
|
|
|
|
|
def unwrap_inst(iref, node, fmt):
|
|
# type: (str, Def, Formatter) -> None
|
|
"""
|
|
Given a `Def` node, emit code that extracts all the instruction fields from
|
|
`dfg[iref]`.
|
|
|
|
Create local variables named after the `Var` instances in `node`.
|
|
|
|
:param iref: Name of the `Inst` reference to unwrap.
|
|
:param node: `Def` node providing variable names.
|
|
|
|
"""
|
|
fmt.comment('Unwrap {}'.format(node))
|
|
expr = node.expr
|
|
iform = expr.inst.format
|
|
nvops = len(iform.value_operands)
|
|
|
|
# The tuple of locals we're extracting is `expr.args`.
|
|
with fmt.indented(
|
|
'let ({}) = if let InstructionData::{} {{'
|
|
.format(', '.join(map(str, expr.args)), iform.name), '};'):
|
|
if iform.boxed_storage:
|
|
# This format indirects to a largish `data` struct.
|
|
fmt.line('ref data,')
|
|
else:
|
|
# Fields are encoded directly.
|
|
for m in iform.members:
|
|
if m:
|
|
fmt.line('{},'.format(m))
|
|
if nvops == 1:
|
|
fmt.line('arg,')
|
|
elif nvops > 1:
|
|
fmt.line('args,')
|
|
fmt.line('..')
|
|
fmt.outdented_line('} = dfg[inst] {')
|
|
# Generate the values for the tuple.
|
|
outs = list()
|
|
prefix = 'data.' if iform.boxed_storage else ''
|
|
for i, m in enumerate(iform.members):
|
|
if m:
|
|
outs.append(prefix + m)
|
|
else:
|
|
# This is a value operand.
|
|
if nvops == 1:
|
|
arg = prefix + 'arg'
|
|
else:
|
|
arg = '{}args[{}]'.format(
|
|
prefix, iform.value_operands.index(i))
|
|
outs.append('dfg.resolve_aliases({})'.format(arg))
|
|
fmt.line('({})'.format(', '.join(outs)))
|
|
fmt.outdented_line('} else {')
|
|
fmt.line('unreachable!("bad instruction format")')
|
|
|
|
# If the node has multiple results, detach the values.
|
|
# Place the secondary values in 'src_{}' locals.
|
|
if len(node.defs) > 1:
|
|
if node.defs == node.defs[0].dst_def.defs:
|
|
# Special case: The instruction replacing node defines the exact
|
|
# same values.
|
|
fmt.comment(
|
|
'Multiple results handled by {}.'
|
|
.format(node.defs[0].dst_def))
|
|
else:
|
|
fmt.comment('Detaching secondary results.')
|
|
# Boring case: Detach the secondary values, capture them in locals.
|
|
for d in node.defs[1:]:
|
|
fmt.line('let src_{};'.format(d))
|
|
with fmt.indented('{', '}'):
|
|
fmt.line('let mut vals = dfg.detach_secondary_results(inst);')
|
|
for d in node.defs[1:]:
|
|
fmt.line('src_{} = vals.next().unwrap();'.format(d))
|
|
fmt.line('assert_eq!(vals.next(), None);')
|
|
|
|
|
|
def wrap_tup(seq):
|
|
# type: (Sequence[object]) -> str
|
|
tup = tuple(map(str, seq))
|
|
if len(tup) == 1:
|
|
return tup[0]
|
|
else:
|
|
return '({})'.format(', '.join(tup))
|
|
|
|
|
|
def emit_dst_inst(node, fmt):
|
|
# type: (Def, Formatter) -> None
|
|
exact_replace = False
|
|
replaced_inst = None # type: str
|
|
fixup_first_result = False
|
|
if len(node.defs) == 0:
|
|
# This node doesn't define any values, so just insert the new
|
|
# instruction.
|
|
builder = 'dfg.ins(pos)'
|
|
else:
|
|
src_def0 = node.defs[0].src_def
|
|
if src_def0 and node.defs[0] == src_def0.defs[0]:
|
|
# The primary result is replacing the primary result of the src
|
|
# pattern.
|
|
# Replace the whole instruction.
|
|
builder = 'let {} = dfg.replace(inst)'.format(wrap_tup(node.defs))
|
|
replaced_inst = 'inst'
|
|
# Secondary values weren't replaced if this is an exact replacement
|
|
# for all the src results.
|
|
exact_replace = (node.defs == src_def0.defs)
|
|
else:
|
|
# Insert a new instruction since its primary def doesn't match the
|
|
# src.
|
|
builder = 'let {} = dfg.ins(pos)'.format(wrap_tup(node.defs))
|
|
fixup_first_result = node.defs[0].is_output()
|
|
|
|
fmt.line('{}.{};'.format(builder, node.expr.rust_builder()))
|
|
|
|
# If we just replaced an instruction, we need to bump the cursor so
|
|
# following instructions are inserted *after* the replaced insruction.
|
|
if replaced_inst:
|
|
with fmt.indented(
|
|
'if pos.current_inst() == Some({}) {{'
|
|
.format(replaced_inst), '}'):
|
|
fmt.line('pos.next_inst();')
|
|
|
|
# Fix up any output vars.
|
|
if fixup_first_result:
|
|
# The first result of the instruction just inserted is an output var,
|
|
# but it was not a primary result in the source pattern.
|
|
# We need to change the original value to an alias of the primary one
|
|
# we just inserted.
|
|
fmt.line('dfg.change_to_alias(src_{0}, {0});'.format(node.defs[0]))
|
|
|
|
if not exact_replace:
|
|
# We don't support secondary values as outputs yet. Depending on the
|
|
# source value, we would need to :
|
|
# 1. For a primary source value, replace with a copy instruction.
|
|
# 2. For a secondary source value, request that the builder reuses the
|
|
# value when making secondary result nodes.
|
|
for d in node.defs[1:]:
|
|
assert not d.is_output()
|
|
|
|
|
|
def gen_xform(xform, fmt):
|
|
# type: (XForm, Formatter) -> None
|
|
"""
|
|
Emit code for `xform`, assuming the the opcode of xform's root instruction
|
|
has already been matched.
|
|
|
|
`inst: Inst` is the variable to be replaced. It is pointed to by `pos:
|
|
Cursor`.
|
|
`dfg: DataFlowGraph` is available and mutable.
|
|
"""
|
|
# Unwrap the source instruction, create local variables for the input
|
|
# variables.
|
|
unwrap_inst('inst', xform.src.rtl[0], fmt)
|
|
|
|
# Emit the destination pattern.
|
|
for dst in xform.dst.rtl:
|
|
emit_dst_inst(dst, fmt)
|
|
|
|
|
|
def gen_xform_group(xgrp, fmt):
|
|
# type: (XFormGroup, Formatter) -> None
|
|
fmt.doc_comment("Legalize the instruction pointed to by `pos`.")
|
|
fmt.line('#[allow(unused_variables,unused_assignments)]')
|
|
with fmt.indented(
|
|
'fn ' + xgrp.name +
|
|
'(pos: &mut Cursor, dfg: &mut DataFlowGraph) -> bool {',
|
|
'}'):
|
|
# Gen the instruction to be legalized. The cursor we're passed must be
|
|
# pointing at an instruction.
|
|
fmt.line('let inst = pos.current_inst().expect("need instruction");')
|
|
|
|
with fmt.indented('match dfg[inst].opcode() {', '}'):
|
|
for xform in xgrp.xforms:
|
|
inst = xform.src.rtl[0].expr.inst
|
|
with fmt.indented(
|
|
'Opcode::{} => {{'.format(inst.camel_name), '}'):
|
|
gen_xform(xform, fmt)
|
|
# We'll assume there are uncovered opcodes.
|
|
fmt.line('_ => return false,')
|
|
fmt.line('true')
|
|
|
|
|
|
def generate(isas, out_dir):
|
|
fmt = Formatter()
|
|
gen_xform_group(legalize.narrow, fmt)
|
|
gen_xform_group(legalize.expand, fmt)
|
|
fmt.update_file('legalizer.rs', out_dir)
|