Skip to content

Commit c029153

Browse files
committed
Add support for using FileCheck in tests.
1 parent 636d916 commit c029153

File tree

9 files changed

+191
-0
lines changed

9 files changed

+191
-0
lines changed

test/Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
33
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
44
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
5+
IOCapture = "b5f81e59-6552-4d32-b1f0-c071b021bf89"
56
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
67
LLVM = "929cbde3-209d-540e-8aea-75f648917ca0"
8+
LLVM_jll = "86de99a1-58d6-5da7-8064-bd56ce2e322c"
79
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
810
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
911
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"

test/helpers/bpf.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ function code_native(io::IO, @nospecialize(func), @nospecialize(types); kwargs..
2525
GPUCompiler.code_native(io, job; kwargs...)
2626
end
2727

28+
# aliases without ::IO argument
29+
for method in (:code_llvm, :code_native)
30+
method = Symbol("$(method)")
31+
@eval begin
32+
$method(@nospecialize(func), @nospecialize(types); kwargs...) =
33+
$method(stdout, func, types; kwargs...)
34+
end
35+
end
36+
2837
# simulates codegen for a kernel function: validates by default
2938
function code_execution(@nospecialize(func), @nospecialize(types); kwargs...)
3039
job, kwargs = create_job(func, types; kwargs...)

test/helpers/gcn.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ function code_native(io::IO, @nospecialize(func), @nospecialize(types); kwargs..
3535
GPUCompiler.code_native(io, job; kwargs...)
3636
end
3737

38+
# aliases without ::IO argument
39+
for method in (:code_warntype, :code_llvm, :code_native)
40+
method = Symbol("$(method)")
41+
@eval begin
42+
$method(@nospecialize(func), @nospecialize(types); kwargs...) =
43+
$method(stdout, func, types; kwargs...)
44+
end
45+
end
46+
3847
# simulates codegen for a kernel function: validates by default
3948
function code_execution(@nospecialize(func), @nospecialize(types); kwargs...)
4049
job, kwargs = create_job(func, types; kernel=true, kwargs...)

test/helpers/metal.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ function code_native(io::IO, @nospecialize(func), @nospecialize(types); kwargs..
3535
GPUCompiler.code_native(io, job; kwargs...)
3636
end
3737

38+
# aliases without ::IO argument
39+
for method in (:code_warntype, :code_llvm, :code_native)
40+
method = Symbol("$(method)")
41+
@eval begin
42+
$method(@nospecialize(func), @nospecialize(types); kwargs...) =
43+
$method(stdout, func, types; kwargs...)
44+
end
45+
end
46+
3847
# simulates codegen for a kernel function: validates by default
3948
function code_execution(@nospecialize(func), @nospecialize(types); kwargs...)
4049
job, kwargs = create_job(func, types; kernel=true, kwargs...)

test/helpers/ptx.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,15 @@ function code_native(io::IO, @nospecialize(func), @nospecialize(types); kwargs..
6767
GPUCompiler.code_native(io, job; kwargs...)
6868
end
6969

70+
# aliases without ::IO argument
71+
for method in (:code_warntype, :code_llvm, :code_native)
72+
method = Symbol("$(method)")
73+
@eval begin
74+
$method(@nospecialize(func), @nospecialize(types); kwargs...) =
75+
$method(stdout, func, types; kwargs...)
76+
end
77+
end
78+
7079
# simulates codegen for a kernel function: validates by default
7180
function code_execution(@nospecialize(func), @nospecialize(types); kwargs...)
7281
job, kwargs = create_job(func, types; kernel=true, kwargs...)

test/helpers/spirv.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,15 @@ function code_native(io::IO, @nospecialize(func), @nospecialize(types); kwargs..
3838
GPUCompiler.code_native(io, job; kwargs...)
3939
end
4040

41+
# aliases without ::IO argument
42+
for method in (:code_warntype, :code_llvm, :code_native)
43+
method = Symbol("$(method)")
44+
@eval begin
45+
$method(@nospecialize(func), @nospecialize(types); kwargs...) =
46+
$method(stdout, func, types; kwargs...)
47+
end
48+
end
49+
4150
# simulates codegen for a kernel function: validates by default
4251
function code_execution(@nospecialize(func), @nospecialize(types); kwargs...)
4352
job, kwargs = create_job(func, types; kernel=true, kwargs...)

test/helpers/test.jl

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,123 @@ end
3535
ret i64 %value"""
3636
return :(Base.llvmcall($llvmcall_str, T, Tuple{T}, i))
3737
end
38+
39+
# filecheck utils
40+
41+
module FileCheck
42+
import LLVM_jll
43+
import IOCapture
44+
using GPUCompiler, LLVM
45+
using Test
46+
47+
export filecheck, @filecheck, @check_str
48+
49+
global filecheck_path::String
50+
function __init__()
51+
# TODO: Windows
52+
global filecheck_path = joinpath(LLVM_jll.artifact_dir, "tools", "FileCheck")
53+
end
54+
55+
function filecheck_exe(; adjust_PATH::Bool=true, adjust_LIBPATH::Bool=true)
56+
env = Base.invokelatest(
57+
LLVM_jll.JLLWrappers.adjust_ENV!,
58+
copy(ENV),
59+
LLVM_jll.PATH[],
60+
LLVM_jll.LIBPATH[],
61+
adjust_PATH,
62+
adjust_LIBPATH
63+
)
64+
65+
return Cmd(Cmd([filecheck_path]); env)
66+
end
67+
68+
const julia_typed_pointers = JuliaContext() do ctx
69+
supports_typed_pointers(ctx)
70+
end
71+
72+
function filecheck(f, input)
73+
# FileCheck assumes that the input is available as a file
74+
mktemp() do path, input_io
75+
write(input_io, input)
76+
close(input_io)
77+
78+
# capture the output of `f` and write it into a temporary buffer
79+
result = IOCapture.capture(rethrow=Union{}) do
80+
f(input)
81+
end
82+
output_io = IOBuffer()
83+
write(output_io, result.output)
84+
println(output_io)
85+
86+
if result.error
87+
# if the function errored, also render the exception and backtrace
88+
showerror(output_io, result.value, result.backtrace)
89+
elseif result.value !== nothing
90+
# also show the returned value; some APIs don't print
91+
write(output_io, string(result.value))
92+
end
93+
94+
# determine some useful prefixes for FileCheck
95+
prefixes = ["CHECK",
96+
"JULIA$(VERSION.major)_$(VERSION.minor)",
97+
"LLVM$(Base.libllvm_version.major)"]
98+
## whether we use typed pointers or opaque pointers
99+
if julia_typed_pointers
100+
push!(prefixes, "TYPED")
101+
else
102+
push!(prefixes, "OPAQUE")
103+
end
104+
## whether we pass pointers as integers or as actual pointers
105+
if VERSION >= v"1.12.0-DEV.225"
106+
push!(prefixes, "PTR_ABI")
107+
else
108+
push!(prefixes, "INTPTR_ABI")
109+
end
110+
111+
# now pass the collected output to FileCheck
112+
seekstart(output_io)
113+
filecheck_io = Pipe()
114+
cmd = ```$(filecheck_exe())
115+
--color
116+
--allow-unused-prefixes
117+
--check-prefixes $(join(prefixes, ','))
118+
$path```
119+
proc = run(pipeline(ignorestatus(cmd); stdin=output_io, stdout=filecheck_io, stderr=filecheck_io); wait=false)
120+
close(filecheck_io.in)
121+
122+
# collect the output of FileCheck
123+
reader = Threads.@spawn String(read(filecheck_io))
124+
Base.wait(proc)
125+
log = strip(fetch(reader))
126+
127+
# error out if FileCheck did not succeed.
128+
# otherwise, return true so that `@test @filecheck` works as expected.
129+
if !success(proc)
130+
error(log)
131+
end
132+
return true
133+
end
134+
end
135+
136+
# collect checks used in the @filecheck block by piggybacking on macro expansion
137+
const checks = String[]
138+
macro check_str(str)
139+
push!(checks, str)
140+
nothing
141+
end
142+
143+
macro filecheck(ex)
144+
ex = Base.macroexpand(__module__, ex)
145+
if isempty(checks)
146+
error("No checks provided within the @filecheck macro block")
147+
end
148+
check_str = join(checks, "\n")
149+
empty!(checks)
150+
151+
esc(quote
152+
filecheck($check_str) do _
153+
$ex
154+
end
155+
end)
156+
end
157+
end

test/setup.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ for file in readdir(joinpath(@__DIR__, "helpers"))
99
include(joinpath(@__DIR__, "helpers", file))
1010
end
1111
end
12+
using .FileCheck
1213

1314

1415
## entry point

test/utils.jl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,26 @@ end
9292
@test occursin(ansi_color, highlighted) skip = !can_highlight
9393
end
9494
end
95+
96+
# Test FileCheck
97+
@testset "FileCheck" begin
98+
@test @filecheck begin
99+
check"CHECK: works"
100+
println("works")
101+
end
102+
103+
@test_throws "expected string not found in input" @filecheck begin
104+
check"CHECK: works"
105+
println("doesn't work")
106+
end
107+
108+
@test @filecheck begin
109+
check"CHECK: errors"
110+
error("errors")
111+
end
112+
113+
@test_throws "expected string not found in input" @filecheck begin
114+
check"CHECK: works"
115+
error("errors")
116+
end
117+
end

0 commit comments

Comments
 (0)