diff --git a/lib/cretonne/meta/cretonne/__init__.py b/lib/cretonne/meta/cretonne/__init__.py index a3f0634b94..8a46b2d08f 100644 --- a/lib/cretonne/meta/cretonne/__init__.py +++ b/lib/cretonne/meta/cretonne/__init__.py @@ -834,6 +834,18 @@ class Instruction(object): suffix = ', '.join(o.name for o in self.ins) 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): """Get the first line of the doc comment""" for line in self.__doc__.split('\n'): diff --git a/lib/cretonne/meta/cretonne/ast.py b/lib/cretonne/meta/cretonne/ast.py index 1269a31668..ac21809317 100644 --- a/lib/cretonne/meta/cretonne/ast.py +++ b/lib/cretonne/meta/cretonne/ast.py @@ -53,16 +53,6 @@ class Def(object): return "({}) << {!s}".format( ', '.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): """ @@ -215,12 +205,12 @@ class Apply(Expr): args = ', '.join(map(str, self.args)) return '{}({})'.format(self.instname(), args) - def root_inst(self): - # type: () -> Instruction - """Get the instruction at the root of this tree.""" - return self.inst - - def defs_expr(self): - # type: () -> Tuple[Tuple[Var, ...], Apply] - """Split into a defs tuple and an Apply expr.""" - return ((), self) + def rust_builder(self): + # type: () -> str + """ + Return a Rust Builder method call for instantiating this instruction + application. + """ + args = ', '.join(map(str, self.args)) + method = self.inst.snake_name() + return '{}({})'.format(method, args) diff --git a/lib/cretonne/meta/cretonne/legalize.py b/lib/cretonne/meta/cretonne/legalize.py index 8dfa2e6b97..102df5f6a5 100644 --- a/lib/cretonne/meta/cretonne/legalize.py +++ b/lib/cretonne/meta/cretonne/legalize.py @@ -77,14 +77,14 @@ expand.legalize( (a, c) << iadd_cout(x, y), Rtl( a << iadd(x, y), - c << icmp('ult', a, x) + c << icmp('IntCC::UnsignedLessThan', a, x) )) expand.legalize( (a, b) << isub_bout(x, y), Rtl( a << isub(x, y), - b << icmp('ugt', a, x) + b << icmp('IntCC::UnsignedGreaterThan', a, x) )) expand.legalize( diff --git a/lib/cretonne/meta/cretonne/xform.py b/lib/cretonne/meta/cretonne/xform.py index 4c2f3c9daf..6b9b37f513 100644 --- a/lib/cretonne/meta/cretonne/xform.py +++ b/lib/cretonne/meta/cretonne/xform.py @@ -195,8 +195,7 @@ class XForm(object): destination pattern. """ assert len(self.src.rtl) == 1, "Legalize needs single instruction." - defs, expr = self.src.rtl[0].defs_expr() - for d in defs: + for d in self.src.rtl[0].defs: if not d.is_output(): raise AssertionError( '{} not defined in dest pattern'.format(d)) diff --git a/lib/cretonne/meta/gen_instr.py b/lib/cretonne/meta/gen_instr.py index bb14d36375..8b4555d9b2 100644 --- a/lib/cretonne/meta/gen_instr.py +++ b/lib/cretonne/meta/gen_instr.py @@ -446,16 +446,12 @@ def gen_inst_builder(inst, fmt): rvals = ', '.join(len(inst.value_results) * ['Value']) rtype = '({})'.format(rvals) - method = inst.name - if method == 'return': - # Avoid Rust keywords by appending '_'. - method += '_' - if len(tmpl_types) > 0: tmpl = '<{}>'.format(', '.join(tmpl_types)) else: 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.line('#[allow(non_snake_case)]') diff --git a/lib/cretonne/meta/gen_legalizer.py b/lib/cretonne/meta/gen_legalizer.py index 2a2387723f..d6b0f5eb2c 100644 --- a/lib/cretonne/meta/gen_legalizer.py +++ b/lib/cretonne/meta/gen_legalizer.py @@ -10,30 +10,29 @@ the input instruction. from __future__ import absolute_import from srcgen import Formatter 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 try: - from typing import Union - DefApply = Union[Def, Apply] + from typing import Sequence # noqa except ImportError: pass 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 - fields from `dfg[iref]`. + 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` or `Apply` node providing variable names. + :param node: `Def` node providing variable names. """ fmt.comment('Unwrap {}'.format(node)) - defs, expr = node.defs_expr() + expr = node.expr iform = expr.inst.format nvops = len(iform.value_operands) @@ -71,7 +70,64 @@ def unwrap_inst(iref, node, fmt): prefix, iform.value_operands.index(i))) fmt.line('({})'.format(', '.join(outs))) 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): @@ -83,9 +139,20 @@ def gen_xform(xform, fmt): `inst: Inst` is the variable to be replaced. It is pointed to by `pos: Cursor`. `dfg: DataFlowGraph` is available and mutable. + + Produce an `Option` result at the end. """ + # 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) + + # TODO: Return the first replacement instruction. + fmt.line('None') + def gen_xform_group(xgrp, fmt): # 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 at the last instruction in the expansion. """) + fmt.line('#[allow(unused_variables,unused_assignments)]') with fmt.indented( 'fn ' + xgrp.name + '(pos: &mut Cursor, dfg: &mut DataFlowGraph) -> ' + - 'Option {{', + 'Option {', '}'): # Gen the instruction to be legalized. The cursor we're passed must be # pointing at an instruction. @@ -106,7 +174,7 @@ def gen_xform_group(xgrp, fmt): with fmt.indented('match dfg[inst].opcode() {', '}'): for xform in xgrp.xforms: - inst = xform.src.rtl[0].root_inst() + inst = xform.src.rtl[0].expr.inst with fmt.indented( 'Opcode::{} => {{'.format(inst.camel_name), '}'): gen_xform(xform, fmt) diff --git a/lib/cretonne/src/legalizer.rs b/lib/cretonne/src/legalizer.rs index 3358f77bb0..89ba8cd569 100644 --- a/lib/cretonne/src/legalizer.rs +++ b/lib/cretonne/src/legalizer.rs @@ -13,7 +13,8 @@ //! The legalizer does not deal with register allocation constraints. These constraints are derived //! 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; /// 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"));