JuliaLabs / Cassette.jl

Overdub Your Julia Code
Other
371 stars 35 forks source link

less helpful stacktraces on 1.6 #185

Closed simeonschaub closed 3 years ago

simeonschaub commented 3 years ago

As reported in https://github.com/JuliaLang/julia/pull/37954#issuecomment-778794119, #37954 seems to have regressed stacktraces of inlined overdubs, so they show up as overdub instead of as the overdubbed function itself. From the discussion with @vchuravy on Slack:

I think the issue is that the we are not inlining 10:32 But reflecting on the method and then emitting code based of that reflection 10:33 So previously that looked like inlining to the compiler 10:33 But now it reflects what is happening accurately 10:33 So we might need to inject an inlining frame into the reflected CodeInfo 10:34 At least that is my looking into my crystal ball 10:34 There is a Mini-Cassette in the base test 10:36 So the thing to do would be to reproduce there, write a test for it and then fix it in the mini Cassette 10:36 Then we can take that fix to Cassette proper and we can reasonably be certain that it won't regress

Simeon Schaub 10:38 PM Ah, I see, so we basically want to trick Julia into thinking this was a different function than the one actually called?

Valentin Churavy:juliaspinner: 10:59 PM Jup

10:59 We could also teach the compiler more about Cassette 11:00 But if we can fix it on the Cassette level I would prefer that

simeonschaub commented 3 years ago

Hmm, interesting... I have made a more minimal reproducer: Julia 1.5:

julia> foo() = error()
foo (generic function with 1 method)

julia> @generated function bar()
           ci = copy(code_lowered(foo, Tuple{})[])
           ci.linetable[1] = Core.LineInfoNode(:baz, :f, 1, 0)
           ci.inlineable = true
           return ci
       end
bar (generic function with 1 method)

julia> foo2() = bar()
foo2 (generic function with 1 method)

julia> @generated function bar2()
           ci = copy(code_lowered(foo2, Tuple{})[])
           ci.linetable[1] = Core.LineInfoNode(:baz2, :f, 1, 0)
           return ci
       end
bar2 (generic function with 1 method)

julia> bar2()
ERROR: 
Stacktrace:
 [1] error() at ./error.jl:42
 [2] baz2 at ./f:1 [inlined]
 [3] bar2() at ./REPL[4]:0
 [4] top-level scope at REPL[5]:1

Latest master:

julia> foo() = error()
foo (generic function with 1 method)

julia> @generated function bar()
           ci = copy(code_lowered(foo, Tuple{})[])
           ci.linetable[1] = Core.LineInfoNode(Main, :baz, :f, 1, 0)
           ci.inlineable = true
           return ci
       end
bar (generic function with 1 method)

julia> foo2() = bar()
foo2 (generic function with 1 method)

julia> @generated function bar2()
           ci = copy(code_lowered(foo2, Tuple{})[])
           ci.linetable[1] = Core.LineInfoNode(Main, :baz2, :f, 1, 0)
           return ci
       end
bar2 (generic function with 1 method)

julia> bar2()
ERROR: 
Stacktrace:
 [1] error()
   @ Base ./error.jl:42
 [2] bar2
   @ ./f:1 [inlined]
 [3] bar2()
   @ Main ./REPL[19]:0
 [4] top-level scope
   @ REPL[20]:1

I am increasingly thinking that this might not be fixable in Cassette alone, since I think the way this now works, for function names in stacktraces we now always take the LineInfoNode from the initial lowering of the generated function itself, not the output of the GeneratedFunctionStub, which we can't really modify inside the body of the generated function. I still don't fully understand all of this, so I could also very well be wrong here.

vchuravy commented 3 years ago

@jeffbezanson @vtjnash any ideas if we can fix this on the Cassette side?

simeonschaub commented 3 years ago

(Note that the 1.5 behavior also seems kind of broken, since I would have expected the second entry to show up as bar instead of bar2.) The best thing here really might be to support this properly in base and have a way of including the whole signature of inlined functions in stacktraces as well. Not sure how difficult that would be to add though and whether there might be other issues complicating this.