Legalization pattern emission WIP.

Begin emitting legalization patterns in the form of two functions,
'expand' and 'narrow' that are included in legalizer.rs.

The generated code compiles, but it is not fully working yet. We need to
deal with the special cases of instructions producing multiple results.
This commit is contained in:
Jakob Stoklund Olesen
2016-11-02 12:56:34 -07:00
parent 3bb6efba6e
commit 125fe64121
7 changed files with 113 additions and 41 deletions

View File

@@ -834,6 +834,18 @@ class Instruction(object):
suffix = ', '.join(o.name for o in self.ins) suffix = ', '.join(o.name for o in self.ins)
return '{}{} {}'.format(prefix, self.name, suffix) return '{}{} {}'.format(prefix, self.name, suffix)
def snake_name(self):
# type: () -> str
"""
Get the snake_case name of this instruction.
Keywords in Rust and Python are altered by appending a '_'
"""
if self.name == 'return':
return 'return_'
else:
return self.name
def blurb(self): def blurb(self):
"""Get the first line of the doc comment""" """Get the first line of the doc comment"""
for line in self.__doc__.split('\n'): for line in self.__doc__.split('\n'):

View File

@@ -53,16 +53,6 @@ class Def(object):
return "({}) << {!s}".format( return "({}) << {!s}".format(
', '.join(map(str, self.defs)), self.expr) ', '.join(map(str, self.defs)), self.expr)
def root_inst(self):
# type: () -> Instruction
"""Get the instruction at the root of this tree."""
return self.expr.root_inst()
def defs_expr(self):
# type: () -> Tuple[Tuple[Var, ...], Apply]
"""Split into a defs tuple and an Apply expr."""
return (self.defs, self.expr)
class Expr(object): class Expr(object):
""" """
@@ -215,12 +205,12 @@ class Apply(Expr):
args = ', '.join(map(str, self.args)) args = ', '.join(map(str, self.args))
return '{}({})'.format(self.instname(), args) return '{}({})'.format(self.instname(), args)
def root_inst(self): def rust_builder(self):
# type: () -> Instruction # type: () -> str
"""Get the instruction at the root of this tree.""" """
return self.inst Return a Rust Builder method call for instantiating this instruction
application.
def defs_expr(self): """
# type: () -> Tuple[Tuple[Var, ...], Apply] args = ', '.join(map(str, self.args))
"""Split into a defs tuple and an Apply expr.""" method = self.inst.snake_name()
return ((), self) return '{}({})'.format(method, args)

View File

