Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initialize ssa decorator #324

Merged
merged 5 commits into from Jan 12, 2019
Merged

Initialize ssa decorator #324

merged 5 commits into from Jan 12, 2019

Conversation

leonardt
Copy link
Collaborator

This PR is tracking an effort to build up a library of standard decorators that
perform Python -> Python transformations, intended to be used by higher
level DSLs being constructed on top of magma.

This adds a module magma.ssa which contains a decorator @ssa which
is intended to take in a Python function and return a new Python
function in SSA form.

This includes a basic set of test cases in tests/test_ssa.

It also refactors some of the m.circuit.combinational code to be reused
across the two decorators.

Summary:
This adds a module `magma.ssa` which contains a decorator `@ssa` which
is intended to take in a Python function and return a new Python
function in SSA form.

This includes a basic set of test cases in `tests/test_ssa`

Test Plan: 

Reviewers: 

Subscribers: 

Tasks: 

Tags: 

Blame Revision: 


Differential Revision: https://phabricator.intern.facebook.com/D13458300
@phanrahan
Copy link
Owner

ast example is very cool!

@coveralls
Copy link

Pull Request Test Coverage Report for Build 1322

  • 100 of 110 (90.91%) changed or added relevant lines in 4 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.4%) to 72.472%

Changes Missing Coverage Covered Lines Changed/Added Lines %
magma/circuit_def.py 8 9 88.89%
magma/ast_utils.py 40 49 81.63%
Totals Coverage Status
Change from base Build 1318: 0.4%
Covered Lines: 3591
Relevant Lines: 4955

💛 - Coveralls

1 similar comment
@coveralls
Copy link

Pull Request Test Coverage Report for Build 1322

  • 100 of 110 (90.91%) changed or added relevant lines in 4 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.4%) to 72.472%

Changes Missing Coverage Covered Lines Changed/Added Lines %
magma/circuit_def.py 8 9 88.89%
magma/ast_utils.py 40 49 81.63%
Totals Coverage Status
Change from base Build 1318: 0.4%
Covered Lines: 3591
Relevant Lines: 4955

💛 - Coveralls

@coveralls
Copy link

coveralls commented Dec 14, 2018

Pull Request Test Coverage Report for Build 1339

  • 112 of 122 (91.8%) changed or added relevant lines in 4 files are covered.
  • 11 unchanged lines in 1 file lost coverage.
  • Overall coverage increased (+0.5%) to 72.561%

Changes Missing Coverage Covered Lines Changed/Added Lines %
magma/circuit_def.py 8 9 88.89%
magma/ast_utils.py 40 49 81.63%
Files with Coverage Reduction New Missed Lines %
magma/fromverilog.py 11 86.67%
Totals Coverage Status
Change from base Build 1318: 0.5%
Covered Lines: 3607
Relevant Lines: 4971

💛 - Coveralls

@coveralls
Copy link

Pull Request Test Coverage Report for Build 1322

  • 100 of 110 (90.91%) changed or added relevant lines in 4 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.4%) to 72.472%

Changes Missing Coverage Covered Lines Changed/Added Lines %
magma/circuit_def.py 8 9 88.89%
magma/ast_utils.py 40 49 81.63%
Totals Coverage Status
Change from base Build 1318: 0.4%
Covered Lines: 3591
Relevant Lines: 4955

💛 - Coveralls

@rsetaluri
Copy link
Collaborator

Cool! Is this meant to be used in conjunction with the combinational decorator? If so can you include an example of how they would both be used and what a useful use case/target output is?

magma/ssa/ssa.py Outdated
def visit_Name(self, node):
if isinstance(node.ctx, ast.Store):
self.var_counter[node.id] += 1
if isinstance(node.ctx, ast.Store) or node.id in self.var_counter:
Copy link
Collaborator

Choose a reason for hiding this comment

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

if isinstance(node.ctx, ast.Store) or node.id in self.var_counter:

Redundant check on if it's a store, no?

node.body = flatten([self.visit(s) for s in node.body])
return node

def visit_Name(self, node):
Copy link
Collaborator

Choose a reason for hiding this comment

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

You might want to rename inputs.

For example, this is handled incorrectly:

@ssa
def default(I: m.Bits(2), x_0: m.Bit) -> m.Bit:
    x = I[1]
    if x_0:
        x = I[0]
    return x

Copy link
Collaborator

Choose a reason for hiding this comment

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

If you want to preserve interface names, I would suggest inserting input renamings as the first thing in the SSA-ified code, so the output might reasonably look like this:

def default(I: m.Bits(2), x_0: m.Bit) -> m.Bit:
    I_0, x_0_0 = I, x_0 # this is done in a single tuple assign to avoid things like def default(x_0: m.Bits(2), x_0_0: m.Bit), which would make x_0_0 = x_0, x_0_0_0 = x_0_0, which is also wrong
    # rest of ssa translated code here using new input names...

