check_allocs(func, types; ignore_throw=true)

Compiles the given function and types to LLVM IR and checks for allocations.

Returns a vector of AllocationSite, DynamicDispatch, and AllocatingRuntimeCall


The Julia language/compiler does not guarantee that this result is stable across Julia invocations.

If you rely on allocation-free code for safety/correctness, it is not sufficient to verify check_allocs in test code and expect that the corresponding call in production will not allocate at runtime.

For this case, you must use @check_allocs instead.


julia> function foo(x::Int, y::Int)
           z = x + y
           return z
foo (generic function with 1 method)

julia> allocs = check_allocs(foo, (Int, Int))

A :dispatch function is responsible for a "dynamic dispatch" to an unknown Julia function.

An :alloc functions is used by codegen to lower allocations for mutable structs, arrays, and other Julia objects.

A :runtime function is any function used by the runtime which does not explicitly perform allocation, but which might allocate to get its job done (e.g. jl_subtype).

compile_callable(f, tt=Tuple{}; kwargs...)

Low-level interface to compile a function invocation for the provided function and tuple of argument types using the naive JuliaOJIT() pipeline.

The output of this function is automatically cached, so that new code will be generated automatically and checked for allocations whenever the function changes or when different types or keyword arguments are provided.


Find all static allocation sites in the provided LLVM IR.

This function modifies the LLVM module in-place, effectively trashing it.


Takes a function definition and returns the expressions needed to forward the arguments to an inner function.

For example function foo(a, ::Int, c...; x, y=1, z...) will

  1. modify the function to gensym() nameless arguments
  2. return (:a, gensym(), :(c...)), (:x, :y, :(z...)))

Resolve the callee of a call embedded in Julia-constructed LLVM IR and replace it with a new locally-declared function that has the resolved name as its identifier.

@check_allocs ignore_throw=true (function def)

Wraps the provided function definition so that all calls to it will be automatically checked for allocations.

If the check fails, an AllocCheckFailure exception is thrown containing the detailed failures, including the backtrace for each defect.

Note: All calls to the wrapped function are effectively a dynamic dispatch, which means they are type-unstable and may allocate memory at function entry. @check_allocs only guarantees the absence of allocations after the function has started running.


julia> @check_allocs multiply(x,y) = x*y
multiply (generic function with 1 method)

julia> multiply(1.5, 3.5) # no allocations for Float64

julia> multiply(rand(3,3), rand(3,3)) # matmul needs to allocate the result
ERROR: @check_allocs function contains 1 allocations.

 [1] macro expansion
   @ ~/repos/AllocCheck/src/macro.jl:134 [inlined]
 [2] multiply(x::Matrix{Float64}, y::Matrix{Float64})
   @ Main ./REPL[2]:133
 [3] top-level scope
   @ REPL[5]:1