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

support finding dependencies of a variable #63

Open
wants to merge 12 commits into
base: master
Choose a base branch
from

Conversation

Roger-luo
Copy link
Contributor

I think we discuss this a long time ago in slack @MikeInnes , by dispatching pullbacks or other contextual calls to only variables dependent on the result, it will at least eliminate some annoying errors for functions that does not involve global references etc.

but I'm not sure this is the right way to implement it. I guess you might have some comments. especially, I'm not sure this is right for branches.

@MikeInnes
Copy link
Member

This seems like a reasonable approach; it won't yet work for branches because ir[v] doesn't exist for block arguments. You instead have to enumerate each branch to the block and include all passed argument variables as dependencies.

It may be better to rework this to use a dataflow-based approach, which is basically like doing type inference. You walk forward over all variables keeping track of the set of dependencies (which is easy to calculate as the union of dependencies of variables in the Expr). When you get to the end of the block, you check each branch to see if its dependencies have changed, and if so add the target block to a work queue. That will correctly handle things like loops where dependencies can be complex.

It might be useful to have a little tutorial on doing data flow analyses in the docs, since this is a good approach to almost every non-trivial task on IR.

@Roger-luo
Copy link
Contributor Author

Thanks! @MikeInnes by dataflow-based approach do you mean something like Mjolnir? Or should I PR this feature into Mjolnir instead of IRTools?

@MikeInnes
Copy link
Member

No, "data flow analysis" is just a general approach to writing algorithms on IR like this; see for example https://en.wikipedia.org/wiki/Data-flow_analysis. The clearest example in IRTools is probably this function which turns all implicit inter-block variable usage into explicit block arguments. So I'm suggesting that you rework the structure of your algorithm to do a dataflow analysis in the way that I described above.

@Roger-luo Roger-luo marked this pull request as ready for review June 8, 2020 01:52
@Roger-luo
Copy link
Contributor Author

@MikeInnes I just rework this, should be ready for a review.

@Roger-luo
Copy link
Contributor Author

err... Not sure why this fails on 1.0

@Roger-luo
Copy link
Contributor Author

I think this is because the IR on 1.0 is different from later Julia versions due to different implementation of @info. So I mark that as broken.

@MikeInnes any chance to review this?

Copy link
Member

@MikeInnes MikeInnes left a comment

Choose a reason for hiding this comment

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

This is looking nice, thanks! However, I think it'd be useful to consider a test case with a loop, like

function pow(x, n)
  r = 1
  while n > 0
    n -= 1
    r *= x
  end
  return r
end

In particular dependencies currently fails on this because it always follows branches, but instead it needs to determine whether a branch actually needs to be followed (ie the dependencies of an input actually changed). I suspect that find_dependency_path would also fail if there's a circular dependency.

The current setup is a little weird because what dependencies calculates – immediate dependencies – is fairly trivial. It doesn't require a dataflow analysis and can be done efficiently for one variable at a time. So instead I think it'd make sense for dependencies to calculate all dependencies (like find_dependency_path), for all variables at once. You can do this by defining the set of dependencies as the set of immediate uses + their dependencies. When you update basic block arguments you can check if they have any new dependencies, and redo the block if there are.


function usages(ex::Expr)
uses = Set{Variable}()
prewalk(ex) do x
Copy link
Member

Choose a reason for hiding this comment

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

For Expr you could just iterate over the arguments, rather than the heavier prewalk.

test/analysis.jl Outdated
# ir = @code_ir f(1.0)

# # NOTE: this test is broken due to different IR generated by 1.0 and 1.2+
# @test_broken find_dependency_path(ir, var(48)) == Set(var.([47, 46, 42, 45, 41, 3, 4, 2]))
Copy link
Member

Choose a reason for hiding this comment

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

I would probably just avoid @info since its implementation is liable to change at any time; perhaps just write down similar code by hand?

@Roger-luo Roger-luo requested a review from MikeInnes June 28, 2020 08:00
@Roger-luo
Copy link
Contributor Author

@MikeInnes bump

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.

2 participants