@@ -77,14 +77,14 @@ expand.legalize(
(a, c) << iadd_cout(x, y), (a, c) << iadd_cout(x, y),
Rtl( Rtl(
a << iadd(x, y), a << iadd(x, y),
c << icmp('ult', a, x) c << icmp('IntCC::UnsignedLessThan', a, x)
)) ))
expand.legalize( expand.legalize(
(a, b) << isub_bout(x, y), (a, b) << isub_bout(x, y),
Rtl( Rtl(
a << isub(x, y), a << isub(x, y),
b << icmp('ugt', a, x) b << icmp('IntCC::UnsignedGreaterThan', a, x)
)) ))
expand.legalize( expand.legalize(

View File

@@ -195,8 +195,7 @@ class XForm(object):
destination pattern. destination pattern.
""" """
assert len(self.src.rtl) == 1, "Legalize needs single instruction." assert len(self.src.rtl) == 1, "Legalize needs single instruction."
defs, expr = self.src.rtl[0].defs_expr() for d in self.src.rtl[0].defs:
for d in defs:
if not d.is_output(): if not d.is_output():
raise AssertionError( raise AssertionError(
'{} not defined in dest pattern'.format(d)) '{} not defined in dest pattern'.format(d))

View File

@@ -446,16 +446,12 @@ def gen_inst_builder(inst, fmt):
rvals = ', '.join(len(inst.value_results) * ['Value']) rvals = ', '.join(len(inst.value_results) * ['Value'])
rtype = '({})'.format(rvals) rtype = '({})'.format(rvals)
method = inst.name
if method == 'return':
# Avoid Rust keywords by appending '_'.
method += '_'
if len(tmpl_types) > 0: if len(tmpl_types) > 0:
tmpl = '<{}>'.format(', '.join(tmpl_types)) tmpl = '<{}>'.format(', '.join(tmpl_types))
else: else:
tmpl = '' tmpl = ''
proto = '{}{}({}) -> {}'.format(method, tmpl, ', '.join(args), rtype) proto = '{}{}({}) -> {}'.format(
inst.snake_name(), tmpl, ', '.join(args), rtype)
fmt.doc_comment('`{}`\n\n{}'.format(inst, inst.blurb())) fmt.doc_comment('`{}`\n\n{}'.format(inst, inst.blurb()))
fmt.line('#[allow(non_snake_case)]') fmt.line('#[allow(non_snake_case)]')

View File

@@ -10,30 +10,29 @@ the input instruction.
from __future__ import absolute_import from __future__ import absolute_import
from srcgen import Formatter from srcgen import Formatter
import cretonne.legalize as legalize import cretonne.legalize as legalize
from cretonne.ast import Def, Apply # noqa from cretonne.ast import Def # noqa
from cretonne.xform import XForm, XFormGroup # noqa from cretonne.xform import XForm, XFormGroup # noqa
try: try:
from typing import Union from typing import Sequence # noqa
DefApply = Union[Def, Apply]
except ImportError: except ImportError:
pass pass
def unwrap_inst(iref, node, fmt): def unwrap_inst(iref, node, fmt):
# type: (str, DefApply, Formatter) -> None # type: (str, Def, Formatter) -> None
""" """
Given a `Def` or `Apply` node, emit code that extracts all the instruction Given a `Def` node, emit code that extracts all the instruction fields from
fields from `dfg[iref]`. `dfg[iref]`.
Create local variables named after the `Var` instances in `node`. Create local variables named after the `Var` instances in `node`.
:param iref: Name of the `Inst` reference to unwrap. :param iref: Name of the `Inst` reference to unwrap.
:param node: `Def` or `Apply` node providing variable names. :param node: `Def` node providing variable names.
""" """
fmt.comment('Unwrap {}'.format(node)) fmt.comment('Unwrap {}'.format(node))
defs, expr = node.defs_expr() expr = node.expr
iform = expr.inst.format iform = expr.inst.format
nvops = len(iform.value_operands) nvops = len(iform.value_operands)
@@ -71,7 +70,64 @@ def unwrap_inst(iref, node, fmt):
prefix, iform.value_operands.index(i))) prefix, iform.value_operands.index(i)))
fmt.line('({})'.format(', '.join(outs))) fmt.line('({})'.format(', '.join(outs)))
fmt.outdented_line('} else {') fmt.outdented_line('} else {')
fmt.line('unimplemented!("bad instruction format")') 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
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))
# 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))
fmt.line('{}.{};'.format(builder, node.expr.rust_builder()))
if exact_replace:
fmt.comment('exactreplacement')
def gen_xform(xform, fmt): def gen_xform(xform, fmt):
@@ -83,9 +139,20 @@ def gen_xform(xform, fmt):
`inst: Inst` is the variable to be replaced. It is pointed to by `pos: `inst: Inst` is the variable to be replaced. It is pointed to by `pos:
Cursor`. Cursor`.
`dfg: DataFlowGraph` is available and mutable. `dfg: DataFlowGraph` is available and mutable.
Produce an `Option<Inst>` result at the end.
""" """
# Unwrap the source instruction, create local variables for the input
# variables.
unwrap_inst('inst', xform.src.rtl[0], fmt) unwrap_inst('inst', xform.src.rtl[0], fmt)
# Emit the destination pattern.
for dst in xform.dst.rtl:
emit_dst_inst(dst, fmt)
# TODO: Return the first replacement instruction.
fmt.line('None')
def gen_xform_group(xgrp, fmt): def gen_xform_group(xgrp, fmt):
# type: (XFormGroup, Formatter) -> None # type: (XFormGroup, Formatter) -> None
@@ -95,10 +162,11 @@ def gen_xform_group(xgrp, fmt):
Return the first instruction in the expansion, and leave `pos` pointing Return the first instruction in the expansion, and leave `pos` pointing
at the last instruction in the expansion. at the last instruction in the expansion.
""") """)
fmt.line('#[allow(unused_variables,unused_assignments)]')
with fmt.indented( with fmt.indented(
'fn ' + xgrp.name + 'fn ' + xgrp.name +
'(pos: &mut Cursor, dfg: &mut DataFlowGraph) -> ' + '(pos: &mut Cursor, dfg: &mut DataFlowGraph) -> ' +
'Option<Inst> {{', 'Option<Inst> {',
'}'): '}'):
# Gen the instruction to be legalized. The cursor we're passed must be # Gen the instruction to be legalized. The cursor we're passed must be
# pointing at an instruction. # pointing at an instruction.
@@ -106,7 +174,7 @@ def gen_xform_group(xgrp, fmt):
with fmt.indented('match dfg[inst].opcode() {', '}'): with fmt.indented('match dfg[inst].opcode() {', '}'):
for xform in xgrp.xforms: for xform in xgrp.xforms:
inst = xform.src.rtl[0].root_inst() inst = xform.src.rtl[0].expr.inst
with fmt.indented( with fmt.indented(
'Opcode::{} => {{'.format(inst.camel_name), '}'): 'Opcode::{} => {{'.format(inst.camel_name), '}'):
gen_xform(xform, fmt) gen_xform(xform, fmt)

View File

@@ -13,7 +13,8 @@
//! The legalizer does not deal with register allocation constraints. These constraints are derived //! The legalizer does not deal with register allocation constraints. These constraints are derived
//! from the encoding recipes, and solved later by the register allocator. //! from the encoding recipes, and solved later by the register allocator.
use ir::Function; use ir::{Function, Cursor, DataFlowGraph, InstructionData, Opcode, Inst, InstBuilder};
use ir::condcodes::IntCC;
use isa::TargetIsa; use isa::TargetIsa;
/// Legalize `func` for `isa`. /// Legalize `func` for `isa`.
@@ -52,3 +53,9 @@ pub fn legalize_function(func: &mut Function, isa: &TargetIsa) {
} }
} }
} }
// Include legalization patterns that were generated by gen_legalizer.py from the XForms in
// meta/cretonne/legalize.py.
//
// Concretely, this defines private functions `narrow()`, and `expand()`.
include!(concat!(env!("OUT_DIR"), "/legalizer.rs"));