Skip to content

Commit 7508239

Browse files
authored
Merge pull request #124 from JuliaDebug/teh/ll
Linked-list implementation
2 parents 2561491 + 5312b84 commit 7508239

19 files changed

+1095
-927
lines changed

docs/src/dev_reference.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ JuliaInterpreter.get_call_framecode
2121
JuliaInterpreter.optimize!
2222
```
2323

24+
## Frame traversal
25+
26+
```@docs
27+
root
28+
leaf
29+
```
30+
2431
## Frame execution
2532

2633
```@docs
@@ -33,8 +40,10 @@ JuliaInterpreter.get_return
3340
JuliaInterpreter.next_until!
3441
JuliaInterpreter.through_methoddef_or_done!
3542
JuliaInterpreter.evaluate_call!
36-
JuliaInterpreter.evaluate_foreigncall!
43+
JuliaInterpreter.evaluate_foreigncall
3744
JuliaInterpreter.maybe_evaluate_builtin
45+
JuliaInterpreter.maybe_next_call!
46+
JuliaInterpreter.handle_err
3847
```
3948

4049
## Breakpoints
@@ -45,14 +54,16 @@ breakpoint
4554
enable
4655
disable
4756
remove
57+
JuliaInterpreter.dummy_breakpoint
4858
```
4959

5060
## Types
5161

5262
```@docs
53-
JuliaInterpreter.JuliaStackFrame
54-
JuliaInterpreter.JuliaFrameCode
55-
JuliaInterpreter.JuliaProgramCounter
63+
JuliaInterpreter.Frame
64+
JuliaInterpreter.FrameCode
65+
JuliaInterpreter.FrameData
66+
JuliaInterpreter.FrameInstance
5667
JuliaInterpreter.BreakpointState
5768
JuliaInterpreter.BreakpointRef
5869
```
@@ -69,11 +80,12 @@ JuliaInterpreter.compiled_methods
6980

7081
```@docs
7182
JuliaInterpreter.@lookup
72-
JuliaInterpreter.iswrappercall
83+
JuliaInterpreter.is_wrapper_call
7384
JuliaInterpreter.is_doc_expr
7485
JuliaInterpreter.is_global_ref
86+
CodeTracking.whereis
87+
JuliaInterpreter.linenumber
7588
JuliaInterpreter.statementnumber
7689
JuliaInterpreter.Variable
7790
JuliaInterpreter.locals
78-
CodeTracking.whereis
7991
```

docs/src/internals.md

Lines changed: 32 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -9,35 +9,25 @@ let's build a frame:
99

1010
```julia
1111
julia> frame = JuliaInterpreter.enter_call(summer, A)
12-
JuliaStackFrame(JuliaInterpreter.JuliaFrameCode(summer(A::AbstractArray{T,N} where N) where T in Main at REPL[1]:2, CodeInfo(
13-
1 ─ s = ($(QuoteNode(zero)))($(Expr(:static_parameter, 1)))
14-
%2 = A
15-
#temp# = ($(QuoteNode(iterate)))(%2)
16-
%4 = ($(QuoteNode(===)))(#temp#, nothing)
17-
%5 = ($(QuoteNode(not_int)))(%4)
18-
└── goto #4 if not %5
19-
2%7 = #temp#
20-
│ a = ($(QuoteNode(getfield)))(%7, 1)
21-
%9 = ($(QuoteNode(getfield)))(%7, 2)
22-
│ s = ($(QuoteNode(+)))(s, a)
23-
#temp# = ($(QuoteNode(iterate)))(%2, %9)
24-
%12 = ($(QuoteNode(===)))(#temp#, nothing)
25-
%13 = ($(QuoteNode(not_int)))(%12)
26-
└── goto #4 if not %13
27-
3 ─ goto #2
28-
4return s
29-
), Core.TypeMapEntry[#undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef], BitSet([2, 4, 5, 7, 9, 12, 13]), false, false, true), Union{Nothing, Some{Any}}[Some(summer), Some([1, 2, 5]), nothing, nothing, nothing], Any[#undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef], Any[Int64], Int64[], Base.RefValue{Any}(nothing), Base.RefValue{JuliaInterpreter.JuliaProgramCounter}(JuliaProgramCounter(1)), Dict{Symbol,Int64}(), Any[])
12+
Frame for summer(A::AbstractArray{T,N} where N) where T in Main at REPL[2]:2
13+
1* 2 1 ─ s = (zero)($(Expr(:static_parameter, 1)))
14+
2 3%2 = A
15+
3 3#temp# = (iterate)(%2)
16+
17+
A = [1, 2, 5]
18+
T = Int64
3019
```
3120

