Skip to content
Open
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
66 changes: 47 additions & 19 deletions pythran/optimizations/pattern_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,27 +66,37 @@
ast.Num(n=-1)],
keywords=[])),

# X * X => X ** 2
(ast.BinOp(left=Placeholder(0), op=ast.Mult(), right=Placeholder(0)),
lambda: ast.BinOp(left=Placeholder(0), op=ast.Pow(), right=ast.Num(n=2))),

# a + "..." + b => "...".join((a, b))
(ast.BinOp(left=ast.BinOp(left=Placeholder(0),
op=ast.Add(),
right=ast.Str(Placeholder(1))),
op=ast.Add(),
right=Placeholder(2)),
lambda: ast.Call(func=ast.Attribute(
ast.Attribute(
ast.Name('__builtin__', ast.Load(), None),
'str',
ast.Load()),
'join', ast.Load()),
args=[ast.Str(Placeholder(1)),
ast.Tuple([Placeholder(0), Placeholder(2)], ast.Load())],
keywords=[])),
]

# Dictionary with ast operator name as keys for each a list of tuple of
# (left, right, replacement) is defined.
# replacement have to be a lambda function to have a new ast to replace when
# replacement is inserted in main ast
know_pattern_BinOp = {
ast.Mult.__name__ : [
(Placeholder(0), ast.Num(1), lambda: Placeholder(0)), # X * 1 => X
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, I relaized that even this simple transformation is not always correct:

x = [1,2]
y = x * 1
y[0] = 2
print(x, y)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arg, damn python.. I need to find a way to make it safe...
For my test case I need somehow the replacements

y += x * 0 => y += 0 => pass

(ast.Num(1), Placeholder(0), lambda: Placeholder(0)), # 1 * X => X
(Placeholder(0), Placeholder(0), lambda: ast.BinOp(left=Placeholder(0), op=ast.Pow(), right=ast.Num(n=2))), # X * X => X ** 2
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This case is already handled by pythran.optimization.square

],
ast.Add.__name__ : [
(Placeholder(0), ast.Num(0), lambda: Placeholder(0)), # X + 0 => X
(ast.Num(0), Placeholder(0), lambda: Placeholder(0)), # 0 + X => X
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same copy issue as above, when one multiplies by one.

( # a + "..." + b => "...".join((a, b))
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one looks correct :-)

ast.BinOp(left=Placeholder(0), op=ast.Add(), right=ast.Str(Placeholder(1))),
Placeholder(2),
lambda: ast.Call(func=ast.Attribute(
ast.Attribute(ast.Name('__builtin__', ast.Load(), None),'str',ast.Load()),'join', ast.Load()),
args=[ast.Str(Placeholder(1)),ast.Tuple([Placeholder(0), Placeholder(2)], ast.Load())],
keywords=[])
),

],
ast.Sub.__name__ : [
(Placeholder(0), ast.Num(0), lambda: Placeholder(0)), # X - 0 => X
(ast.Num(0), Placeholder(0), lambda: ast.UnaryOp(op=ast.USub(), operand=Placeholder(0))), # 0 - X => -X
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same copy issue here.

],
}


class PlaceholderReplace(Transformation):

Expand Down Expand Up @@ -125,3 +135,21 @@ def visit(self, node):
node = PlaceholderReplace(check.placeholders).visit(replace())
self.update = True
return super(PatternTransform, self).visit(node)

def visit_BinOp(self, node):
"""
Special method for BinOp.
Try to replace if node match the given pattern.
"""
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you need that specialization?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought would be good for speeding-up the optimization.
As soon as I find a way to implement safely the patterns I have in mind, we may have order of 100 patterns to check for replacement.
Then, as it was, pattern transform would check for any node any given pattern careless of the structure.
In this way, instead, it goes trough BinOp just when needed and select from the dictionary the patterns relative to the used operator.

self.generic_visit(node)
op_name = node.op.__class__.__name__
if op_name in know_pattern_BinOp:
for left, right, replace in know_pattern_BinOp[op_name]:
check_left = Check(node.left, dict())
if check_left.visit(left):
check_right = Check(node.right, check_left.placeholders)
if check_right.visit(right):
node = PlaceholderReplace(check_right.placeholders).visit(replace())
self.update = True
break
return node