@@ -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
2122const LBT_F2C_PLAIN = 0
2223const 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
3032struct LBTLibraryInfo
3133 libname:: String
@@ -164,14 +166,74 @@ function lbt_get_default_func()
164166 return ccall ((:lbt_get_default_func , libblastrampoline), Ptr{Cvoid}, ())
165167end
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
172205end
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 it's truly
174211function 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])
176239end
177- =#
0 commit comments