|
| 1 | +## `FixedSizeArrays.jl` |
| 2 | + |
| 3 | +`FixedSizeArrays.jl` is a proof-of-concept package for the [Julia programming language](https://julialang.org/) which implements mutable fixed-size arrays, which means the lenght of the array is constant and is amenable to be [constant-propagated](https://en.wikipedia.org/wiki/Constant_folding) at compile-time when possible. |
| 4 | +This is an alternative implementation to [`MArray`](https://juliaarrays.github.io/StaticArrays.jl/stable/pages/api/#StaticArraysCore.MArray) from [`StaticArrays.jl`](https://github.com/JuliaArrays/StaticArrays.jl). |
| 5 | + |
| 6 | +Main differences between `FixedSizeArray` and `MArray` are: |
| 7 | + |
| 8 | +* `FixedSizeArray` is based on the `Memory` type introduced in Julia v1.11, `MArray` is backed by tuples; |
| 9 | +* the size of the array is part of the type parameters of `MArray`, this isn't the case for `FixedSizeArray`, where the size is only a constant field of the data structure. |
| 10 | + |
| 11 | +Note: `FixedSizeArray`s are not guaranteed to be stack-allocated, in fact they will more likely *not* be stack-allocated. |
| 12 | +However, in some *extremely* simple cases the compiler may be able to completely elide their allocations: |
| 13 | +```julia |
| 14 | +julia> using FixedSizeArrays |
| 15 | + |
| 16 | +julia> @noinline f(A::AbstractArray) = length(A) |
| 17 | +f (generic function with 1 method) |
| 18 | + |
| 19 | +julia> g() = f(FixedSizeVector{Float64}(undef, 3)) |
| 20 | +g (generic function with 1 method) |
| 21 | + |
| 22 | +julia> h() = f(Vector{Float64}(undef, 3)) |
| 23 | +h (generic function with 1 method) |
| 24 | + |
| 25 | +julia> code_llvm(g) |
| 26 | +``` |
| 27 | +```llvm |
| 28 | +; Function Signature: g() |
| 29 | +; @ REPL[3]:1 within `g` |
| 30 | +define i64 @julia_g_511() #0 { |
| 31 | +top: |
| 32 | + ret i64 3 |
| 33 | +} |
| 34 | +
|
| 35 | +``` |
| 36 | +```julia |
| 37 | +julia> code_llvm(h) |
| 38 | +``` |
| 39 | +```llvm |
| 40 | +; Function Signature: h() |
| 41 | +; @ REPL[4]:1 within `h` |
| 42 | +define i64 @julia_h_693() #0 { |
| 43 | +top: |
| 44 | + %gcframe1 = alloca [3 x ptr], align 16 |
| 45 | + call void @llvm.memset.p0.i64(ptr align 16 %gcframe1, i8 0, i64 24, i1 true) |
| 46 | + %pgcstack = call ptr inttoptr (i64 7452881148 to ptr)(i64 262) #10 |
| 47 | + store i64 4, ptr %gcframe1, align 16 |
| 48 | + %task.gcstack = load ptr, ptr %pgcstack, align 8 |
| 49 | + %frame.prev = getelementptr inbounds ptr, ptr %gcframe1, i64 1 |
| 50 | + store ptr %task.gcstack, ptr %frame.prev, align 8 |
| 51 | + store ptr %gcframe1, ptr %pgcstack, align 8 |
| 52 | +; ┌ @ boot.jl:576 within `Array` |
| 53 | +; │┌ @ boot.jl:514 within `GenericMemory` |
| 54 | + %"Memory{Float64}[]" = call ptr @jl_alloc_genericmemory(ptr nonnull @"+Core.GenericMemory#695.jit", i64 3) |
| 55 | +; │└ |
| 56 | +; │ @ boot.jl:577 within `Array` |
| 57 | + %.data_ptr = getelementptr inbounds { i64, ptr }, ptr %"Memory{Float64}[]", i64 0, i32 1 |
| 58 | + %0 = load ptr, ptr %.data_ptr, align 8 |
| 59 | + %gc_slot_addr_0 = getelementptr inbounds ptr, ptr %gcframe1, i64 2 |
| 60 | + store ptr %"Memory{Float64}[]", ptr %gc_slot_addr_0, align 16 |
| 61 | + %ptls_field = getelementptr inbounds ptr, ptr %pgcstack, i64 2 |
| 62 | + %ptls_load = load ptr, ptr %ptls_field, align 8 |
| 63 | + %"new::Array" = call noalias nonnull align 8 dereferenceable(32) ptr @ijl_gc_pool_alloc_instrumented(ptr %ptls_load, i32 800, i32 32, i64 4645053728) #8 |
| 64 | + %"new::Array.tag_addr" = getelementptr inbounds i64, ptr %"new::Array", i64 -1 |
| 65 | + store atomic i64 4645053728, ptr %"new::Array.tag_addr" unordered, align 8 |
| 66 | + %1 = getelementptr inbounds ptr, ptr %"new::Array", i64 1 |
| 67 | + store ptr %0, ptr %"new::Array", align 8 |
| 68 | + store ptr %"Memory{Float64}[]", ptr %1, align 8 |
| 69 | + %"new::Array.size_ptr" = getelementptr inbounds i8, ptr %"new::Array", i64 16 |
| 70 | + store i64 3, ptr %"new::Array.size_ptr", align 8 |
| 71 | + store ptr %"new::Array", ptr %gc_slot_addr_0, align 16 |
| 72 | +; └ |
| 73 | + %2 = call i64 @j_f_699(ptr nonnull %"new::Array") |
| 74 | + %frame.prev10 = load ptr, ptr %frame.prev, align 8 |
| 75 | + store ptr %frame.prev10, ptr %pgcstack, align 8 |
| 76 | + ret i64 %2 |
| 77 | +} |
| 78 | +``` |
| 79 | + |
| 80 | +> [!WARNING] |
| 81 | +> This package should currently be used only to experiment with the idea of `Memory`-backed fixed-size arrays, it's highly non-optimised, absolutely don't use it for production. |
0 commit comments