Skip to content

Commit 00e61c8

Browse files
authored
Merge pull request #329 from maleadt/tb/sdk_version
Add support for setting the module SDK Version metadata
2 parents 363a66f + 315211a commit 00e61c8

File tree

10 files changed

+179
-41
lines changed

10 files changed

+179
-41
lines changed

deps/LLVMExtra/include/LLVMExtra.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,5 +162,8 @@ void LLVMReplaceMDNodeOperandWith(LLVMMetadataRef MD, unsigned I, LLVMMetadataRe
162162
LLVMBool LLVMContextSupportsTypedPointers(LLVMContextRef C);
163163
#endif
164164

165+
// constant data
166+
LLVMValueRef LLVMConstDataArray(LLVMTypeRef ElementTy, const void *Data, unsigned NumElements);
167+
165168
LLVM_C_EXTERN_C_END
166169
#endif

deps/LLVMExtra/lib/llvm-api.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,3 +556,8 @@ LLVMBool LLVMContextSupportsTypedPointers(LLVMContextRef C) {
556556
return unwrap(C)->supportsTypedPointers();
557557
}
558558
#endif
559+
560+
LLVMValueRef LLVMConstDataArray(LLVMTypeRef ElementTy, const void *Data, unsigned NumElements) {
561+
StringRef S((const char *)Data, NumElements * unwrap(ElementTy)->getPrimitiveSizeInBits() / 8);
562+
return wrap(ConstantDataArray::getRaw(S, NumElements, unwrap(ElementTy)));
563+
}

deps/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ CMake_jll = "3f4e10e2-61f2-5801-8945-23b9d642d0e6"
33
Git = "d7ba0133-e1db-5d97-8f8c-041e4b3a1eb2"
44
LLVMExtra_jll = "dad2f222-ce93-54a1-a47d-0025e8a3acab"
55
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
6+
Ninja_jll = "76642167-d241-5cee-8c94-7a494e8cb7b7"
67
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
78
Preferences = "21216c6a-2e73-6563-6e65-726566657250"
89
Scratch = "6c6a2e73-6563-6170-7368-637461726353"

deps/build_local.jl

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,57 +8,67 @@ if haskey(ENV, "GITHUB_ACTIONS")
88
println("::warning ::Using a locally-built LLVMExtra; A bump of LLVMExtra_jll will be required before releasing LLVM.jl.")
99
end
1010

11-
using Pkg, Scratch, Preferences, Libdl, CMake_jll
11+
using Pkg, Scratch, Preferences, Libdl, CMake_jll, Ninja_jll
1212

1313
LLVM = Base.UUID("929cbde3-209d-540e-8aea-75f648917ca0")
1414

15-
# 1. Ensure that an appropriate LLVM_full_jll is installed
15+
# get scratch directories
16+
scratch_dir = get_scratch!(LLVM, "build")
17+
isdir(scratch_dir) && rm(scratch_dir; recursive=true)
18+
source_dir = joinpath(@__DIR__, "LLVMExtra")
19+
20+
# get build directory
21+
build_dir = if isempty(ARGS)
22+
mktempdir()
23+
else
24+
ARGS[1]
25+
end
26+
mkpath(build_dir)
27+
28+
# download LLVM
1629
Pkg.activate(; temp=true)
1730
llvm_assertions = try
1831
cglobal((:_ZN4llvm24DisableABIBreakingChecksE, Base.libllvm_path()), Cvoid)
1932
false
2033
catch
2134
true
2235
end
36+
llvm_pkg_version = "$(Base.libllvm_version.major).$(Base.libllvm_version.minor)"
2337
LLVM = if llvm_assertions
24-
Pkg.add(name="LLVM_full_assert_jll", version=Base.libllvm_version)
38+
Pkg.add(name="LLVM_full_assert_jll", version=llvm_pkg_version)
2539
using LLVM_full_assert_jll
2640
LLVM_full_assert_jll
2741
else
28-
Pkg.add(name="LLVM_full_jll", version=Base.libllvm_version)
42+
Pkg.add(name="LLVM_full_jll", version=llvm_pkg_version)
2943
using LLVM_full_jll
3044
LLVM_full_jll
3145
end
3246
LLVM_DIR = joinpath(LLVM.artifact_dir, "lib", "cmake", "llvm")
3347

34-
# 2. Get a scratch directory
35-
scratch_dir = get_scratch!(LLVM, "build")
36-
isdir(scratch_dir) && rm(scratch_dir; recursive=true)
37-
source_dir = joinpath(@__DIR__, "LLVMExtra")
38-
39-
# Build!
40-
mktempdir() do build_dir
41-
@info "Building" source_dir scratch_dir build_dir LLVM_DIR
42-
run(`$(cmake()) -DLLVM_DIR=$(LLVM_DIR) -DCMAKE_INSTALL_PREFIX=$(scratch_dir) -B$(build_dir) -S$(source_dir)`)
43-
run(`$(cmake()) --build $(build_dir)`)
44-
run(`$(cmake()) --install $(build_dir)`)
48+
# build and install
49+
@info "Building" source_dir scratch_dir build_dir LLVM_DIR
50+
cmake() do cmake_path
51+
ninja() do ninja_path
52+
run(`$cmake_path -GNinja -DLLVM_DIR=$(LLVM_DIR) -DCMAKE_INSTALL_PREFIX=$(scratch_dir) -B$(build_dir) -S$(source_dir)`)
53+
run(`$ninja_path -C $(build_dir) install`)
54+
end
4555
end
4656

47-
# Discover built libraries
57+
# discover built libraries
4858
built_libs = filter(readdir(joinpath(scratch_dir, "lib"))) do file
4959
endswith(file, ".$(Libdl.dlext)")
5060
end
5161
lib_path = joinpath(scratch_dir, "lib", only(built_libs))
5262
isfile(lib_path) || error("Could not find library $lib_path in build directory")
5363

54-
# Tell LLVMExtra_jll to load our library instead of the default artifact one
64+
# tell LLVMExtra_jll to load our library instead of the default artifact one
5565
set_preferences!(
5666
joinpath(dirname(@__DIR__), "LocalPreferences.toml"),
5767
"LLVMExtra_jll",
5868
"libLLVMExtra_path" => lib_path;
5969
force=true,
6070
)
6171

62-
# Copy the preferences to `test/` as well to work around Pkg.jl#2500
72+
# copy the preferences to `test/` as well to work around Pkg.jl#2500
6373
cp(joinpath(dirname(@__DIR__), "LocalPreferences.toml"),
6474
joinpath(dirname(@__DIR__), "test", "LocalPreferences.toml"); force=true)

lib/libLLVM_extra.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,3 +416,7 @@ function LLVMContextSupportsTypedPointers(Ctx)
416416
ccall((:LLVMContextSupportsTypedPointers, libLLVMExtra), LLVMBool, (LLVMContextRef,), Ctx)
417417
end
418418
end
419+
420+
function LLVMConstDataArray(ElementTy, Data, NumElements)
421+
ccall((:LLVMConstDataArray, libLLVMExtra), LLVMValueRef, (LLVMTypeRef, Ptr{Cvoid}, Cuint), ElementTy, Data, NumElements)
422+
end

src/core/module.jl

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,3 +267,30 @@ function Base.setindex!(iter::ModuleFlagDict, val::Metadata,
267267
(name, behavior)::Tuple{String, API.LLVMModuleFlagBehavior})
268268
API.LLVMAddModuleFlag(iter.mod, behavior, name, length(name), val)
269269
end
270+
271+
272+
## sdk version
273+
274+
export sdk_version, sdk_version!
275+
276+
function sdk_version!(mod::Module, version::VersionNumber)
277+
entries = Int32[version.major]
278+
if version.minor != 0 || version.patch != 0
279+
push!(entries, version.minor)
280+
if version.patch != 0
281+
push!(entries, version.patch)
282+
end
283+
# cannot represent prerelease or build metadata
284+
end
285+
md = Metadata(ConstantDataArray(entries; ctx=context(mod)))
286+
287+
flags(mod)["SDK Version", LLVM.API.LLVMModuleFlagBehaviorWarning] = md
288+
end
289+
290+
function sdk_version(mod::Module)
291+
haskey(flags(mod), "SDK Version") || return nothing
292+
md = flags(mod)["SDK Version"]
293+
c = Value(md; ctx=context(mod))
294+
entries = collect(c)
295+
VersionNumber(map(val->convert(Int, val), entries)...)
296+
end

src/core/type.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ function ArrayType(eltyp::LLVMType, count)
163163
return ArrayType(API.LLVMArrayType(eltyp, count))
164164
end
165165

166-
Base.length(arrtyp::ArrayType) = API.LLVMGetArrayLength(arrtyp)
166+
Base.length(arrtyp::ArrayType) = Int(API.LLVMGetArrayLength(arrtyp))
167167

168168
Base.isempty(@nospecialize(T::ArrayType)) = length(T) == 0 || isempty(eltype(T))
169169

src/core/value/constant.jl

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -112,17 +112,67 @@ Base.convert(::Type{T}, val::ConstantFP) where {T<:AbstractFloat} =
112112
convert(T, API.LLVMConstRealGetDouble(val, Ref{API.LLVMBool}()))
113113

114114

115-
# sequential
115+
# sequential data
116116

117117
export ConstantDataSequential, ConstantDataArray, ConstantDataVector
118118

119119
abstract type ConstantDataSequential <: Constant end
120120

121+
# ConstantData can only contain primitive types (1/2/4/8 byte integers, float/half),
122+
# as opposed to ConstantAggregate which can contain arbitrary LLVM values.
123+
#
124+
# however, LLVM seems to use both array types interchangeably, e.g., constructing
125+
# a ConstArray through LLVMConstArray may return a ConstantDataArray (presumably as an
126+
# optimization, when the data can be represented as densely packed primitive values).
127+
# because of that, ConstantDataArray and ConstantArray need to behave the same way,
128+
# concretely, indexing a ConstantDataArray has to return LLVM constant values...
129+
#
130+
# XXX: maybe we should just not expose ConstantDataArray then?
131+
# one advantage of keeping them separate is that creating a ConstantDataArray
132+
# is much cheaper (we should also be able to iterate much more efficiently,
133+
# but cannot support that as explained above).
134+
135+
# array interface
136+
Base.eltype(cda::ConstantDataSequential) = llvmeltype(cda)
137+
Base.length(cda::ConstantDataSequential) = length(llvmtype(cda))
138+
Base.size(cda::ConstantDataSequential) = (length(cda),)
139+
function Base.getindex(cda::ConstantDataSequential, idx::Integer)
140+
@boundscheck 1 <= idx <= length(cda) || throw(BoundsError(cda, idx))
141+
Value(API.LLVMGetElementAsConstant(cda, idx-1))
142+
end
143+
function Base.collect(cda::ConstantDataSequential)
144+
constants = Array{Value}(undef, length(cda))
145+
for i in 1:length(cda)
146+
@inbounds constants[i] = cda[i]
147+
end
148+
return constants
149+
end
150+
121151
@checked struct ConstantDataArray <: ConstantDataSequential
122152
ref::API.LLVMValueRef
123153
end
124154
register(ConstantDataArray, API.LLVMConstantDataArrayValueKind)
125155

156+
function ConstantDataArray(typ::LLVMType, data::Array{T}) where {T <: Union{Integer, AbstractFloat}}
157+
# TODO: can we look up the primitive size of the LLVM type?
158+
# use that to assert it matches the Julia element type.
159+
return ConstantDataArray(API.LLVMConstDataArray(typ, data, length(data)))
160+
end
161+
162+
# shorthands with arrays of plain Julia data
163+
# FIXME: duplicates the ConstantInt/ConstantFP conversion rules
164+
# XXX: X[X(...)] instead of X.(...) because of empty-container inference
165+
ConstantDataArray(data::AbstractVector{T}; ctx::Context) where {T<:Integer} =
166+
ConstantDataArray(IntType(sizeof(T)*8; ctx), data)
167+
ConstantDataArray(data::AbstractVector{Core.Bool}; ctx::Context) =
168+
ConstantDataArray(Int1Type(ctx), data)
169+
ConstantDataArray(data::AbstractVector{Float16}; ctx::Context) =
170+
ConstantDataArray(HalfType(ctx), data)
171+
ConstantDataArray(data::AbstractVector{Float32}; ctx::Context) =
172+
ConstantDataArray(FloatType(ctx), data)
173+
ConstantDataArray(data::AbstractVector{Float64}; ctx::Context) =
174+
ConstantDataArray(DoubleType(ctx), data)
175+
126176
@checked struct ConstantDataVector <: ConstantDataSequential
127177
ref::API.LLVMValueRef
128178
end
@@ -162,16 +212,14 @@ export ConstantArray
162212
ref::API.LLVMValueRef
163213
end
164214
register(ConstantArray, API.LLVMConstantArrayValueKind)
165-
register(ConstantArray, API.LLVMConstantDataArrayValueKind)
166-
167-
ConstantArrayOrAggregateZero(value) = Value(value)::Union{ConstantArray,ConstantAggregateZero}
168215

169216
# generic constructor taking an array of constants
170217
function ConstantArray(typ::LLVMType, data::AbstractArray{T,N}=T[]) where {T<:Constant,N}
171218
@assert all(x->x==typ, llvmtype.(data))
172219

173220
if N == 1
174-
return ConstantArrayOrAggregateZero(API.LLVMConstArray(typ, Array(data), length(data)))
221+
# XXX: this can return a ConstDataArray (presumably as an optimization?)
222+
return Value(API.LLVMConstArray(typ, Array(data), length(data)))
175223
end
176224

177225
ca_vec = map(x->ConstantArray(typ, x), eachslice(data, dims=1))
@@ -183,15 +231,15 @@ end
183231
# shorthands with arrays of plain Julia data
184232
# FIXME: duplicates the ConstantInt/ConstantFP conversion rules
185233
# XXX: X[X(...)] instead of X.(...) because of empty-container inference
186-
ConstantArray(data::AbstractArray{T,N}; ctx::Context) where {T<:Integer,N} =
234+
ConstantArray(data::AbstractArray{T}; ctx::Context) where {T<:Integer} =
187235
ConstantArray(IntType(sizeof(T)*8; ctx), ConstantInt[ConstantInt(x; ctx) for x in data])
188-
ConstantArray(data::AbstractArray{Core.Bool,N}; ctx::Context) where {N} =
236+
ConstantArray(data::AbstractArray{Core.Bool}; ctx::Context) =
189237
ConstantArray(Int1Type(ctx), ConstantInt[ConstantInt(x; ctx) for x in data])
190-
ConstantArray(data::AbstractArray{Float16,N}; ctx::Context) where {N} =
238+
ConstantArray(data::AbstractArray{Float16}; ctx::Context) =
191239
ConstantArray(HalfType(ctx), ConstantFP[ConstantFP(x; ctx) for x in data])
192-
ConstantArray(data::AbstractArray{Float32,N}; ctx::Context) where {N} =
240+
ConstantArray(data::AbstractArray{Float32}; ctx::Context) =
193241
ConstantArray(FloatType(ctx), ConstantFP[ConstantFP(x; ctx) for x in data])
194-
ConstantArray(data::AbstractArray{Float64,N}; ctx::Context) where {N} =
242+
ConstantArray(data::AbstractArray{Float64}; ctx::Context) =
195243
ConstantArray(DoubleType(ctx), ConstantFP[ConstantFP(x; ctx) for x in data])
196244

197245
# convert back to known array types
@@ -226,7 +274,9 @@ function Base.getindex(ca::ConstantArray, idx::Integer...)
226274
I = CartesianIndices(size(ca))[idx...]
227275
for i in Tuple(I)
228276
if isempty(operands(ca))
229-
ca = LLVM.Value(API.LLVMGetElementAsConstant(ca, i-1))
277+
# XXX: is this valid? LLVMGetElementAsConstant is meant to be used with
278+
# Constant*Data*Arrays, not ConstantArrays
279+
ca = Value(API.LLVMGetElementAsConstant(ca, i-1))
230280
else
231281
ca = (Base.@_propagate_inbounds_meta; operands(ca)[i])
232282
end

test/core.jl

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -412,29 +412,23 @@ end
412412
end
413413

414414

415-
@testset "array constants" begin
415+
@testset "array aggregate constants" begin
416416

417417
# from Julia values
418418
let
419-
vec = Int32[1,2,3,4]
419+
vec = Int128[1,2,3,4]
420420
ca = ConstantArray(vec; ctx)
421+
@test ca isa ConstantArray
421422
@test size(vec) == size(ca)
422423
@test length(vec) == length(ca)
423424
@test ca[1] == ConstantInt(vec[1]; ctx)
424425
@test collect(ca) == ConstantInt.(vec; ctx)
425426
end
426-
let
427-
vec = Float32[1.1f0,2.2f0,3.3f0,4.4f0]
428-
ca = ConstantArray(vec; ctx)
429-
@test size(vec) == size(ca)
430-
@test length(vec) == length(ca)
431-
@test ca[1] == ConstantFP(vec[1]; ctx)
432-
@test collect(ca) == ConstantFP.(vec; ctx)
433-
end
434427
let
435428
# tests for ConstantAggregateZero, constructed indirectly.
436429
# should behave similarly to ConstantArray since it can get returned there.
437430
ca = ConstantArray(Int[]; ctx)
431+
@test ca isa ConstantAggregateZero
438432
@test size(ca) == (0,)
439433
@test length(ca) == 0
440434
@test isempty(collect(ca))
@@ -451,7 +445,7 @@ end
451445

452446
end
453447

454-
@testset "struct constants" begin
448+
@testset "struct aggregate constants" begin
455449

456450
# from Julia values
457451
let
@@ -506,6 +500,36 @@ end
506500
end
507501

508502
end
503+
504+
505+
@testset "array data constants" begin
506+
507+
let
508+
vec = Int32[1,2,3,4]
509+
eltyp = LLVM.Int32Type(ctx)
510+
cda = ConstantDataArray(eltyp, vec)
511+
@test cda isa ConstantDataArray
512+
@test llvmtype(cda) == LLVM.ArrayType(eltyp, 4)
513+
@test collect(cda) == ConstantInt.(vec; ctx)
514+
end
515+
516+
# from Julia values
517+
for T in [Int8, Int16, Int32, Int64]
518+
vec = T[1,2,3,4]
519+
cda = ConstantDataArray(vec; ctx)
520+
@test cda isa ConstantDataArray
521+
@test size(vec) == size(cda)
522+
@test collect(cda) == ConstantInt.(vec; ctx)
523+
end
524+
for T in [Float32, Float64]
525+
vec = T[1,2,3,4]
526+
cda = ConstantDataArray(vec; ctx)
527+
@test cda isa ConstantDataArray
528+
@test size(vec) == size(cda)
529+
@test collect(cda) == ConstantFP.(vec; ctx)
530+
end
531+
532+
end
509533
end
510534

511535
# constant expressions
@@ -984,6 +1008,10 @@ end
9841008

9851009
@test mod_flags["foobar"] == md
9861010
@test_throws KeyError mod_flags["foobaz"]
1011+
1012+
@test sdk_version(mod) === nothing
1013+
sdk_version!(mod, v"1.2.3")
1014+
@test sdk_version(mod) == v"1.2.3"
9871015
end
9881016

9891017
# metadata iteration

test/runtests.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
# Work around JuliaLang/Pkg.jl#2500
2+
if VERSION < v"1.8-"
3+
test_project = first(Base.load_path())
4+
preferences_file = "LocalPreferences.toml"
5+
test_preferences_file = joinpath(dirname(test_project), "LocalPreferences.toml")
6+
if isfile(preferences_file) && !isfile(test_preferences_file)
7+
cp(preferences_file, test_preferences_file)
8+
end
9+
end
10+
111
using LLVM
212
using Test
313

0 commit comments

Comments
 (0)