Skip to content

Commit c2e3809

Browse files
authored
Merge pull request #37 from timholy/teh/localmt
Towards a semi-performant recursive interpreter
2 parents 6ffc8f5 + d03e44e commit c2e3809

21 files changed

+1567
-320
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@
33
*.jl.mem
44
expected.out
55
failed.out
6+
src/builtins.jl
7+
deps/build.log
8+
docs/build/

.travis.yml

+12
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,17 @@ julia:
99
- 1.1
1010
- nightly
1111

12+
script:
13+
- julia --project -e 'using Pkg;
14+
Pkg.instantiate();
15+
Pkg.add([PackageSpec(name = "TerminalRegressionTests", rev = "master"),
16+
PackageSpec(name = "VT100", rev = "master")]);
17+
Pkg.build();
18+
Pkg.test()'
19+
20+
matrix:
21+
allow_failures:
22+
- julia: nightly
23+
1224
notifications:
1325
email: false

Manifest.toml

+1-19
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,14 @@ uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
77
deps = ["REPL"]
88
git-tree-sha1 = "e4a4693fc3fd3924d469bb0fa46215672fd6d4b8"
99
repo-rev = "master"
10-
repo-url = "https://github.com/Keno/DebuggerFramework.jl.git"
10+
repo-url = "https://github.com/JuliaDebug/DebuggerFramework.jl.git"
1111
uuid = "67417a49-6d77-5db2-98c7-c13144130cd2"
1212
version = "0.1.2+"
1313

14-
[[Distributed]]
15-
deps = ["Random", "Serialization", "Sockets"]
16-
uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b"
17-
1814
[[InteractiveUtils]]
1915
deps = ["Markdown"]
2016
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
2117

22-
[[Logging]]
23-
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"
24-
2518
[[Markdown]]
2619
deps = ["Base64"]
2720
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
@@ -30,16 +23,5 @@ uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
3023
deps = ["InteractiveUtils", "Markdown", "Sockets"]
3124
uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
3225

33-
[[Random]]
34-
deps = ["Serialization"]
35-
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
36-
37-
[[Serialization]]
38-
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
39-
4026
[[Sockets]]
4127
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"
42-
43-
[[Test]]
44-
deps = ["Distributed", "InteractiveUtils", "Logging", "Random"]
45-
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

Project.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ version = "0.1.1"
44

55
[deps]
66
DebuggerFramework = "67417a49-6d77-5db2-98c7-c13144130cd2"
7+
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
78
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
89
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
910

1011
[extras]
12+
TerminalRegressionTests = "98bfdc55-cc95-5876-a49a-74609291cbe0"
1113
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
14+
VT100 = "7774df62-37c0-5c21-b34d-f6d7f98f54bc"
1215

1316
[targets]
14-
test = ["Test"]
17+
test = ["Test", "TerminalRegressionTests", "VT100"]

deps/build.jl

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
using InteractiveUtils
2+
3+
const srcpath = joinpath(dirname(@__DIR__), "src")
4+
include(joinpath(srcpath, "generate_builtins.jl"))
5+
open(joinpath(srcpath, "builtins.jl"), "w") do io
6+
generate_builtins(io)
7+
end

docs/make.jl

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using Documenter, ASTInterpreter2
2+
3+
makedocs(
4+
modules = [ASTInterpreter2],
5+
clean = false,
6+
format = Documenter.HTML(prettyurls = get(ENV, "CI", nothing) == "true"),
7+
sitename = "ASTInterpreter2.jl",
8+
authors = "Keno Fischer, Tim Holy, and others",
9+
linkcheck = !("skiplinks" in ARGS),
10+
pages = [
11+
"Home" => "index.md",
12+
"ast.md",
13+
"internals.md",
14+
"dev_reference.md",
15+
],
16+
)
17+
18+
deploydocs(
19+
repo = "github.com/JuliaDebug/ASTInterpreter2.jl.git",
20+
)

