Skip to content

Commit a3b06bb

Browse files
Merge pull request #32 from brenhinkeller/test_readme_compilation
Improve test coverage, various bugfixes
2 parents 00d8deb + cd011f9 commit a3b06bb

File tree

12 files changed

+133
-110
lines changed

12 files changed

+133
-110
lines changed

.github/workflows/CompatHelper.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: CompatHelper
22
on:
33
schedule:
4-
- cron: 0 0 * * *
4+
- cron: 0 0 * * 0
55
workflow_dispatch:
66
jobs:
77
CompatHelper:

README.md

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,28 @@
11
# StaticTools
22

3-
[![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://brenhinkeller.github.io/StaticTools.jl/dev) [![CI](https://github.com/brenhinkeller/StaticTools.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/brenhinkeller/StaticTools.jl/actions/workflows/CI.yml)
4-
[![CI (Integration)](https://github.com/brenhinkeller/StaticTools.jl/workflows/CI%20(Integration)/badge.svg)](https://github.com/brenhinkeller/StaticTools.jl/actions/workflows/CI-integration.yml)
5-
[![CI (Julia nightly)](https://github.com/brenhinkeller/StaticTools.jl/workflows/CI%20(Julia%20nightly)/badge.svg)](https://github.com/brenhinkeller/StaticTools.jl/actions/workflows/CI-julia-nightly.yml)
6-
[![CI (Integration nightly)](https://github.com/brenhinkeller/StaticTools.jl/workflows/CI%20(Integration%20nightly)/badge.svg)](https://github.com/brenhinkeller/StaticTools.jl/actions/workflows/CI-integration-nightly.yml)
7-
[![Coverage](https://codecov.io/gh/brenhinkeller/StaticTools.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/brenhinkeller/StaticTools.jl)
3+
[![Docs][docs-dev-img]][docs-dev-url]
4+
[![CI][ci-img]][ci-url]
5+
[![CI (Integration)][ci-integration-img]][ci-integration-url]
6+
[![CI (Julia nightly)][ci-nightly-img]][ci-nightly-url]
7+
[![CI (Integration nightly)][ci-integration-nightly-img]][ci-integration-nightly-url]
8+
[![Coverage][codecov-img]][codecov-url]
9+
810

911
Tools to enable [StaticCompiler.jl](https://github.com/tshort/StaticCompiler.jl)-based static compilation of Julia code (or more accurately, a subset of Julia which we might call "unsafe Julia") to standalone native binaries by avoiding GC allocations and `llvmcall`-ing all the things! (Experimental! 🐛)
1012

1113
This package currently requires Julia 1.8 or greater for best results (if in doubt, check [which versions are passing CI](https://github.com/brenhinkeller/StaticTools.jl/actions?query=workflow%3ACI++)). Integration tests against StaticCompiler.jl and LoopVectorization.jl are currently run with Julia 1.8 and 1.9 on x86-64 linux and mac; other platforms and versions may or may not work but will depend on StaticCompiler.jl support.
1214

1315
While we'll do our best to keep things working, this package should still be considered experimental at present, and necessarily involves a lot of juggling of pointers and such (i.e., "unsafe Julia"). If there are errors in any of the `llvmcall`s (which we have to use instead of simpler `ccall`s for things to statically compile smoothly), there could be serious bugs or even undefined behavior. Please report any unexpected bugs you find, and PRs are welcome!
1416

15-
The stack-allocated statically-sized `StaticString`s in this package are heavily inspired by the techniques used in [JuliaSIMD/ManualMemory.jl](https://github.com/JuliaSIMD/ManualMemory.jl); you can use that package via [StrideArraysCore.jl](https://github.com/JuliaSIMD/StrideArraysCore.jl) or [StrideArrays.jl](https://github.com/chriselrod/StrideArrays.jl) to obtain fast stack-allocated statically-sized arrays which should also be StaticCompiler-friendly.
16-
1717
In addition to the exported names, Julia `Base` functions extended for StaticTools types (i.e., `StaticString`/ `MallocString` and `StackArray`/`MallocArray`) include:
1818
* `print`, `println`, `error`,
1919
* `parse`, `read`, `write`
2020
* `rand`/`rand!` (when using an `rng` initialied with `static_rng`, `SplitMix64`, or `Xoshiro256✴︎✴︎` )
2121
* `randn`/`randn!` (when using an `rng` initialied with `MarsagliaPolar`, `BoxMuller`, or `Ziggurat` )
2222
* and much or all of the `AbstractArray` and `AbstractString` interfaces where relevant.
2323

24+
The stack-allocated statically-sized `StaticString`s and `StackArray`s in this package are heavily inspired by the techniques used in [JuliaSIMD/ManualMemory.jl](https://github.com/JuliaSIMD/ManualMemory.jl); you can use that package via [StrideArraysCore.jl](https://github.com/JuliaSIMD/StrideArraysCore.jl) or [StrideArrays.jl](https://github.com/chriselrod/StrideArrays.jl) to obtain fast stack-allocated statically-sized arrays which should also be StaticCompiler-friendly, up to the stack limit size. For larger arrays, space must be allocated with `malloc`, as in `MallocArray`s. However, as in any other language, any memory `malloc`ed must be freed once and only once. If you want `malloc`-backed StaticCompiler-able arrays without taking on this risk and responsibility, you may consider a bump allocator like [Bumper.jl](https://github.com/MasonProtter/Bumper.jl)
25+
2426
[![Mandelbrot Set in the terminal with compiled Julia](docs/mandelcompilemov.jpg)](http://www.youtube.com/watch?v=YsNC4oO0rLA)
2527
[printmandel.jl](https://gist.github.com/brenhinkeller/ca2246ab0928e109e281a4d540010b2d)
2628

@@ -583,3 +585,16 @@ Hello from 3 of 4 processors!
583585
Hello from 2 of 4 processors!
584586
Hello from 0 of 4 processors!
585587
```
588+
589+
[docs-dev-img]: https://img.shields.io/badge/docs-dev-blue.svg
590+
[docs-dev-url]: https://brenhinkeller.github.io/StaticTools.jl/dev/
591+
[ci-img]: https://github.com/brenhinkeller/StaticTools.jl/workflows/CI/badge.svg
592+
[ci-url]: https://github.com/brenhinkeller/StaticTools.jl/actions/workflows/CI.yml
593+
[ci-nightly-img]: https://github.com/brenhinkeller/StaticTools.jl/workflows/CI%20(Julia%20nightly)/badge.svg
594+
[ci-nightly-url]: https://github.com/brenhinkeller/StaticTools.jl/actions/workflows/CI-julia-nightly.yml
595+
[ci-integration-img]: https://github.com/brenhinkeller/StaticTools.jl/workflows/CI%20(Integration)/badge.svg
596+
[ci-integration-url]: https://github.com/brenhinkeller/StaticTools.jl/actions/workflows/CI-integration.yml
597+
[ci-integration-nightly-img]: https://github.com/brenhinkeller/StaticTools.jl/workflows/CI%20(Integration%20nightly)/badge.svg
598+
[ci-integration-nightly-url]: https://github.com/brenhinkeller/StaticTools.jl/actions/workflows/CI-integration-nightly.yml
599+
[codecov-img]: http://codecov.io/github/brenhinkeller/StaticTools.jl/coverage.svg?branch=main
600+
[codecov-url]: http://codecov.io/github/brenhinkeller/StaticTools.jl?branch=main

src/llvminterop.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Ptr{StaticTools.DYLIB} @0x000000010bf49b78
4040
julia> fp = StaticTools.dlsym(lib, c"time")
4141
Ptr{Nothing} @0x00007fffa773dfa4
4242
43-
julia> dltime() = @ptrcall fp()::Int
43+
julia> dltime() = @ptrcall fp(C_VOID::Ptr{Nothing})::Int
4444
dltime (generic function with 1 method)
4545
4646
julia> dltime()

src/llvmlibc.jl

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -562,29 +562,24 @@ julia> parse(Int64, c"3.141592")
562562
3
563563
```
564564
"""
565-
@inline function Base.parse(::Type{Float64}, s::Union{StaticString, MallocString})
565+
@inline function Base.parse(::Type{Float64}, s::AbstractStaticString)
566566
num, pbuf = strtod(s)
567567
load(pointer(pbuf)) == pointer(s) && return NaN
568568
return num
569569
end
570-
@inline function Base.parse(::Type{Float64}, s::Ptr{UInt8})
571-
num, pbuf = strtod(s)
572-
load(pointer(pbuf)) == s && return NaN
573-
return num
574-
end
575-
@inline Base.parse(::Type{T}, s::Union{StaticString, MallocString, Ptr{UInt8}}) where {T <: AbstractFloat} = T(parse(Float64, s))
570+
@inline Base.parse(::Type{T}, s::AbstractStaticString) where {T <: AbstractFloat} = T(parse(Float64, s))
576571

577-
@inline function Base.parse(::Type{Int64}, s::Union{StaticString, MallocString, Ptr{UInt8}})
572+
@inline function Base.parse(::Type{Int64}, s::AbstractStaticString)
578573
num, pbuf = strtol(s)
579574
return num
580575
end
581-
@inline Base.parse(::Type{T}, s::Union{StaticString, MallocString, Ptr{UInt8}}) where {T <: Integer} = T(parse(Int64, s))
576+
@inline Base.parse(::Type{T}, s::AbstractStaticString) where {T <: Integer} = T(parse(Int64, s))
582577

583-
@inline function Base.parse(::Type{UInt64}, s::Union{StaticString, MallocString, Ptr{UInt8}})
578+
@inline function Base.parse(::Type{UInt64}, s::AbstractStaticString)
584579
num, pbuf = strtoul(s)
585580
return num
586581
end
587-
@inline Base.parse(::Type{T}, s::Union{StaticString, MallocString, Ptr{UInt8}}) where {T <: Unsigned} = T(parse(UInt64, s))
582+
@inline Base.parse(::Type{T}, s::AbstractStaticString) where {T <: Unsigned} = T(parse(UInt64, s))
588583

589584
# Convenient parsing for argv
590585
@inline argparse(::Type{T}, argv::Ptr{Ptr{UInt8}}, n::Integer) where {T} = parse(T, MallocString(argv, n))

src/parsedlm.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,13 @@ end
9494
@inbounds while gets!(str,fp) != C_NULL
9595

9696
# identify the delimited fields,
97-
field[1] = 0
97+
field[1] = 1
9898
k, columns = 1, 1
9999
while str[k] != 0x00 #UInt8('\0')
100100
if str[k] == UInt8(delim)
101101
str[k] = '\0'
102102
columns += 1
103-
field[columns] = k
103+
field[columns] = k+1
104104
elseif str[k] == UInt8('\n')
105105
str[k] = '\0'
106106
end
@@ -109,7 +109,7 @@ end
109109

110110
# and perform operations on each field
111111
for j = 1:maxcolumns
112-
importedmatrix[i,j] = parse(T, pointer(str) + field[j])
112+
importedmatrix[i,j] = parse(T, str[field[j]:end])
113113
end
114114
i += 1
115115
end
@@ -159,20 +159,20 @@ end
159159
@inbounds for i 1:rows
160160

161161
# identify the delimited fields,
162-
field[1] = k-1
162+
field[1] = k
163163
columns = 1
164164
while str[k] != 0x0a
165165
if str[k] == delim
166166
str[k] = 0x00
167167
columns += 1
168-
field[columns] = k
168+
field[columns] = k+1
169169
end
170170
k += 1
171171
end
172172

173173
# and perform operations on each field
174174
for j = 1:maxcolumns
175-
importedmatrix[i,j] = parse(T, pointer(str) + field[j])
175+
importedmatrix[i,j] = parse(T, str[field[j]:end])
176176
end
177177
k += 1
178178
end

src/printformats.jl

Lines changed: 30 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -175,116 +175,76 @@ The value of x is currently 1
175175
```
176176
"""
177177
@inline function printf(args::Tuple{T1, T2}) where {T1, T2}
178-
printf(args[1])
179-
printf(args[2])
178+
Base.Cartesian.@nexprs 2 i->printf(args[i])
180179
return zero(Int32)
181180
end
182181
@inline function printf(args::Tuple{T1, T2, T3}) where {T1, T2, T3}
183-
printf(args[1])
184-
printf(args[2])
185-
printf(args[3])
182+
Base.Cartesian.@nexprs 3 i->printf(args[i])
186183
return zero(Int32)
187184
end
188185
@inline function printf(args::Tuple{T1, T2, T3, T4}) where {T1, T2, T3, T4}
189-
printf(args[1])
190-
printf(args[2])
191-
printf(args[3])
192-
printf(args[4])
186+
Base.Cartesian.@nexprs 4 i->printf(args[i])
193187
return zero(Int32)
194188
end
195189
@inline function printf(args::Tuple{T1, T2, T3, T4, T5}) where {T1, T2, T3, T4, T5}
196-
printf(args[1])
197-
printf(args[2])
198-
printf(args[3])
199-
printf(args[4])
200-
printf(args[5])
190+
Base.Cartesian.@nexprs 5 i->printf(args[i])
201191
return zero(Int32)
202192
end
203193
@inline function printf(args::Tuple{T1, T2, T3, T4, T5, T6}) where {T1, T2, T3, T4, T5, T6}
204-
printf(args[1])
205-
printf(args[2])
206-
printf(args[3])
207-
printf(args[4])
208-
printf(args[5])
209-
printf(args[6])
194+
Base.Cartesian.@nexprs 6 i->printf(args[i])
210195
return zero(Int32)
211196
end
212197
@inline function printf(args::Tuple{T1, T2, T3, T4, T5, T6, T7}) where {T1, T2, T3, T4, T5, T6, T7}
213-
printf(args[1])
214-
printf(args[2])
215-
printf(args[3])
216-
printf(args[4])
217-
printf(args[5])
218-
printf(args[6])
219-
printf(args[7])
198+
Base.Cartesian.@nexprs 7 i->printf(args[i])
220199
return zero(Int32)
221200
end
222201
@inline function printf(args::Tuple{T1, T2, T3, T4, T5, T6, T7, T8}) where {T1, T2, T3, T4, T5, T6, T7, T8}
223-
printf(args[1])
224-
printf(args[2])
225-
printf(args[3])
226-
printf(args[4])
227-
printf(args[5])
228-
printf(args[6])
229-
printf(args[7])
230-
printf(args[8])
202+
Base.Cartesian.@nexprs 8 i->printf(args[i])
203+
return zero(Int32)
204+
end
205+
@inline function printf(args::Tuple{T1, T2, T3, T4, T5, T6, T7, T8, T9}) where {T1, T2, T3, T4, T5, T6, T7, T8, T9}
206+
Base.Cartesian.@nexprs 9 i->printf(args[i])
207+
return zero(Int32)
208+
end
209+
@inline function printf(args::Tuple{T1, T2, T3, T4, T5, T6, T7, T8, T9, T10}) where {T1, T2, T3, T4, T5, T6, T7, T8, T9, T10}
210+
Base.Cartesian.@nexprs 10 i->printf(args[i])
231211
return zero(Int32)
232212
end
233213
# Print to file
234214
@inline function printf(fp::Ptr{FILE}, args::Tuple{T1, T2}) where {T1, T2}
235-
printf(fp, args[1])
236-
printf(fp, args[2])
215+
Base.Cartesian.@nexprs 2 i->printf(fp, args[i])
237216
return zero(Int32)
238217
end
239218
@inline function printf(fp::Ptr{FILE}, args::Tuple{T1, T2, T3}) where {T1, T2, T3}
240-
printf(fp, args[1])
241-
printf(fp, args[2])
242-
printf(fp, args[3])
219+
Base.Cartesian.@nexprs 3 i->printf(fp, args[i])
243220
return zero(Int32)
244221
end
245222
@inline function printf(fp::Ptr{FILE}, args::Tuple{T1, T2, T3, T4}) where {T1, T2, T3, T4}
246-
printf(fp, args[1])
247-
printf(fp, args[2])
248-
printf(fp, args[3])
249-
printf(fp, args[4])
223+
Base.Cartesian.@nexprs 4 i->printf(fp, args[i])
250224
return zero(Int32)
251225
end
252226
@inline function printf(fp::Ptr{FILE}, args::Tuple{T1, T2, T3, T4, T5}) where {T1, T2, T3, T4, T5}
253-
printf(fp, args[1])
254-
printf(fp, args[2])
255-
printf(fp, args[3])
256-
printf(fp, args[4])
257-
printf(fp, args[5])
227+
Base.Cartesian.@nexprs 5 i->printf(fp, args[i])
258228
return zero(Int32)
259229
end
260230
@inline function printf(fp::Ptr{FILE}, args::Tuple{T1, T2, T3, T4, T5, T6}) where {T1, T2, T3, T4, T5, T6}
261-
printf(fp, args[1])
262-
printf(fp, args[2])
263-
printf(fp, args[3])
264-
printf(fp, args[4])
265-
printf(fp, args[5])
266-
printf(fp, args[6])
231+
Base.Cartesian.@nexprs 6 i->printf(fp, args[i])
267232
return zero(Int32)
268233
end
269234
@inline function printf(fp::Ptr{FILE}, args::Tuple{T1, T2, T3, T4, T5, T6, T7}) where {T1, T2, T3, T4, T5, T6, T7}
270-
printf(fp, args[1])
271-
printf(fp, args[2])
272-
printf(fp, args[3])
273-
printf(fp, args[4])
274-
printf(fp, args[5])
275-
printf(fp, args[6])
276-
printf(fp, args[7])
235+
Base.Cartesian.@nexprs 7 i->printf(fp, args[i])
277236
return zero(Int32)
278237
end
279238
@inline function printf(fp::Ptr{FILE}, args::Tuple{T1, T2, T3, T4, T5, T6, T7, T8}) where {T1, T2, T3, T4, T5, T6, T7, T8}
280-
printf(fp, args[1])
281-
printf(fp, args[2])
282-
printf(fp, args[3])
283-
printf(fp, args[4])
284-
printf(fp, args[5])
285-
printf(fp, args[6])
286-
printf(fp, args[7])
287-
printf(fp, args[8])
239+
Base.Cartesian.@nexprs 8 i->printf(fp, args[i])
240+
return zero(Int32)
241+
end
242+
@inline function printf(fp::Ptr{FILE}, args::Tuple{T1, T2, T3, T4, T5, T6, T7, T8, T9}) where {T1, T2, T3, T4, T5, T6, T7, T8, T9}
243+
Base.Cartesian.@nexprs 9 i->printf(fp, args[i])
244+
return zero(Int32)
245+
end
246+
@inline function printf(fp::Ptr{FILE}, args::Tuple{T1, T2, T3, T4, T5, T6, T7, T8, T9, T10}) where {T1, T2, T3, T4, T5, T6, T7, T8, T9, T10}
247+
Base.Cartesian.@nexprs 10 i->printf(fp, args[i])
288248
return zero(Int32)
289249
end
290250

test/scripts/stack_times_table.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using StaticCompiler
2+
using StaticTools
3+
4+
function stack_times_table()
5+
a = StackArray{Int64}(undef, 5, 5)
6+
for i axes(a,1)
7+
for j axes(a,2)
8+
a[i,j] = i*j
9+
end
10+
end
11+
print(a)
12+
fwrite(c"table.b", a)
13+
GC.@preserve a printdlm(c"table.tsv", a)
14+
15+
# Test reinterpreting
16+
println(c"\nThe same array, reinterpreted as Int32:")
17+
GC.@preserve a print(reinterpret(Int32, a))
18+
end
19+
20+
path = compile_executable(stack_times_table, (), "./")

test/scripts/times_table.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ function times_table(argc::Int, argv::Ptr{Ptr{UInt8}})
1313
end
1414
end
1515
# Print to stdout
16-
printf(M)
16+
print(M)
1717
# Also print to file
1818
fwrite(c"table.b", M)
1919
printdlm(c"table.tsv", M)
2020

2121
# Test reinterpreting
2222
Mr = reinterpret(Int32, M)
23-
println(c"\n\nThe same array, reinterpreted as Int32:")
24-
printf(Mr)
23+
println(c"\nThe same array, reinterpreted as Int32:")
24+
print(Mr)
2525

2626
# Clean up matrix
2727
free(M)

test/testllvminterop.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
a, b = ccall(timefp, Int64, (Ptr{Cvoid},), C_NULL), time()
2626
@test isapprox(a, b, atol = 5)
2727

28-
dltime() = @ptrcall timefp()::Int64
28+
dltime() = @ptrcall timefp(C_NULL::Ptr{Nothing})::Int64
2929
a, b = dltime(), time()
3030
@test isapprox(a, b, atol = 5)
3131

test/testllvmio.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
@test printf(0x00000001) == 0
3030
@test printf(0x0000000000000001) == 0
3131
@test printf(Ptr{UInt64}(0)) == 0
32-
@test printf((c"The value of x is currently ", 1.0, c"\n")) == 0
33-
tpl = (c"Sphinx ", c"of ", c"black ", c"quartz, ", c"judge ", c"my ", c"vow!", c"\n")
32+
@test printf((c"\nThe value of x is currently ", 1.0, c"\n")) == 0
33+
tpl = (c"1", c": ", c"Sphinx ", c"of ", c"black ", c"quartz, ", c"judge ", c"my ", c"vow!", c"\n")
3434
for n 1:length(tpl)-1
3535
@test printf(tpl[[1:n..., length(tpl)]]) == 0
3636
end
@@ -44,7 +44,7 @@
4444
@test puts(fp, "1") == 0
4545
@test printf(fp, "2") == 1
4646
@test putchar(fp, '\n') == 0
47-
@test printf(fp, "%s\n", "3") == 2
47+
@test printf(fp, "%s\n", "3") == 2 broken=(Sys.ARCH===:aarch64)
4848
@test printf(fp, 4) == 0
4949
@test printf(fp, 5.0) == 0
5050
@test printf(fp, 10.0f0) == 0
@@ -53,8 +53,8 @@
5353
@test printf(fp, 0x00000001) == 0
5454
@test printf(fp, 0x0000000000000001) == 0
5555
@test printf(fp, Ptr{UInt64}(0)) == 0
56-
@test printf(fp, (c"The value of x is currently ", 1.0, c"\n")) == 0
57-
tpl = (c"Sphinx ", c"of ", c"black ", c"quartz, ", c"judge ", c"my ", c"vow!", c"\n")
56+
@test printf(fp, (c"\nThe value of x is currently ", 1.0, c"\n")) == 0
57+
tpl = (c"1", c": ", c"Sphinx ", c"of ", c"black ", c"quartz, ", c"judge ", c"my ", c"vow!", c"\n")
5858
for n 1:length(tpl)-1
5959
@test printf(fp, tpl[[1:n..., length(tpl)]]) == 0
6060
end

0 commit comments

Comments
 (0)