32-
This is a [`JuliaInterpreter.JuliaStackFrame`](@ref). The `CodeInfo` is the most prominent part of this display,
33-
and extractable as `code = frame.code.code`. (It's a slightly modified form of one returned by `@code_lowered`,
21+
This is a [`Frame`](@ref). Only a portion of the `CodeInfo` is shown, a small region surrounding
22+
the current statement (marked with `*` or in yellow text). The full `CodeInfo` can be extracted
23+
as `code = frame.framecode.src`. (It's a slightly modified form of one returned by `@code_lowered`,
3424
in that it has been processed by [`JuliaInterpreter.optimize!`](@ref) to speed up run-time execution.)
3525

36-
Much of the rest of the `frame` holds values needed for or generated by execution.
26+
`frame` has another field, `framedata`, that holds values needed for or generated by execution.
3727
The input arguments are in `locals`:
3828

3929
```julia
40-
julia> frame.locals
30+
julia> frame.framedata.locals
4131
5-element Array{Union{Nothing, Some{Any}},1}:
4232
Some(summer)
4333
Some([1, 2, 5])
@@ -49,10 +39,10 @@ julia> frame.locals
4939
These correspond to the `code.slotnames`; the first is the `#self#` argument and the second
5040
is the input array. The remaining local variables (e.g., `s` and `a`), have not yet been assigned---we've
5141
only built the frame, but we haven't yet begun to execute it.
52-
The static parameter, `T`, is stored in `frame.sparams`:
42+
The static parameter, `T`, is stored in `frame.framedata.sparams`:
5343

5444
```julia
55-
julia> frame.sparams
45+
julia> frame.framedata.sparams
5646
1-element Array{Any,1}:
5747
Int64
5848
```
@@ -62,7 +52,7 @@ The `Expr(:static_parameter, 1)` statement refers to this value.
6252
The other main storage is for the generated SSA values:
6353

6454
```julia
65-
julia> frame.ssavalues
55+
julia> frame.framedata.ssavalues
6656
16-element Array{Any,1}:
6757
#undef
6858
#undef
@@ -88,32 +78,26 @@ The other main entity is the so-called [program counter](https://en.wikipedia.or
8878
which just indicates the next statement to be executed:
8979

9080
```julia
91-
julia> frame.pc[]
92-
JuliaProgramCounter(1)
81+
julia> frame.pc
82+
1
9383
```
9484

95-
This is stored as a `Ref` so that it can be updated as execution progresses.
96-
97-
Let's try executing the first statement. So that we can recurse into calls (e.g., `iterate`, `+`, etc.,),
98-
we'll create a [stack](https://en.wikipedia.org/wiki/Call_stack) of frames and then run the first statement:
85+
Let's try executing the first statement:
9986

10087
```julia
101-
julia> stack = JuliaInterpreter.JuliaStackFrame[]
102-
0-element Array{JuliaStackFrame,1}
103-
104-
julia> JuliaInterpreter.step_expr!(stack, frame)
105-
JuliaProgramCounter(2)
88+
julia> JuliaInterpreter.step_expr!(frame)
89+
2
10690
```
10791

10892
This indicates that it ran statement 1 and is prepared to run statement 2.
10993
(It's worth noting that the first line included a `call` to `zero`, so behind the scenes
110-
JuliaInterpreter pushed this frame onto `stack`, created a new frame for `zero`,
111-
executed all the statements, and then popped the stack.)
94+
JuliaInterpreter created a new frame for `zero`, executed all the statements, and then popped
95+
back to `frame`.)
11296
Since the first statement is an assignment of a local variable, let's check the
11397
locals again:
11498

11599
```julia
116-
julia> frame.locals
100+
julia> frame.framedata.locals
117101
5-element Array{Union{Nothing, Some{Any}},1}:
118102
Some(summer)
119103
Some([1, 2, 5])
@@ -128,10 +112,10 @@ The next statement just retrieves one of the slots (the input argument `A`) and
128112
it in an SSA value:
129113

130114
```julia
131-
julia> JuliaInterpreter.step_expr!(stack, frame)
132-
JuliaProgramCounter(3)
115+
julia> JuliaInterpreter.step_expr!(frame)
116+
3
133117

134-
julia> frame.ssavalues
118+
julia> frame.framedata.ssavalues
135119
16-element Array{Any,1}:
136120
#undef
137121
[1, 2, 5]
@@ -152,9 +136,9 @@ julia> frame.ssavalues
152136
```
153137

154138
One can easily continue this until execution completes, which is indicated when `step_expr!`
155-
returns `nothing`. Alternatively, use the higher-level `JuliaInterpreter.finish!(stack, frame)`
139+
returns `nothing`. Alternatively, use the higher-level `JuliaInterpreter.finish!(frame)`
156140
to step through the entire frame,
157-
or `JuliaInterpreter.finish_and_return!(stack, frame)` to also obtain the return value.
141+
or `JuliaInterpreter.finish_and_return!(frame)` to also obtain the return value.
158142

159143
## More complex expressions
160144

@@ -171,7 +155,7 @@ ex = quote
171155
end
172156
173157
frame = JuliaInterpreter.prepare_thunk(Main, ex)
174-
JuliaInterpreter.finish_and_return!(JuliaStackFrame[], frame)
158+
JuliaInterpreter.finish_and_return!(frame)
175159
176160
# output
177161
@@ -196,7 +180,7 @@ Here's a demonstration of the problem:
196180
```julia
197181
ex = :(map(x->x^2, [1, 2, 3]))
198182
frame = JuliaInterpreter.prepare_thunk(Main, ex)
199-
julia> JuliaInterpreter.finish_and_return!(JuliaStackFrame[], frame)
183+
julia> JuliaInterpreter.finish_and_return!(frame)
200184
ERROR: this frame needs to be run a top level
201185
```
202186

@@ -235,7 +219,7 @@ function" for this type, equivalent to `(##17#18)(x) = x^2`.
235219
In some cases one can fix this simply by indicating that we want to run this frame at top level:
236220
237221
```julia
238-
julia> JuliaInterpreter.finish_and_return!(JuliaStackFrame[], frame, true)
222+
julia> JuliaInterpreter.finish_and_return!(frame, true)
239223
3-element Array{Int64,1}:
240224
1
241225
4
@@ -247,11 +231,10 @@ for more complex situations where there may be nested calls of new methods):
247231
248232
```julia
249233
modexs, _ = JuliaInterpreter.split_expressions(Main, ex)
250-
stack = JuliaStackFrame[]
251234
for (mod, e) in modexs
252235
frame = JuliaInterpreter.prepare_thunk(mod, e)
253236
while true
254-
JuliaInterpreter.through_methoddef_or_done!(stack, frame) === nothing && break
237+
JuliaInterpreter.through_methoddef_or_done!(frame) === nothing && break
255238
end
256239
JuliaInterpreter.get_return(frame)
257240
end

src/JuliaInterpreter.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ using Random.DSFMT
1212
using InteractiveUtils
1313
using CodeTracking
1414

15-
export @interpret, Compiled, JuliaStackFrame,
16-
Breakpoints, breakpoint, @breakpoint, breakpoints, enable, disable, remove
15+
export @interpret, Compiled, Frame, root, leaf,
16+
BreakpointRef, breakpoint, @breakpoint, breakpoints, enable, disable, remove
1717

1818
module CompiledCalls
1919
# This module is for handling intrinsics that must be compiled (llvmcall)
@@ -26,6 +26,7 @@ include("localmethtable.jl")
2626
include("interpret.jl")
2727
include("builtins-julia$(Int(VERSION.major)).$(Int(VERSION.minor)).jl")
2828
include("optimize.jl")
29+
include("commands.jl")
2930
include("breakpoints.jl")
3031

3132
function set_compiled_methods()

src/breakpoints.jl

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,37 +21,37 @@ function add_breakpoint(framecode, stmtidx)
2121
return bp
2222
end
2323

24-
function shouldbreak(frame, pc=frame.pc[])
25-
idx = convert(Int, pc)
26-
isassigned(frame.code.breakpoints, idx) || return false
27-
bp = frame.code.breakpoints[idx]
24+
function shouldbreak(frame::Frame, pc::Int)
25+
bps = frame.framecode.breakpoints
26+
isassigned(bps, pc) || return false
27+
bp = bps[pc]
2828
bp.isactive || return false
2929
return Base.invokelatest(bp.condition, frame)::Bool
3030
end
3131

32-
function prepare_slotfunction(framecode::JuliaFrameCode, body::Union{Symbol,Expr})
32+
function prepare_slotfunction(framecode::FrameCode, body::Union{Symbol,Expr})
3333
ismeth = framecode.scope isa Method
3434
uslotnames = Set{Symbol}()
3535
slotnames = Symbol[]
36-
for name in framecode.code.slotnames
36+
for name in framecode.src.slotnames
3737
if name uslotnames
3838
push!(slotnames, name)
3939
push!(uslotnames, name)
4040
end
4141
end
42-
assignments = Expr[]
43-
framename = gensym("frame")
42+
framename, dataname = gensym("frame"), gensym("data")
43+
assignments = Expr[:($dataname = $framename.framedata)]
4444
default = Unassigned()
4545
for i = 1:length(slotnames)
46-
slotname = framecode.code.slotnames[i]
46+
slotname = framecode.src.slotnames[i]
4747
qslotname = QuoteNode(slotname)
48-
getexpr = :(something($framename.locals[$framename.last_reference[$qslotname]]))
49-
push!(assignments, Expr(:(=), slotname, :(haskey($framename.last_reference, $qslotname) ? $getexpr : $default)))
48+
getexpr = :(something($dataname.locals[$dataname.last_reference[$qslotname]]))
49+
push!(assignments, Expr(:(=), slotname, :(haskey($dataname.last_reference, $qslotname) ? $getexpr : $default)))
5050
end
5151
if ismeth
5252
syms = sparam_syms(framecode.scope)
5353
for i = 1:length(syms)
54-
push!(assignments, Expr(:(=), syms[i], :($framename.sparams[$i])))
54+
push!(assignments, Expr(:(=), syms[i], :($dataname.sparams[$i])))
5555
end
5656
end
5757
funcname = ismeth ? gensym("slotfunction") : gensym(Symbol(framecode.scope.name, "_slotfunction"))
@@ -62,8 +62,8 @@ const Condition = Union{Nothing,Expr,Tuple{Module,Expr}}
6262
_unpack(condition) = isa(condition, Expr) ? (Main, condition) : condition
6363

6464
## The fundamental implementations of breakpoint-setting
65-
function breakpoint!(framecode::JuliaFrameCode, pc, condition::Condition=nothing)
66-
stmtidx = convert(Int, pc)
65+
function breakpoint!(framecode::FrameCode, pc, condition::Condition=nothing)
66+
stmtidx = pc
6767
if condition === nothing
6868
framecode.breakpoints[stmtidx] = BreakpointState()
6969
else
@@ -73,8 +73,8 @@ function breakpoint!(framecode::JuliaFrameCode, pc, condition::Condition=nothing
7373
end
7474
return add_breakpoint(framecode, stmtidx)
7575
end
76-
breakpoint!(frame::JuliaStackFrame, pc=frame.pc[], condition::Condition=nothing) =
77-
breakpoint!(frame.code, pc, condition)
76+
breakpoint!(frame::Frame, pc=frame.pc, condition::Condition=nothing) =
77+
breakpoint!(frame.framecode, pc, condition)
7878

7979
"""
8080
enable(bp::BreakpointRef)

0 commit comments

Comments
 (0)