docs/src/ast.md

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Lowered representation
2+
3+
Let's start with a demonstration on simple function:
4+
5+
```julia
6+
function summer(A::AbstractArray{T}) where T
7+
s = zero(T)
8+
for a in A
9+
s += a
10+
end
11+
return s
12+
end
13+
14+
A = [1, 2, 5]
15+
```
16+
17+
ASTIntepreter2 uses the lowered representation of code:
18+
19+
```julia
20+
julia> code = @code_lowered summer(A)
21+
CodeInfo(
22+
1 ─ s = (Main.zero)($(Expr(:static_parameter, 1)))
23+
%2 = A
24+
#temp# = (Base.iterate)(%2)
25+
%4 = #temp# === nothing
26+
%5 = (Base.not_int)(%4)
27+
└── goto #4 if not %5
28+
2%7 = #temp#
29+
│ a = (Core.getfield)(%7, 1)
30+
%9 = (Core.getfield)(%7, 2)
31+
│ s = s + a
32+
#temp# = (Base.iterate)(%2, %9)
33+
%12 = #temp# === nothing
34+
%13 = (Base.not_int)(%12)
35+
└── goto #4 if not %13
36+
3 ─ goto #2
37+
4return s
38+
)
39+
```
40+
41+
To understand this package's internals, you need to familiarize yourself with these
42+
`CodeInfo` objects. The numbers on the left correspond to [basic blocks](https://en.wikipedia.org/wiki/Basic_block);
43+
when used in statements these are printed with a hash, e.g., in `goto #4 if not %6`, the
44+
`#4` refers to basic block 4.
45+
The numbers in the next column--e.g., `%1`, refer to [single static assignment (SSA) values](https://en.wikipedia.org/wiki/Static_single_assignment_form).
46+
Each statement (each line of this printout) corresponds to a single SSA value,
47+
but only those used later in the code are printed using assignment syntax.
48+
Wherever a previous SSA value is used, it's referenced by an `SSAValue` and printed as `%6`;
49+
for example, in `goto #4 if not %6`, the `%6` is the result of evaluating the 6th statement,
50+
which is `(Base.not_int)(%5)`, which in turn refers to the result of statement 5.
51+
Together lines 5 and 6 correspond to `!(#temp# === nothing)`.
52+
(The `#temp#` means that this was a generated variable name not present explicitly in the original source code.)
53+
54+
Before diving into the details, let's first look at the statements themselves:
55+
56+
```julia
57+
julia> code.code
58+
16-element Array{Any,1}:
59+
:(_3 = (Main.zero)($(Expr(:static_parameter, 1))))
60+
:(_2)
61+
:(_4 = (Base.iterate)(%2))
62+
:(_4 === nothing)
63+
:((Base.not_int)(%4))
64+
:(unless %5 goto %16)
65+
:(_4)
66+
:(_5 = (Core.getfield)(%7, 1))
67+
:((Core.getfield)(%7, 2))
68+
:(_3 = _3 + _5)
69+
:(_4 = (Base.iterate)(%2, %9))
70+
:(_4 === nothing)
71+
:((Base.not_int)(%12))
72+
:(unless %13 goto %16)
73+
:(goto %7)
74+
:(return _3)
75+
```
76+
77+
You can see directly that the SSA assignments are implicit; they are not directly
78+
present in the statement list.
79+
The most noteworthy change here is the appearance of objects like `_3`, which are
80+
references that index into local variable slots:
81+
82+
```julia
83+
julia> code.slotnames
84+
5-element Array{Any,1}:
85+
Symbol("#self#")
86+
:A
87+
:s
88+
Symbol("#temp#")
89+
:a
90+
```
91+
92+
When printing the whole `CodeInfo` object, these `slotnames` are substituted in.
93+
The types of objects that can be in `code.code` is well-described in the [Julia AST](https://docs.julialang.org/en/latest/devdocs/ast/) documentation.

docs/src/dev_reference.md

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Function reference
2+
3+
## Top-level
4+
5+
```@docs
6+
@interpret
7+
```
8+
9+
## Frame creation
10+
11+
```@docs
12+
ASTInterpreter2.enter_call
13+
ASTInterpreter2.enter_call_expr
14+
ASTInterpreter2.build_frame
15+
ASTInterpreter2.determine_method_for_expr
16+
ASTInterpreter2.prepare_args
17+
ASTInterpreter2.prepare_call
18+
ASTInterpreter2.get_call_framecode
19+
ASTInterpreter2.optimize!
20+
```
21+
22+
## Frame execution
23+
24+
```@docs
25+
ASTInterpreter2.Compiled
26+
ASTInterpreter2.step_expr!
27+
ASTInterpreter2.finish!
28+
ASTInterpreter2.finish_and_return!
29+
ASTInterpreter2.next_until!
30+
ASTInterpreter2.evaluate_call!
31+
ASTInterpreter2.evaluate_foreigncall!
32+
ASTInterpreter2.maybe_evaluate_builtin
33+
ASTInterpreter2.@eval_rhs
34+
```
35+
36+
## Types
37+
38+
```@docs
39+
ASTInterpreter2.JuliaStackFrame
40+
ASTInterpreter2.JuliaFrameCode
41+
ASTInterpreter2.JuliaProgramCounter
42+
```
43+
44+
## Internal storage
45+
46+
```@docs
47+
ASTInterpreter2.framedict
48+
ASTInterpreter2.genframedict
49+
```
50+
51+
## Utilities
52+
53+
```@docs
54+
ASTInterpreter2.iswrappercall
55+
```

docs/src/index.md

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# ASTInterpreter2
2+
3+
This package implements an [interpreter](https://en.wikipedia.org/wiki/Interpreter_(computing)) for Julia code.
4+
Normally, Julia compiles your code when you first execute it; using ASTInterpreter2 you can
5+
avoid compilation and execute the expressions that define your code directly.
6+
Interpreters have a number of applications, including support for stepping debuggers.
7+
8+
At a pure user level, there is not much to know:
9+
10+
```jldoctest
11+
julia> using ASTInterpreter2
12+
13+
julia> a = [1, 2, 5]
14+
3-element Array{Int64,1}:
15+
1
16+
2
17+
5
18+
19+
julia> sum(a)
20+
8
21+
22+
julia> @interpret sum(a)
23+
8
24+
```
25+
26+
Those who want to dive deeper should continue reading.

0 commit comments

Comments
 (0)