Skip to content

Commit

Permalink
Test system for testing IR
Browse files Browse the repository at this point in the history
New system for testing generated IR based on pretty printing the IR and
comparing to a reference string. String comparison is an excellent
way to do IR comparisons because
* It's very simple to implement
* Writing test cases is simple
* It's easy to pinpoint errors in tests
* Irrelevant detail can easily be omitted in the pretty printing

The main downside is that, in principle, the pretty printing may have
ambiguities, and changing the pretty printing causes high churn in test
cases. However these seem like reasonable tradeoffs, given the benefits.
  • Loading branch information
c42f committed Jun 26, 2024
1 parent d7289aa commit dafaad7
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 88 deletions.
85 changes: 1 addition & 84 deletions test/branching.jl
Original file line number Diff line number Diff line change
Expand Up @@ -290,90 +290,7 @@ end
#-------------------------------------------------------------------------------
@testset "Detailed lowering tests" begin

@test ir_as_text(test_mod, """
begin
local a, b
if a
b
end
end
""") == """
slot.₁/a
(gotoifnot ssa.₁ label.₅)
slot.₂/b
(return ssa.₃)
core.nothing
(return ssa.₅)"""

@test ir_as_text(test_mod, """
begin
local a, b, c
if a
b
end
c
end
""") == """
slot.₁/a
(gotoifnot ssa.₁ label.₄)
slot.₂/b
slot.₃/c
(return ssa.₄)"""

@test ir_as_text(test_mod, """
begin
local a, b, c
if a
b
else
c
end
end
""") == """
slot.₁/a
(gotoifnot ssa.₁ label.₅)
slot.₂/b
(return ssa.₃)
slot.₃/c
(return ssa.₅)"""

@test ir_as_text(test_mod, """
begin
local a, b, c, d
if a
b
else
c
end
d
end
""") == """
slot.₁/a
(gotoifnot ssa.₁ label.₅)
slot.₂/b
(goto label.₆)
slot.₃/c
slot.₄/d
(return ssa.₆)"""

# Blocks compile directly to branches
@test ir_as_text(test_mod, """
begin
local a, b, c, d
if (a; b && c)
d
end
end
""") == """
slot.₁/a
slot.₂/b
(gotoifnot ssa.₂ label.₈)
slot.₃/c
(gotoifnot ssa.₄ label.₈)
slot.₄/d
(return ssa.₆)
core.nothing
(return ssa.₈)"""
test_ir_cases(joinpath(@__DIR__,"branching_ir.jl"))

end

Expand Down
88 changes: 88 additions & 0 deletions test/branching_ir.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
######################################
# Basic branching tail && value
begin
local a, b
if a
b
end
end
#-------------------------
1 slot.₁/a
2 (gotoifnot ssa.₁ label.₅)
3 slot.₂/b
4 (return ssa.₃)
5 core.nothing
6 (return ssa.₅)

######################################
# Branching, !tail && !value
begin
local a, b, c
if a
b
end
c
end
#-------------------------
1 slot.₁/a
2 (gotoifnot ssa.₁ label.₄)
3 slot.₂/b
4 slot.₃/c
5 (return ssa.₄)

######################################
# Branching with else
begin
local a, b, c
if a
b
else
c
end
end
#---------------------
1 slot.₁/a
2 (gotoifnot ssa.₁ label.₅)
3 slot.₂/b
4 (return ssa.₃)
5 slot.₃/c
6 (return ssa.₅)

######################################
# Branching with else, !tail && !value
begin
local a, b, c, d
if a
b
else
c
end
d
end
#---------------------
1 slot.₁/a
2 (gotoifnot ssa.₁ label.₅)
3 slot.₂/b
4 (goto label.₆)
5 slot.₃/c
6 slot.₄/d
7 (return ssa.₆)

######################################
# Blocks compile directly to branches
begin
local a, b, c, d
if (a; b && c)
d
end
end
#---------------------
1 slot.₁/a
2 slot.₂/b
3 (gotoifnot ssa.₂ label.₈)
4 slot.₃/c
5 (gotoifnot ssa.₄ label.₈)
6 slot.₄/d
7 (return ssa.₆)
8 core.nothing
9 (return ssa.₈)
2 changes: 2 additions & 0 deletions test/loops.jl
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ break
continue
""")

test_ir_cases(joinpath(@__DIR__, "loops_ir.jl"))

# TODO: Test scope rules

end
37 changes: 33 additions & 4 deletions test/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,37 @@ format_as_ast_macro(ex) = format_as_ast_macro(stdout, ex)

#-------------------------------------------------------------------------------

# Parse and lower `src`, and print statements from the linear IR in text format
function ir_as_text(mod, src)
ex = JuliaLowering.lower(mod, parsestmt(SyntaxTree, src))
join(string.(children(ex[1])), "\n")
# Test tools

function match_ir_test_case(case_str)
m = match(r"# *([^\n]*)\n((?:.|\n)*)#----*\n((?:.|\n)*)"m, strip(case_str))
if isnothing(m)
error("Malformatted IR test case:\n$(repr(case_str))")
end
(name=strip(m[1]), input=strip(m[2]), output=strip(m[3]))
end

function format_ir_test_case(mod, input)
ex = parsestmt(SyntaxTree, input)
x = JuliaLowering.lower(mod, ex)
output = strip(sprint(JuliaLowering.print_ir, x))
output = replace(output, string(mod)=>"TestMod")
end

function test_ir_cases(filename)
str = read(filename, String)
cases = [match_ir_test_case(s) for s in split(str, r"####*") if strip(s) != ""]

mod = Module(:TestMod)
for (name,input,ref) in cases
output = format_ir_test_case(mod, input)
@testset "$name" begin
if output != ref
# Do our own error dumping, as @test will
@error "Test \"$name\" failed" output=Text(output) ref=Text(ref)
end
@test output == ref
end
end
end

0 comments on commit dafaad7

Please sign in to comment.