Define AST nodes and instruction transformations.
Enable syntax: iadd(x, y) which creates an Apply node. Enable syntax: z << iadd(x, y) which creates a Def node. Add an XForm class which represents source and destination patterns as RTL lists.
This commit is contained in:
@@ -10,6 +10,7 @@ import math
|
|||||||
import importlib
|
import importlib
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from .predicates import And
|
from .predicates import And
|
||||||
|
from .ast import Apply
|
||||||
|
|
||||||
|
|
||||||
camel_re = re.compile('(^|_)([a-z])')
|
camel_re = re.compile('(^|_)([a-z])')
|
||||||
@@ -603,6 +604,12 @@ class Operand(object):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "`{}`".format(self.name)
|
return "`{}`".format(self.name)
|
||||||
|
|
||||||
|
def is_value(self):
|
||||||
|
"""
|
||||||
|
Is this an SSA value operand?
|
||||||
|
"""
|
||||||
|
return self.kind is value
|
||||||
|
|
||||||
|
|
||||||
class InstructionFormat(object):
|
class InstructionFormat(object):
|
||||||
"""
|
"""
|
||||||
@@ -779,10 +786,13 @@ class Instruction(object):
|
|||||||
self.format = InstructionFormat.lookup(self.ins, self.outs)
|
self.format = InstructionFormat.lookup(self.ins, self.outs)
|
||||||
# Indexes into outs for value results. Others are `variable_args`.
|
# Indexes into outs for value results. Others are `variable_args`.
|
||||||
self.value_results = tuple(
|
self.value_results = tuple(
|
||||||
i for i, o in enumerate(self.outs) if o.kind is value)
|
i for i, o in enumerate(self.outs) if o.is_value())
|
||||||
self._verify_polymorphic()
|
self._verify_polymorphic()
|
||||||
InstructionGroup.append(self)
|
InstructionGroup.append(self)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
def _verify_polymorphic(self):
|
def _verify_polymorphic(self):
|
||||||
"""
|
"""
|
||||||
Check if this instruction is polymorphic, and verify its use of type
|
Check if this instruction is polymorphic, and verify its use of type
|
||||||
@@ -910,6 +920,13 @@ class Instruction(object):
|
|||||||
assert not self.is_polymorphic, self
|
assert not self.is_polymorphic, self
|
||||||
return (self, ())
|
return (self, ())
|
||||||
|
|
||||||
|
def __call__(self, *args):
|
||||||
|
"""
|
||||||
|
Create an `ast.Apply` AST node representing the application of this
|
||||||
|
instruction to the arguments.
|
||||||
|
"""
|
||||||
|
return Apply(self, args)
|
||||||
|
|
||||||
|
|
||||||
class BoundInstruction(object):
|
class BoundInstruction(object):
|
||||||
"""
|
"""
|
||||||
@@ -951,6 +968,13 @@ class BoundInstruction(object):
|
|||||||
assert len(self.typevars) == 1 + len(self.inst.other_typevars)
|
assert len(self.typevars) == 1 + len(self.inst.other_typevars)
|
||||||
return (self.inst, self.typevars)
|
return (self.inst, self.typevars)
|
||||||
|
|
||||||
|
def __call__(self, *args):
|
||||||
|
"""
|
||||||
|
Create an `ast.Apply` AST node representing the application of this
|
||||||
|
instruction to the arguments.
|
||||||
|
"""
|
||||||
|
return Apply(self, args)
|
||||||
|
|
||||||
|
|
||||||
# Defining target ISAs.
|
# Defining target ISAs.
|
||||||
|
|
||||||
|
|||||||
122
meta/cretonne/ast.py
Normal file
122
meta/cretonne/ast.py
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
"""
|
||||||
|
Abstract syntax trees.
|
||||||
|
|
||||||
|
This module defines classes that can be used to create abstract syntax trees
|
||||||
|
for patern matching an rewriting of cretonne instructions.
|
||||||
|
"""
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
|
||||||
|
class Def(object):
|
||||||
|
"""
|
||||||
|
An AST definition associates a set of variables with the values produced by
|
||||||
|
an expression.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
>>> from .base import iadd_cout, iconst
|
||||||
|
>>> x = Var('x')
|
||||||
|
>>> y = Var('y')
|
||||||
|
>>> x << iconst(4)
|
||||||
|
(Var(x),) << Apply(iconst, (4,))
|
||||||
|
>>> (x, y) << iadd_cout(4, 5)
|
||||||
|
(Var(x), Var(y)) << Apply(iadd_cout, (4, 5))
|
||||||
|
|
||||||
|
The `<<` operator is used to create variable definitions.
|
||||||
|
|
||||||
|
:param defs: Single variable or tuple of variables to be defined.
|
||||||
|
:param expr: Expression generating the values.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, defs, expr):
|
||||||
|
if not isinstance(defs, tuple):
|
||||||
|
defs = (defs,)
|
||||||
|
assert isinstance(expr, Expr)
|
||||||
|
self.defs = defs
|
||||||
|
self.expr = expr
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "{} << {!r}".format(self.defs, self.expr)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if len(self.defs) == 1:
|
||||||
|
return "{!s} << {!s}".format(self.defs[0], self.expr)
|
||||||
|
else:
|
||||||
|
return "({}) << {!s}".format(", ".join(self.defs), self.expr)
|
||||||
|
|
||||||
|
|
||||||
|
class Expr(object):
|
||||||
|
"""
|
||||||
|
An AST expression.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __rlshift__(self, other):
|
||||||
|
"""
|
||||||
|
Define variables using `var << expr` or `(v1, v2) << expr`.
|
||||||
|
"""
|
||||||
|
return Def(other, self)
|
||||||
|
|
||||||
|
|
||||||
|
class Var(Expr):
|
||||||
|
"""
|
||||||
|
A free variable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
self.name = name
|
||||||
|
# Bitmask of contexts where this variable is defined.
|
||||||
|
# See XForm._rewrite_defs().
|
||||||
|
self.defctx = 0
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
s = self.name
|
||||||
|
if self.defctx:
|
||||||
|
s += ", d={:02b}".format(self.defctx)
|
||||||
|
return "Var({})".format(s)
|
||||||
|
|
||||||
|
|
||||||
|
class Apply(Expr):
|
||||||
|
"""
|
||||||
|
Apply an instruction to arguments.
|
||||||
|
|
||||||
|
An `Apply` AST expression is created by using function call syntax on
|
||||||
|
instructions. This applies to both bound and unbound polymorphic
|
||||||
|
instructions:
|
||||||
|
|
||||||
|
>>> from .base import jump, iadd
|
||||||
|
>>> jump('next', ())
|
||||||
|
Apply(jump, ('next', ()))
|
||||||
|
>>> iadd.i32('x', 'y')
|
||||||
|
Apply(iadd.i32, ('x', 'y'))
|
||||||
|
|
||||||
|
:param inst: The instruction being applied, an `Instruction` or
|
||||||
|
`BoundInstruction` instance.
|
||||||
|
:param args: Tuple of arguments.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, inst, args):
|
||||||
|
from . import BoundInstruction
|
||||||
|
if isinstance(inst, BoundInstruction):
|
||||||
|
self.inst = inst.inst
|
||||||
|
self.typevars = inst.typevars
|
||||||
|
else:
|
||||||
|
self.inst = inst
|
||||||
|
self.typevars = ()
|
||||||
|
self.args = args
|
||||||
|
assert len(self.inst.ins) == len(args)
|
||||||
|
|
||||||
|
def instname(self):
|
||||||
|
i = self.inst.name
|
||||||
|
for t in self.typevars:
|
||||||
|
i += '.{}'.format(t)
|
||||||
|
return i
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "Apply({}, {})".format(self.instname(), self.args)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
args = ', '.join(map(str, self.args))
|
||||||
|
return '{}({})'.format(self.instname(), args)
|
||||||
28
meta/cretonne/test_ast.py
Normal file
28
meta/cretonne/test_ast.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
from __future__ import absolute_import
|
||||||
|
from unittest import TestCase
|
||||||
|
from doctest import DocTestSuite
|
||||||
|
from . import ast
|
||||||
|
from .base import jump, iadd
|
||||||
|
|
||||||
|
|
||||||
|
def load_tests(loader, tests, ignore):
|
||||||
|
tests.addTests(DocTestSuite(ast))
|
||||||
|
return tests
|
||||||
|
|
||||||
|
|
||||||
|
x = 'x'
|
||||||
|
y = 'y'
|
||||||
|
a = 'a'
|
||||||
|
|
||||||
|
|
||||||
|
class TestPatterns(TestCase):
|
||||||
|
def test_apply(self):
|
||||||
|
i = jump(x, y)
|
||||||
|
self.assertEqual(repr(i), "Apply(jump, ('x', 'y'))")
|
||||||
|
|
||||||
|
i = iadd.i32(x, y)
|
||||||
|
self.assertEqual(repr(i), "Apply(iadd.i32, ('x', 'y'))")
|
||||||
|
|
||||||
|
def test_single_ins(self):
|
||||||
|
pat = a << iadd.i32(x, y)
|
||||||
|
self.assertEqual(repr(pat), "('a',) << Apply(iadd.i32, ('x', 'y'))")
|
||||||
59
meta/cretonne/test_xform.py
Normal file
59
meta/cretonne/test_xform.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
from __future__ import absolute_import
|
||||||
|
from unittest import TestCase
|
||||||
|
from doctest import DocTestSuite
|
||||||
|
from . import xform
|
||||||
|
from .base import iadd, iadd_imm, iconst
|
||||||
|
from .ast import Var
|
||||||
|
from .xform import Rtl, XForm
|
||||||
|
|
||||||
|
|
||||||
|
def load_tests(loader, tests, ignore):
|
||||||
|
tests.addTests(DocTestSuite(xform))
|
||||||
|
return tests
|
||||||
|
|
||||||
|
|
||||||
|
x = Var('x')
|
||||||
|
y = Var('y')
|
||||||
|
a = Var('a')
|
||||||
|
c = Var('c')
|
||||||
|
|
||||||
|
|
||||||
|
class TestXForm(TestCase):
|
||||||
|
def test_macro_pattern(self):
|
||||||
|
src = Rtl(a << iadd_imm(x, y))
|
||||||
|
dst = Rtl(
|
||||||
|
c << iconst(y),
|
||||||
|
a << iadd(x, c))
|
||||||
|
XForm(src, dst)
|
||||||
|
|
||||||
|
def test_def_input(self):
|
||||||
|
# Src pattern has a def which is an input in dst.
|
||||||
|
src = Rtl(a << iadd_imm(x, 1))
|
||||||
|
dst = Rtl(y << iadd_imm(a, 1))
|
||||||
|
with self.assertRaisesRegexp(
|
||||||
|
AssertionError,
|
||||||
|
"'a' used as both input and def"):
|
||||||
|
XForm(src, dst)
|
||||||
|
|
||||||
|
def test_input_def(self):
|
||||||
|
# Converse of the above.
|
||||||
|
src = Rtl(y << iadd_imm(a, 1))
|
||||||
|
dst = Rtl(a << iadd_imm(x, 1))
|
||||||
|
with self.assertRaisesRegexp(
|
||||||
|
AssertionError,
|
||||||
|
"'a' used as both input and def"):
|
||||||
|
XForm(src, dst)
|
||||||
|
|
||||||
|
def test_extra_input(self):
|
||||||
|
src = Rtl(a << iadd_imm(x, 1))
|
||||||
|
dst = Rtl(a << iadd(x, y))
|
||||||
|
with self.assertRaisesRegexp(AssertionError, "extra inputs in dst"):
|
||||||
|
XForm(src, dst)
|
||||||
|
|
||||||
|
def test_double_def(self):
|
||||||
|
src = Rtl(
|
||||||
|
a << iadd_imm(x, 1),
|
||||||
|
a << iadd(x, y))
|
||||||
|
dst = Rtl(a << iadd(x, y))
|
||||||
|
with self.assertRaisesRegexp(AssertionError, "'a' multiply defined"):
|
||||||
|
XForm(src, dst)
|
||||||
182
meta/cretonne/xform.py
Normal file
182
meta/cretonne/xform.py
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
"""
|
||||||
|
Instruction transformations.
|
||||||
|
"""
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from .ast import Def, Var, Apply
|
||||||
|
|
||||||
|
|
||||||
|
SRCCTX = 1
|
||||||
|
DSTCTX = 2
|
||||||
|
|
||||||
|
|
||||||
|
class Rtl(object):
|
||||||
|
"""
|
||||||
|
Register Transfer Language list.
|
||||||
|
|
||||||
|
An RTL object contains a list of register assignments in the form of `Def`
|
||||||
|
objects and/or Apply objects for side-effecting instructions.
|
||||||
|
|
||||||
|
An RTL list can represent both a source pattern to be matched, or a
|
||||||
|
destination pattern to be inserted.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args):
|
||||||
|
self.rtl = args
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.rtl)
|
||||||
|
|
||||||
|
|
||||||
|
class XForm(object):
|
||||||
|
"""
|
||||||
|
An instruction transformation consists of a source and destination pattern.
|
||||||
|
|
||||||
|
Patterns are expressed in *register transfer language* as tuples of
|
||||||
|
`ast.Def` or `ast.Expr` nodes.
|
||||||
|
|
||||||
|
A legalization pattern must have a source pattern containing only a single
|
||||||
|
instruction.
|
||||||
|
|
||||||
|
>>> from .base import iconst, iadd, iadd_imm
|
||||||
|
>>> a = Var('a')
|
||||||
|
>>> c = Var('c')
|
||||||
|
>>> v = Var('v')
|
||||||
|
>>> x = Var('x')
|
||||||
|
>>> XForm(
|
||||||
|
... Rtl(c << iconst(v),
|
||||||
|
... a << iadd(x, c)),
|
||||||
|
... Rtl(a << iadd_imm(x, v)))
|
||||||
|
XForm(inputs=[Var(v), Var(x)], defs=[Var(c, d=01), Var(a, d=11)],
|
||||||
|
c << iconst(v)
|
||||||
|
a << iadd(x, c)
|
||||||
|
=>
|
||||||
|
a << iadd_imm(x, v)
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, src, dst):
|
||||||
|
self.src = src
|
||||||
|
self.dst = dst
|
||||||
|
# Variables that are inputs to the source pattern.
|
||||||
|
self.inputs = list()
|
||||||
|
# Variables defined in either src or dst.
|
||||||
|
self.defs = list()
|
||||||
|
|
||||||
|
# Rewrite variables in src and dst RTL lists to our own copies.
|
||||||
|
# Map name -> private Var.
|
||||||
|
symtab = dict()
|
||||||
|
self._rewrite_rtl(src, symtab, SRCCTX)
|
||||||
|
num_src_inputs = len(self.inputs)
|
||||||
|
self._rewrite_rtl(dst, symtab, DSTCTX)
|
||||||
|
|
||||||
|
# Check for inconsistently used inputs.
|
||||||
|
for i in self.inputs:
|
||||||
|
if i.defctx:
|
||||||
|
raise AssertionError(
|
||||||
|
"'{}' used as both input and def".format(i))
|
||||||
|
|
||||||
|
# Check for spurious inputs in dst.
|
||||||
|
if len(self.inputs) > num_src_inputs:
|
||||||
|
raise AssertionError(
|
||||||
|
"extra inputs in dst RTL: {}".format(
|
||||||
|
self.inputs[num_src_inputs:]))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
s = "XForm(inputs={}, defs={},\n ".format(self.inputs, self.defs)
|
||||||
|
s += '\n '.join(str(n) for n in self.src)
|
||||||
|
s += '\n=>\n '
|
||||||
|
s += '\n '.join(str(n) for n in self.dst)
|
||||||
|
s += '\n)'
|
||||||
|
return s
|
||||||
|
|
||||||
|
def _rewrite_rtl(self, rtl, symtab, context):
|
||||||
|
for line in rtl:
|
||||||
|
if isinstance(line, Def):
|
||||||
|
line.defs = tuple(
|
||||||
|
self._rewrite_defs(line.defs, symtab, context))
|
||||||
|
expr = line.expr
|
||||||
|
else:
|
||||||
|
expr = line
|
||||||
|
self._rewrite_expr(expr, symtab, context)
|
||||||
|
|
||||||
|
def _rewrite_expr(self, expr, symtab, context):
|
||||||
|
"""
|
||||||
|
Find all uses of variables in `expr` and replace them with our own
|
||||||
|
local symbols.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Accept a whole expression tree.
|
||||||
|
stack = [expr]
|
||||||
|
while len(stack) > 0:
|
||||||
|
expr = stack.pop()
|
||||||
|
expr.args = tuple(
|
||||||
|
self._rewrite_uses(expr, stack, symtab, context))
|
||||||
|
|
||||||
|
def _rewrite_defs(self, defs, symtab, context):
|
||||||
|
"""
|
||||||
|
Given a tuple of symbols defined in a Def, rewrite them to local
|
||||||
|
symbols. Yield the new locals.
|
||||||
|
"""
|
||||||
|
for sym in defs:
|
||||||
|
name = str(sym)
|
||||||
|
if name in symtab:
|
||||||
|
var = symtab[name]
|
||||||
|
if var.defctx & context:
|
||||||
|
raise AssertionError("'{}' multiply defined".format(name))
|
||||||
|
else:
|
||||||
|
var = Var(name)
|
||||||
|
symtab[name] = var
|
||||||
|
self.defs.append(var)
|
||||||
|
var.defctx |= context
|
||||||
|
yield var
|
||||||
|
|
||||||
|
def _rewrite_uses(self, expr, stack, symtab, context):
|
||||||
|
"""
|
||||||
|
Given an `Apply` expr, rewrite all uses in its arguments to local
|
||||||
|
variables. Yield a sequence of new arguments.
|
||||||
|
|
||||||
|
Append any `Apply` arguments to `stack`.
|
||||||
|
"""
|
||||||
|
for arg, operand in zip(expr.args, expr.inst.ins):
|
||||||
|
# Nested instructions are allowed. Visit recursively.
|
||||||
|
if isinstance(arg, Apply):
|
||||||
|
stack.push(arg)
|
||||||
|
yield arg
|
||||||
|
continue
|
||||||
|
if not isinstance(arg, Var):
|
||||||
|
assert not operand.is_value(), "Value arg must be `Var`"
|
||||||
|
yield arg
|
||||||
|
continue
|
||||||
|
# This is supposed to be a symbolic value reference.
|
||||||
|
name = str(arg)
|
||||||
|
if name in symtab:
|
||||||
|
var = symtab[name]
|
||||||
|
# The variable must be used consistenty as a def or input.
|
||||||
|
if var.defctx and (var.defctx & context) == 0:
|
||||||
|
raise AssertionError(
|
||||||
|
"'{}' used as both input and def"
|
||||||
|
.format(name))
|
||||||
|
else:
|
||||||
|
# First time use of variable.
|
||||||
|
var = Var(name)
|
||||||
|
symtab[name] = var
|
||||||
|
self.inputs.append(var)
|
||||||
|
yield var
|
||||||
|
|
||||||
|
|
||||||
|
class XFormGroup(object):
|
||||||
|
"""
|
||||||
|
A group of related transformations.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.xforms = list()
|
||||||
|
|
||||||
|
def legalize(self, src, dst):
|
||||||
|
"""
|
||||||
|
Add a legalization pattern to this group.
|
||||||
|
|
||||||
|
:param src: Single `Def` or `Apply` to be legalized.
|
||||||
|
:param dst: `Rtl` list of replacement instructions.
|
||||||
|
"""
|
||||||
|
self.xforms.append(XForm(Rtl(src), dst))
|
||||||
Reference in New Issue
Block a user