Hello all!
I’m encountering a very strange issue using llvmcall
on my Apple M2 Mac with Julia 1.11.3. Hoping someone might have some insights or suggestions.
Problem:
When I try to use llvmcall
to call basic LLVM scalar integer intrinsics (like llvm.abs.i64
, llvm.neg.i64
, llvm.add.i64
, etc.), I consistently get the error: "ERROR: llvmcall only supports intrinsic calls"
. This is happening even when I believe I am correctly calling intrinsics with the right types (using Int64
or VecElement{Int64}
, etc.).
However, llvmcall
seems to work perfectly fine for floating-point intrinsics (both scalar like llvm.pow.f64
and vector like llvm.fmuladd.v2f64
).
Example Code (Failing - Integer Intrinsics):
julia> abs_i64(x) = ccall("llvm.abs.i64", llvmcall, Int64, (Int64,), x)
abs_i64 (generic function with 1 method)
julia> abs_i64(-5)
ERROR: llvmcall only supports intrinsic calls
Stacktrace:
[1] abs_i64(x::Int64)
@ Main ./REPL[1]:1
[2] top-level scope
@ REPL[2]:1
julia> neg_i64(x::Int64) = ccall("llvm.neg.i64", llvmcall, Int64, (Int64,), x)
neg_i64 (generic function with 1 method)
julia> neg_i64(5)
ERROR: llvmcall only supports intrinsic calls
Stacktrace:
[1] neg_i64(x::Int64)
@ Main ./REPL[3]:1
[2] top-level scope
@ REPL[4]:1
# ... (and similarly for add_i64, sub_i64, mul_i64, sdiv_i64, srem_i64, pmull64) ...
Example Code (Working - Floating-Point Intrinsics):
julia> llvm_pow(a, b) = ccall("llvm.pow.f64", llvmcall, Float64, (Float64, Float64), a, b)
llvm_pow (generic function with 1 method)
julia> llvm_pow(2.0, 3.0)
8.0
julia> fmuladd_v2f64(a, b, c) = ccall("llvm.fmuladd.v2f64", llvmcall, NTuple{2, VecElement{Float64}}, (NTuple{2, VecElement{Float64}}, NTuple{2, VecElement{Float64}}, NTuple{2, VecElement{Float64}}), a, b, c)
fmuladd_v2f64 (generic function with 1 method)
julia> a_vec = (VecElement(1.0), VecElement(1.0))
julia> fmuladd_v2f64(a_vec, a_vec, a_vec)
(VecElement{Float64}(2.0), VecElement{Float64}(2.0))
Observations with @code_llvm
:
When I examine the generated LLVM IR using @code_llvm
for a failing integer intrinsic, it appears that Julia is not even attempting to call the intrinsic. Instead, it’s directly generating code to throw the error. For example:
julia> @code_llvm abs_i64(-5)
; Function Signature: abs_i64(Int64)
; @ REPL[1]:1 within `abs_i64`
define i64 @julia_abs_i64_9092(i64 signext %"x::Int64") #0 {
top:
call void @ijl_error(ptr nonnull @"_j_str_llvmcall only supports in...#1")
unreachable
}
This suggests the issue is not with the LLVM code generation itself, but rather with how Julia is resolving or recognizing scalar integer intrinsics within llvmcall
on my system.
Environment Information:
julia> versioninfo()
Julia Version 1.11.3
Commit d63adeda50d (2025-01-21 19:42 UTC)
Build Info:
Official https://julialang.org/ release
Platform Info:
OS: macOS (arm64-apple-darwin24.0.0)
CPU: 8 × Apple M2
WORD_SIZE: 64
LLVM: libLLVM-16.0.6 (ORCJIT, apple-m2)
Threads: 1 default, 0 interactive, 1 GC (on 4 virtual cores)
Questions:
- Is this a known issue with
llvmcall
on Apple M2 or ARM64? - Is there something I’m missing in how I’m defining or calling these integer intrinsics?
- Are there any workarounds or alternative approaches to achieve the same functionality (calling LLVM integer intrinsics)?
- Any guidance on how to further debug this or report it as a potential bug would be awesome!
Thank you all