Skip to content

Commit 23adfdb

Browse files
committed
[LinearAlgebra] flesh out LBT API a bit more
This adds `lbt_find_backing_library()`, which is a useful debugging routine to allow advanced users/package authors to query LBT to determine which backing BLAS library will service a particular BLAS call. It also exposes the "footgun API", which allows users to directly set/get forwarding on a per-function basis. Because this has the ability to generate truly bizarre setups, we do not advertise this capability broadly (simply using `lbt_forward()` should be enough for most usecases) however it's nice to have wrapped.
1 parent 5584620 commit 23adfdb

File tree

2 files changed

+69
-7
lines changed

2 files changed

+69
-7
lines changed

stdlib/LinearAlgebra/src/blas.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ import LinearAlgebra: BlasReal, BlasComplex, BlasFloat, BlasInt, DimensionMismat
7979
include("lbt.jl")
8080

8181
"""
82-
get_config()
82+
get_config()
8383
8484
Return an object representing the current `libblastrampoline` configuration.
8585

stdlib/LinearAlgebra/src/lbt.jl

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const LBT_INTERFACE_MAP = Dict(
1717
LBT_INTERFACE_ILP64 => :ilp64,
1818
LBT_INTERFACE_UNKNOWN => :unknown,
1919
)
20+
const LBT_INV_INTERFACE_MAP = Dict(v => k for (k, v) in LBT_INTERFACE_MAP)
2021

2122
const LBT_F2C_PLAIN = 0
2223
const LBT_F2C_REQUIRED = 1
@@ -26,6 +27,7 @@ const LBT_F2C_MAP = Dict(
2627
LBT_F2C_REQUIRED => :required,
2728
LBT_F2C_UNKNOWN => :unknown,
2829
)
30+
const LBT_INV_F2C_MAP = Dict(v => k for (k, v) in LBT_F2C_MAP)
2931

3032
struct LBTLibraryInfo
3133
libname::String
@@ -164,14 +166,74 @@ function lbt_get_default_func()
164166
return ccall((:lbt_get_default_func, libblastrampoline), Ptr{Cvoid}, ())
165167
end
166168

167-
#=
168-
Don't define footgun API (yet)
169+
"""
170+
lbt_find_backing_library(symbol_name, interface; config::LBTConfig = lbt_get_config())
169171
170-
function lbt_get_forward(symbol_name, interface, f2c = LBT_F2C_PLAIN)
171-
return ccall((:lbt_get_forward, libblastrampoline), Ptr{Cvoid}, (Cstring, Int32, Int32), symbol_name, interface, f2c)
172+
Return the `LBTLibraryInfo` that represents the backing library for the given symbol
173+
exported from libblastrampoline. This allows us to discover which library will service
174+
a particular BLAS call from Julia code. This method returns `nothing` if either of the
175+
following conditions are met:
176+
177+
* No loaded library exports the desired symbol (the default function will be called)
178+
* The symbol was set via `lbt_set_forward()`, which does not track library provenance.
179+
180+
If the given `symbol_name` is not contained within the list of exported symbols, an
181+
`ArgumentError` will be thrown.
182+
"""
183+
function lbt_find_backing_library(symbol_name, interface::Symbol;
184+
config::LBTConfig = lbt_get_config())
185+
if interface (:ilp64, :lp64)
186+
throw(Argument("Invalid interface specification: '$(interface)'"))
187+
end
188+
symbol_idx = findfirst(s -> s == symbol_name, config.exported_symbols)
189+
if symbol_idx === nothing
190+
throw(ArgumentError("Invalid exported symbol name '$(symbol_name)'"))
191+
end
192+
# Convert to zero-indexed
193+
symbol_idx -= 1
194+
195+
forward_byte_offset = div(symbol_idx, 8)
196+
forward_byte_mask = 1 << mod(symbol_idx, 8)
197+
for lib in filter(l -> l.interface == interface, config.loaded_libs)
198+
if lib.active_forwards[forward_byte_offset+1] & forward_byte_mask != 0x00
199+
return lib
200+
end
201+
end
202+
203+
# No backing library was found
204+
return nothing
172205
end
173206

207+
208+
## NOTE: Manually setting forwards is referred to as the 'footgun API'. It allows truly
209+
## bizarre and complex setups to be created. If you run into strange errors while using
210+
## it, the first thing you should ask yourself is whether you've set things up properly.
174211
function lbt_set_forward(symbol_name, addr, interface, f2c = LBT_F2C_PLAIN; verbose::Bool = false)
175-
return ccall((:lbt_set_forward, libblastrampoline), Int32, (Cstring, Ptr{Cvoid}, Int32, Int32, Int32), symbol_name, addr, interface, f2c, verbose ? 1 : 0)
212+
return ccall(
213+
(:lbt_set_forward, libblastrampoline),
214+
Int32,
215+
(Cstring, Ptr{Cvoid}, Int32, Int32, Int32),
216+
string(symbol_name),
217+
addr,
218+
Int32(interface),
219+
Int32(f2c),
220+
verbose ? Int32(1) : Int32(0),
221+
)
222+
end
223+
function lbt_set_forward(symbol_name, addr, interface::Symbol, f2c::Symbol = :plain; kwargs...)
224+
return lbt_set_forward(symbol_name, addr, LBT_INV_INTERFACE_MAP[interface], LBT_INV_F2C_MAP[f2c]; kwargs...)
225+
end
226+
227+
function lbt_get_forward(symbol_name, interface, f2c = LBT_F2C_PLAIN)
228+
return ccall(
229+
(:lbt_get_forward, libblastrampoline),
230+
Ptr{Cvoid},
231+
(Cstring, Int32, Int32),
232+
string(symbol_name),
233+
Int32(interface),
234+
Int32(f2c),
235+
)
236+
end
237+
function lbt_get_forward(symbol_name, interface::Symbol, f2c::Symbol = :plain)
238+
return lbt_get_forward(symbol_name, LBT_INV_INTERFACE_MAP[interface], LBT_INV_F2C_MAP[f2c])
176239
end
177-
=#

0 commit comments

Comments
 (0)