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

Iteration protocol change #25261

Merged
merged 2 commits into from
May 18, 2018
Merged

Iteration protocol change #25261

merged 2 commits into from
May 18, 2018

Commits on May 18, 2018

  1. Change iteration protocol

    This changes the iteration protocol from `start`/`next`/`done` to `iterate`.
    The new lowering of a for loop is as follows:
    
    ```
    for x in itr
        ...
    end
    ```
    
    becomes
    
    ```
    next = iterate(itr)
    while next !== nothing
        x, state = next::Tuple{Any, Any}
        ...
        next = iterate(itr, state)
    end
    ```
    
    The semantics are as apparent from the above lowering. `iterate` returns
    either `nothing` or a tuple of value and state. The state is passed
    to any subsequent operation. The first iteration is indicated, by not passing
    the second, state argument to the `iterate` method.
    
    Adaptors in both directions are provided to keep the legacy iteration
    protocol working for now. However, performance of the legacy iteration
    protocol will be severely pessimized.
    
    As an optional add-on for mutable iterators, a new `isdone` function is
    provided. This function is intended as an O(1) approximate query for
    iterator completion, where such a calculation is possible without mutation
    and/or is significantly faster than attempting to obtain the element itself.
    The function makes use of 3-value logic. `missing` is always an acceptable
    answer, in which case the caller should go ahead and attempt the iteration
    to obtain a definite result. If the result is not `missing`, it must be
    exact (i.e. if true, the next call to iterate must return `nothing`, if
    false it must not return nothing).
    Keno committed May 18, 2018
    2 Configuration menu
    Copy the full SHA
    1a1d6b6 View commit details
    Browse the repository at this point in the history
  2. Use loop inverted loop lowering

    The primary idea of the new iteration protocol is that for
    a function like:
    ```
    function iterate(itr)
       done(itr) ? nothing : next(itr)
    end
    ```
    we can fuse the `done` comparison into the loop condition and
    recover the same loop structure we had before (while retaining
    the flexibility of not requiring the done function to be separate),
    i.e. for
    ```
    y = iterate(itr)
    y === nothing && break
    ```
    we want to have after inlining and early optimization:
    ```
    done(itr) && break
    y = next(itr)
    ```
    LLVM performs this optimization in jump threading. However, we run
    into a problem. At the top of the loop we have:
    ```
    y = iterate
    top:
    %cond = y === nothing
    br i1 %cond, %exit, %loop
    ....
    ```
    We'd want to thread over the `top` block (this makes sense, since
    by the discussion above, we need to merge our condition into the
    loop exit condition). However, LLVM (quite sensibly) refuses to
    thread over loop headers and since `top` is both a loop header
    and a loop exit, we fail to perform the appropriate transformation.
    
    However, there's a simple fix. Instead of emitting a foor loop as
    ```
    y = iterate(itr)
    while y !== nothing
        x, state = y
        ...
        y = iterate(itr, state)
    end
    ```
    we can emit it as
    ```
    y = iterate(itr)
    if y !== nothing
        while true
           x, state = y
           ...
           y = iterate(itr, state)
           y === nothing && break
        end
    end
    ```
    This transformation is known as `loop inversion` (or a special
    case of `loop rotation`. In our case the primary benefit is
    that we can fuse the condition contained in the initial `iterate`
    call into the bypass if, which then lets LLVM understand our loop
    structure.
    
    Co-authored-by: Jeff Bezanson <[email protected]>
    Keno and Jeff Bezanson committed May 18, 2018
    Configuration menu
    Copy the full SHA
    62fbad2 View commit details
    Browse the repository at this point in the history