Copy link
Collaborator

Choose a reason for hiding this comment

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

The first example I posted generates this right now:

def default(I: m.Bits(2), x_0: m.Bit) ->m.Bit:
    x_0 = I[1]
    x_1 = I[0]
    x_2 = phi([x_0, x_1], x_0)
    return x_2

Copy link
Collaborator

Choose a reason for hiding this comment

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

Here's a test for the edge case I mentioned in the second comment:

@ssa
def default(x_0: m.Bits(2), x_0_0: m.Bit) -> m.Bit:
    x = x_0[1]
    if x_0_0:
        x = x_0[0]
    return x

Copy link
Collaborator

Choose a reason for hiding this comment

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

Just going to piggyback on this and say it might be a bigger issue than you think, if this is intended to be used in conjunction with other passes, I wouldn't be surprised if there would be renaming of variables happening in other passes.

For example, creating a wrapper module - I might rename the wires on the internal one by appending _0 to the inputs if I need to modify the wires before feeding them into the internal module. Rigel did something like this. Combined with the ssa pass things would break in ways that would be pretty odd.

node.id += f"_{self.var_counter[node.id]}"
return node

def visit_If(self, node):
Copy link
Collaborator

@hofstee hofstee Dec 20, 2018

Choose a reason for hiding this comment

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

I think this (the visit_If function) might be made simpler if you kept track of two things, a dict with the counters for stores, and a dict mapping the variable to its last name for loads.

This way you would preserve the store dict across both branches in the if, and reset the load mapping dict for each branch. This would also eliminate the need to go through everything in the false_var_counter and remap variable store names.

The phi logic would still need to be roughly the same though, but you would have the two load mappings and then you would insert a phi for every entry where the last load name is different between the two dicts for a variable.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Here's another bug case I think:

def test_wat():
    @ssa
    def basic_if(I: m.Bit, S: m.Bit) -> m.Bit:
        x = I
        if S:
            x = x
        else:
            x = x
        return x

    print(inspect.getsource(basic_if))
    assert inspect.getsource(basic_if) == """\
def basic_if(I: m.Bits(2), S: m.Bit) ->m.Bit:
    x_0 = I
    x_1 = x_0
    x_2 = x_0
    x_3 = phi([x_2, x_1], S)
    return x_3
"""

Copy link
Collaborator

@hofstee hofstee Jan 9, 2019

Choose a reason for hiding this comment

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

Should this phi be the other way around?

def test_default():
    @ssa
    def default(I: m.Bits(2), S: m.Bit) -> m.Bit:
        x = I[1]
        if S:
            x = I[0]
        return x

    assert inspect.getsource(default) == """\
def default(I: m.Bits(2), S: m.Bit) ->m.Bit:
    x_0 = I[1]
    x_1 = I[0]
    x_2 = phi([x_0, x_1], S)
    return x_2
"""

Copy link
Collaborator

Choose a reason for hiding this comment

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

Nevermind on that last point.

node.id += f"_{self.var_counter[node.id]}"
return node

def visit_If(self, node):
Copy link
Collaborator

@hofstee hofstee Jan 9, 2019

Choose a reason for hiding this comment

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

Should this phi be the other way around?

def test_default():
    @ssa
    def default(I: m.Bits(2), S: m.Bit) -> m.Bit:
        x = I[1]
        if S:
            x = I[0]
        return x

    assert inspect.getsource(default) == """\
def default(I: m.Bits(2), S: m.Bit) ->m.Bit:
    x_0 = I[1]
    x_1 = I[0]
    x_2 = phi([x_0, x_1], S)
    return x_2
"""

@leonardt
Copy link
Collaborator Author

@THofstee looks like the tests pass when you renamed the inputs, but that's because it only checks that the generated code makes sense. The changes you pushed look good.

I think this is okay, since this logic/tests are self contained in the sense that they don't test how this affects use with magma, but that's by design (in case someone wants to use this for some other DSL). Basically, I think any magma logic (say m.circuit.combinational) should be aware that the ssa transform will change the input names. It's the responsibility of the user to handle this issue (so as a first pass m.circuit.combinational could generate wrappers to handle the name change, and then we can add better logic to remove the need for wrappers in the generated code so it is less verbose).

Can you confirm if you think the input name issue has been resolved? Are there are any other issues we should be looking in to? Otherwise, I vote we merge and start using it and open up specific issues as they arise.

@hofstee
Copy link
Collaborator

hofstee commented Jan 11, 2019

I addressed all the comments I made in my review so I don't think there's anything else for the time being.

@hofstee hofstee merged commit 2cae041 into master Jan 12, 2019
@leonardt leonardt deleted the ssa branch February 5, 2019 18:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants