Skip to content

Conversation

@Keno
Copy link
Member

@Keno Keno commented Jan 15, 2025

After recent changes, essentially all global symbol scopes are resolved by lowering, so the code in method.c that replaces symbols by globalrefs is no longer necessary. The one small exception to this were symbols resulting from closure conversion, because these get inserted after the point at which lowering converts symbols to globalrefs. However, in the current design, I think that's basically a bug (and easily addressed by inserting globalrefs where appropriate). Removing this extra resolution step is not particularly necessary, but for the upcoming binding partition backedges, it removes an ambiguity as to which version of the lowered code to scan. It also partially resolves a very old todo about not performing post-hoc mutation of lowered code (although we still do this for ccall).

@Keno Keno requested a review from JeffBezanson January 15, 2025 00:41
Copy link
Member

@vtjnash vtjnash left a comment

Choose a reason for hiding this comment

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

LGTM. Particularly the rename so this function finally is named after what its actual purpose is for 😅

src/method.c Outdated
Comment on lines 174 to 175
if (e->head == jl_call_sym && jl_expr_nargs(e) == 3 &&
jl_is_globalref(jl_exprarg(e, 0)) &&
Copy link
Member

Choose a reason for hiding this comment

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

Do we still care about this hack (replacing the syntax a.b with GlobalRef(a, :b) if a appears to be a module?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think that hack has been broken for a long time.

Copy link
Member

Choose a reason for hiding this comment

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

okay, yeah, do we just clean this up by deleting it too?

julia> @eval Base f() = Base.x
f (generic function with 1 method)

julia> @code_lowered Base.f()
CodeInfo(
    @ REPL[10]:1 within `unknown scope`
1 ─ %1 = Base.Base
│   %2 =   dynamic Base.getproperty(%1, :x)
└──      return %2
)

Copy link
Member Author

Choose a reason for hiding this comment

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

Fine by me. We could consider putting it into lowering though.

Copy link
Member

Choose a reason for hiding this comment

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

Lowering doesn't know the symbol will refers to a module constant, so it had to be when we evaluate the expression here

Copy link
Member Author

Choose a reason for hiding this comment

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

Alright, fair enough. In that case, I don't think we can really do this at all. With binding redefinition, our semantics do not allow us to make that decision here.

After recent changes, essentially all global symbol scopes are
resolved by lowering, so the code in method.c that replaces
symbols by globalrefs is no longer necessary. The one small
exception to this were symbols resulting from closure conversion,
because these get inserted after the point at which lowering converts
symbols to globalrefs. However, in the current design, I think
that's basically a bug (and easily addressed by inserting
`globalref`s where appropriate). Removing this extra resolution
step is not particularly necessary, but for the upcoming
binding partition backedges, it removes an ambiguity as to
which version of the lowered code to scan. It also partially
resolves a very old todo about not performing post-hoc mutation
of lowered code (although we still do this for ccall).
@Keno Keno force-pushed the kf/nopostlowerresolve branch from 6ab8ffd to f52bdb9 Compare January 15, 2025 17:26
@Keno Keno merged commit 30177d0 into master Jan 15, 2025
5 of 7 checks passed
@Keno Keno deleted the kf/nopostlowerresolve branch January 15, 2025 21:49
@oscardssmith oscardssmith added the compiler:codegen Generation of LLVM IR and native code label Jan 16, 2025
JeffBezanson pushed a commit that referenced this pull request Mar 28, 2025
…57648)

As discovered in #57547, lowering isn't resolving references to captured variables when a global of the same name is declared in any scope in the current function:

```
julia> let
            g = 1
            function f()
                let; global g = 2; end;
                return g    # g is not resolved
            end; f()
        end
ERROR: Found raw symbol in code returned from lowering. Expected all symbols to
have been resolved to GlobalRef or slots.
```

`resolve-scopes` correctly detects that we're returning the local, but scope-blocks are removed in this pass. As a result, `analyze-vars-lambda` finds more globals than `resolve-scopes` did when calling `find-global-decls` (which does not peek inside nested scopes) on `f`.  `analyze-vars-lambda` then calculates the set of captured variables in `f` to be:

```
captvars = (current_env ∩ free_vars) - (new_staticparams ∪ wrong_globals)
```

so `g` in this case is never captured.

This bug was introduced (revealed, maybe) in #57051---the whole resolution step used to happen after lowering, so lowering passes disagreeing on scopes would be less visible.

Fix: omit globals from the free variable list in `analyze-vars-lambda` in the first place.  This way we don't need to call the scope-block-dependent `find-global-decls`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

compiler:codegen Generation of LLVM IR and native code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants