StaticLLVM
Documentation for StaticLLVM
StaticLLVM.LLVMBlock
StaticLLVM.MethodInfo
StaticLLVM.ModVarInfo
StaticLLVM.ModuleInfo
StaticLLVM.assemble_modinfo
StaticLLVM.build
StaticLLVM.clean_cache
StaticLLVM.collect_methods!
StaticLLVM.collect_modvar_pairs
StaticLLVM.compile_llvm_files
StaticLLVM.dump_llvm_ir
StaticLLVM.emit_llvm
StaticLLVM.emit_llvm
StaticLLVM.emit_native
StaticLLVM.emit_native
StaticLLVM.extract_llvm
StaticLLVM.find_matching_brace
StaticLLVM.get_arg_types
StaticLLVM.get_config
StaticLLVM.get_mod_filepath
StaticLLVM.is_static_code
StaticLLVM.load_pkg
StaticLLVM.make_modvar_def
StaticLLVM.patch_memory_alloc!
StaticLLVM.patch_memory_instance!
StaticLLVM.pyprint
StaticLLVM.recover_heap_object
StaticLLVM.recover_heap_object
StaticLLVM.remove_memoryref_calls
StaticLLVM.remove_substrings
StaticLLVM.replace_memory_alloc
StaticLLVM.run_command
StaticLLVM.split_blocks
StaticLLVM.strip_comments
StaticLLVM.strip_gc_allocations
StaticLLVM.write_if_changed
StaticLLVM.LLVMBlock
— TypeLLVMBlock(lable::Symbol, index::Int, ir::SubString{String})
Represents a basic block in LLVM IR with its label, index (order), and IR body.
label::Symbol
:Identifier for the LLVM IR block, e.g.:entry
,:L3
, etc.index::Int
: The order of block in LLVM IR function.ir::SubString{String}
: A substring view of the whole block.
StaticLLVM.MethodInfo
— TypeMethodInfo(m::Core.Method)
MethodInfo(m::Core.Method, name::Symbol())
A mutable struct that stores metadata and compiled LLVM IR information for a specific Julia method.
Fields
name::Symbol
: A user-friendly name for the method. Defaults to the mangled name if not provided.mangled::Symbol
: The internal compiler name of the method, used for LLVM IR lookup.arg_types::Tuple
: A tuple of argument types (excluding the function type), derived from the method signature.method::Core.Method
: The original JuliaMethod
objectllvm_ir::String
: The LLVM IR code generated for the method, extracted as a string.
StaticLLVM.ModVarInfo
— TypeModVarInfo(name::String, mod::Module, mangled::String, llvm_def::String, llvm_decl::String)
Holds metadata for a module-level mutable variable (ModVar
):
name::Symbol
: Julia binding namefile::Symbol
: file of mod-varsmangled::String
: LLVM mangled global namellvm_def::String
: LLVM IR global variable definitionllvm_decl::String
: LLVM IR external declaration
StaticLLVM.ModuleInfo
— TypeModuleInfo
Stores metadata associated with a Julia Module
, including referenced methods and global module-level variables.
Fields
mod::Module
: The Julia module this information is associated with.mangled::String
: The mangled name of the module, following the Itanium C++ ABImodvars::IdSet{ModVarInfo}
: A set of global static variables (ModVarInfo
) used in this module. Identity-based (IdSet
) to ensure uniqueness by object reference.methods::Vector{MethodInfo}
: A list of methods (MethodInfo
) defined or referenced within this module.
Usage
ModuleInfo
is typically constructed internally during code analysis or code generation workflows to track both method definitions and global state referenced by a module.
StaticLLVM.assemble_modinfo
— Methodassemble_modinfo(config::Dict{String,Any}, method_map::IdDict{Core.Method,Symbol}, modvar_map::IdDict{UInt,ModVarInfo}, check_ir::Bool = true) -> IdDict{Module, ModuleInfo}
Constructs ModuleInfo
objects that capture method-level (MethodInfo
) and global variable (ModVarInfo
) metadata within each Julia module.
Each entry in method_map
is processed to generate a MethodInfo
object containing LLVM IR and related properties. Similarly, each global variable entry in modvar_map
is assigned to the appropriate module.
Arguments
config::Dict{String,Any}
: Configuration dictionary. Must include keys like"debug"
and"policy"
, controlling diagnostics and symbol filtering.method_map::IdDict{Core.Method,Symbol}
: Maps JuliaMethod
objects to their LLVM mangled symbol names.modvar_map::IdDict{UInt,ModVarInfo}
: Maps raw pointer addresses (asUInt
) to their corresponding global variable metadata.
LLVM IR policy Behavior
:warn
: Emits a warning if non-static LLVM IR is found.:strict
: Throws an error.:strip
: Attempts to strip the specific GC-related IR allocations:strip_all
: Attempts to strip all GC-related IR allocations.
Returns
- An
IdDict{Module, ModuleInfo}
mapping each involved JuliaModule
to its assembledModuleInfo
representation, including static methods and module-level variables.
StaticLLVM.build
— Functionbuild(mod::Module=Main, config::Dict=default_config())
Main build process:
- Parse arguments if called from
Main
. - Collect global variables and method info.
- Dump LLVM IR files.
- Optionally compile with clang.
Arguments
mod
: The module to process (defaults toMain
).config
: Build configuration dictionary.
Supported compile modes:
:none
– Just generate IR:onefile
– Compile all IR into a single binary:makefile
– (Not implemented)
StaticLLVM.clean_cache
— Methodclean_cache(path::String)
Delete cached build files (e.g., .o, .so, .dll, .lib, .a, .dylib) under the given directory. This is useful for cleaning intermediate or compiled files before a fresh build.
Arguments
path
: Directory where cache files are stored.
StaticLLVM.collect_methods!
— Functioncollect_methods!(name_map::IdDict{Method, Symbol}, method::Method)
Recursively collect methods starting from method
, ensuring all are precompiled and mapped to mangled names. This function avoids use of global state by requiring an explicit name map to be passed in.
Arguments
method::Method
: The starting method to process.name_map::IdDict{Method, Symbol}
: A dictionary to store original methods and their original names.
Behavior
- Ensures the method is precompiled.
- Stores a mapping from the method to its mangled name.
- Recursively processes
Core.MethodInstance
objects found inmethod.roots
.
StaticLLVM.collect_modvar_pairs
— Methodcollect_modvars(mod::Module) -> Vector{Tuple{Int, ModVarInfo}}
Recursively collects all mutable, constant global variables defined in a Julia module mod
(excluding functions, types, and strings), and returns a list of (pointer, ModVarInfo)
pairs.
Each ModVarInfo
contains:
- the original symbol name
- the module it belongs to
- its mangled LLVM symbol name
- its LLVM IR definition and declaration
StaticLLVM.compile_llvm_files
— Methodcompile_llvm_files(config::Dict)
Compile all LLVM IR files in a specified directory into a single output binary.
Expected keys in config
:
"module"
: Name of the output executable."dir"
: Directory containing.ll
files."clang"
: Path toclang
compiler."cflag"
: Compiler flags (as a single string, e.g. "-O2 -flto").
Prints status messages and compilation result.
StaticLLVM.dump_llvm_ir
— Methoddump_llvm_ir(modinfo::ModuleInfo, output_dir::String, check::Bool)
Write LLVM IR files for a given ModuleInfo
instance.
- Writes the IR of static module variables into one file named after the module.
- Writes the IR for each method individually into separate files.
- Skips writing files if content is unchanged (if
check
is true).
StaticLLVM.emit_llvm
— Functionemit_llvm(fn::Function, args::Union{Tuple, Nothing}=nothing; clean::Bool=true, dump::Bool=true) -> String
Generate LLVM IR for a specific method of a Julia function specialized on given argument types.
Arguments
fn
: Julia function whose LLVM IR is requested.args
: Tuple of argument types specifying the method specialization; ifnothing
, expect exactly one method forfn
.clean
: Remove extraneous comments and optionally add header if true (default: true).dump
: Include full LLVM module in output if true (default: true).
Returns
- LLVM IR string of the matched method, optionally cleaned.
Behavior
- If
args
is provided, usewhich
to find the exact method. - If
args
isnothing
, expectfn
to have exactly one method, or throw an error. - Delegates actual IR emission to another
emit_llvm
method accepting aMethod
.
Example
ir = emit_llvm(sin, (Float64,); clean=true, dump=false)
println(ir)
add(x::Int) = x + 1
ir = emit_llvm(add)
println(ir)
StaticLLVM.emit_llvm
— Methodemit_llvm(method::Core.Method; clean::Bool=true, dump::Bool=true) -> String
Generate the LLVM IR for a given Julia method.
Arguments
method
: TheCore.Method
object to generate LLVM IR for.clean
: Iftrue
, strip comments and optionally prepend a header comment. Default istrue
.dump
: Iftrue
, include the full LLVM module in the output. Default istrue
.
Returns
- A string containing the LLVM IR of the method. When
clean
istrue
, comments are stripped.
Details
- Extracts the function instance and argument types from the method signature.
- Uses
InteractiveUtils.code_llvm
to get the LLVM IR as a string. - Optionally cleans the IR by removing comments using
strip_comments
. - When cleaning, adds the method signature as a header comment.
Example
ir = emit_llvm(my_method, clean=true, dump=false)
println(ir)
StaticLLVM.emit_native
— Functionemit_native(fn::Function, args::Union{Tuple, Nothing}=nothing; clean::Bool=true, dump::Bool=true) -> String
Generate native LLVM assembly for a specific method of a Julia function given argument types.
Arguments
fn
: Julia function whose LLVM IR is requested.args
: Tuple of argument types specifying the method specialization; ifnothing
, expect exactly one method forfn
.clean
: Remove extraneous comments and optionally add header if true (default: true).dump
: Include full LLVM module in output if true (default: true).
Returns
- A string containing the native LLVM assembly code.
- When
clean
istrue
, comments and debug info are removed.
Behavior
- If
args
is provided, usewhich
to find the exact method. - If
args
isnothing
, expectfn
to have exactly one method, or throw an error. - Delegates actual IR emission to another
emit_llvm
method accepting aMethod
.
Example
ir = emit_native(sin, (Float64,); clean=true, dump=false)
println(ir)
add(x::Int) = x + 1
ir = emit_native(add)
println(ir)
StaticLLVM.emit_native
— Methodemit_native(method::Core.Method; clean::Bool=true, dump::Bool=true) -> String
Generate the native LLVM bitcode (assembly) for a given Julia method.
Arguments
method
: TheCore.Method
to generate native code for.clean
: Iftrue
, remove comments and debug info from the output. Default istrue
.dump
: Iftrue
, include the full module dump. Default istrue
.
Returns
- A string containing the native LLVM assembly code.
- When
clean
istrue
, comments and debug info are removed.
Details
- Extracts the function instance and argument types from the method signature.
- Calls
InteractiveUtils.code_native
to get native LLVM assembly. - Controls debug info level:
:none
if clean, otherwise:default
. - Optionally cleans the output by stripping comments.
Example
native_ir = emit_native(my_method, clean=true, dump=false)
println(native_ir)
StaticLLVM.extract_llvm
— Functionextract_llvm(method::Core.Method, ir::String; main::Bool=false) -> String
Extract and clean up the LLVM IR of a single Julia-compiled function from the full IR string ir
.
Arguments
method::Core.Method
: The Julia method to locate in the LLVM IR.ir::String
: The full LLVM IR text to search within.main::Bool=false
: If true, rename the function to@main
, otherwise use the Julia function name.
Returns
String
: A cleaned and rewritten IR block for the requested function, including global constants and necessary declarations.
Notes
- Handles name mangling in
@julia_<funcname>_<id>
style. - Rewrites global constant names for uniqueness.
- Gathers required
declare
lines and LLVM attributes for external linkage.
StaticLLVM.find_matching_brace
— Functionfind_matching_brace(s::String, start_pos::Int=1) -> Int
Finds the index of the closing brace '}' that matches the first opening brace '{' found at or after start_pos
in the string s
.
Returns the index of the matching closing brace, or -1 if:
- No opening brace is found at or after
start_pos
, or - Braces are unbalanced and a match can't be found.
Arguments
s
: The input string to search.start_pos
: The position in the string to start searching from (1-based). Defaults to 1.
Example
find_matching_brace("a{b{c}d}e") # returns 9
find_matching_brace("abc", 1) # returns -1
StaticLLVM.get_arg_types
— Methodget_arg_types(m::Core.Method) -> Tuple
Extracts the argument types (excluding the function itself) from the method's signature.
StaticLLVM.get_config
— Methodget_config(; kwargs...) -> Dict{String, Any}
Return a copy of the default config, with optional keyword overrides.
StaticLLVM.get_mod_filepath
— Methodget_mod_filepath(mod::Module) -> Symbol
Retrieve the source file path symbol where the given module mod
is defined.
Arguments
mod::Module
: The Julia module to inspect.
Returns
Symbol
: The source file path as a Symbol if found.
Behavior
- Checks if the module has a special field
:_source_file_
and returns it if present. - Otherwise, scans module names (excluding some built-ins) to find a function defined solely in this module and returns the file path of that function's method.
- Throws an error if no suitable source file path is found.
Notes
- Skips imported names and private names starting with
#
. - Excludes names like
:eval
and:include
to avoid common standard functions.
StaticLLVM.is_static_code
— Methodis_static_code(ir::String)::Bool
Determines whether the given LLVM IR string is "static", i.e., free from dynamic symbols or Julia internal functions.
Returns
true
if the IR contains no known dynamic patterns.false
if any non-static signature (like@ijl_
) is found.
StaticLLVM.load_pkg
— Methodload_pkg() -> Module
Load a Julia package or source file specified by the first command-line argument (ARGS[1]
).
Behavior
- If
ARGS[1]
is a file path, includes the file and extracts the package name from the filename. - Otherwise, attempts to
import
the package by name. - If import fails and a directory with the package name exists, tries to include the source file under
./<package>/src/<package>
. - Raises an error if the package cannot be found or loaded.
- Optionally, uses
ARGS[2]
as the module name to return; defaults to the package name.
Returns
- The loaded Julia module.
Notes
- Depends on global
ARGS
array (command line arguments). - Prints status messages indicating loading steps.
Example
```bash julia script.jl MyPackage OptionalModuleName
StaticLLVM.make_modvar_def
— Methodmake_modvar_def(name::String, value::T, is_const::Bool = false) -> (String, String)
Generate LLVM IR global variable definition and external declaration strings for a Julia module variable.
name
: The variable name to be used in LLVM IR (as@name
).value
: The Julia module variable value to represent.is_const
: If true, the LLVM global is markedconstant
; otherwise, it's mutable (global
).
Returns a tuple (definition::String, declaration::String)
where:
definition
is the LLVM IR global definition string with initialization.declaration
is the LLVM IR external global declaration string.
Supported Julia types for value
:
- Floating point: Float64, Float32
- Integer types: Int8, Int16, Int32, Int64, Int128 and unsigned equivalents
- Bool
- Ptr types
- String
- Immutable bitstypes (non-primitive)
Throws an error if the type is unsupported.
StaticLLVM.patch_memory_alloc!
— Methodpatch_memory_alloc!(block::LLVMBlock, type_map::Vector{Pair{String, Int}}) -> Bool
Replaces the LLVM IR code that performs allocation via jl_alloc_genericmemory
with explicit malloc
or calloc
instructions, based on whether the allocated type is mutable or abstract.
Arguments
block
: AnLLVMBlock
containing the IR code.type_map
: A list ofPair{String, Int}
, mapping type IDs (e.g.,"GenericMemory#1222"
) to an integer flag (0 = mutable/abstract, 1 = concrete/immutable).
Returns
true
if the replacement occurred,false
otherwise.
StaticLLVM.patch_memory_instance!
— Methodpatch_memory_instance!(blocks_map::Dict{Symbol, LLVMBlock}, block::LLVMBlock) -> Bool
Patch the LLVM IR of a block to replace a specific Core.GenericMemory
atomic load instruction and simplify its associated conditional branch logic.
Arguments
blocks_map
: A dictionary mapping basic block names (asSymbol
) toLLVMBlock
objects.block
: The LLVM block whose IR may contain aCore.GenericMemory
load instruction.
Returns
true
if the block was modified,false
otherwise.
Description
This function searches for a specific pattern in the LLVM IR indicating the use of a hard-coded atomic load
from Core.GenericMemory#<id>.jit
. If found, the instruction is replaced with a cast from a globally defined pointer @GenericMemoryInstance
. The jump logic immediately following the load is also simplified to jump unconditionally to the "success" label.
This is a low-level IR patching utility meant to canonicalize memory access logic.
StaticLLVM.pyprint
— Methodpyprint(args...; sep=" ", tail="
")
Print multiple arguments joined by a separator and ending with a specified tail string.
Arguments
args...
: A variable number of arguments to be printed.sep
: Separator string inserted between arguments. Default is a single space" "
.tail
: String appended at the end of the output. Default is newline `"
"`.
Behavior
- Converts all arguments to strings.
- Joins them with the separator.
- Prints the resulting string followed by the tail string.
Example
pyprint("Hello", "world", 123; sep=", ", tail="!
")
# Output: Hello, world, 123!
StaticLLVM.recover_heap_object
— Methodrecover_heap_object(addr::Integer) -> Any
Given a raw address (e.g. from pointer_from_objref
), attempts to reconstruct the original Julia object stored at that memory location.
This function inspects the memory layout:
- If the tag indicates a
String
, reconstruct it. - If the tag seems to point to a valid heap-allocated
DataType
, rehydrate the object.
Returns nothing
if the tag is not recognizable or unsupported.
StaticLLVM.recover_heap_object
— Methodrecover_heap_object(p::Ptr) -> Any
Low-level internal logic to reconstruct a Julia object from a raw pointer p
. This inspects the memory tag to determine the type of the object.
Used internally by recover_heap_object
.
StaticLLVM.remove_memoryref_calls
— Methodremove_memoryref_calls(block::LLVMBlock) -> Bool
Scans the LLVM IR in block
and removes all lines that match calls to @memoryref
. Returns true
if any modifications were made.
Arguments
block
: TheLLVMBlock
to process.
Returns
true
if the block's IR was modified,false
otherwise.
StaticLLVM.remove_substrings
— Methodremove_substrings(input_str::AbstractString, ranges::Vector{Tuple{Int, Int}}) -> String
Removes substrings from input_str
specified by the list of ranges
.
Arguments
input_str
: The original string.ranges
: A vector of(start, stop)
index tuples indicating substrings to remove.
Returns
- A new string with the specified substrings removed.
Example
remove_substrings("Hello, world!", [(1,5), (8,8)]) # returns ", orld!"
StaticLLVM.replace_memory_alloc
— Methodreplace_memory_alloc(method::MethodInfo)
Scans and patches LLVM IR in the given method
to replace allocations related to Core.GenericMemory
.
This function:
- Extracts memory layout sizes for GenericMemory types from method metadata.
- Iterates over all LLVM functions in the method, splits them into blocks.
- For each block, attempts to patch GenericMemory instances and allocations, and removes calls to
@memoryref
. - Finally updates the LLVM IR in the method with the patched blocks.
Arguments
method
: AMethodInfo
struct containing LLVM IR and metadata.
Notes
- Relies on low-level unsafe pointer operations to inspect Julia internal data.
- Emits a warning if element size exceeds 256 bytes.
- Throws an error if no GenericMemory alias is found.
StaticLLVM.run_command
— Methodrun_command(cmd::Cmd; verbose::Bool=false) -> NamedTuple
Run the given command and capture its output.
Arguments
cmd
: ACmd
object representing the system command to execute.verbose
: If true, prints the command before execution.
Returns
A named tuple (success, code, output)
:
success
:true
if the command succeeded,false
otherwise.code
: Exit code (0 if success, -1 if error caught).output
: Command output or error message as a string.
StaticLLVM.split_blocks
— Methodsplit_blocks(ir::AbstractString) -> Vector{LLVMBlock}
Splits a full LLVM IR string into a list of LLVMBlock
objects, based on labeled basic blocks (e.g., entry:
, L3:
).
Arguments
ir
: A string containing LLVM IR code.
Returns
- A vector of
LLVMBlock
objects, each representing a labeled block of IR code.
Example
blocks = split_blocks(ir_string)
StaticLLVM.strip_comments
— Methodstrip_comments(ir::String) -> String
Removes comments and trailing whitespace from LLVM IR code lines, while preserving leading indentation and empty lines with no code content.
Arguments
ir
: A multiline string containing LLVM IR code.
Returns
- A new string where each line has comments (starting with
;
) and trailing spaces removed. - Lines that contain only whitespace or comments are omitted.
Details
- The function splits the input text into lines.
- For each line, it finds the first comment delimiter
;
. - It keeps only the part of the line before the comment.
- Trailing whitespace is trimmed, but leading whitespace (indentation) is preserved.
- Empty or whitespace-only lines after stripping are skipped.
- The resulting lines are joined back with newline characters.
Example
code = """
define i32 @main() {
%1 = add i32 1, 2 ; addition
ret i32 %1 ; return value
}
"""
println(strip_comments(code))
# Output:
# define i32 @main() {
# %1 = add i32 1, 2
# ret i32 %1
# }
StaticLLVM.strip_gc_allocations
— Methodstrip_gc_allocations(ir::String)::String
Clean up Julia IR by removing GC-related stack management and replacing @ijl_gc_pool_alloc_instrumented
calls with standard malloc
calls for further IR-level optimization or analysis.
Arguments
ir::String
: The input LLVM IR string generated by Julia.
Returns
- A cleaned-up IR string with GC stack frames and pool allocation calls removed or replaced.
StaticLLVM.write_if_changed
— Methodwrite_if_changed(filepath::String, content::String, check::Bool)::Int
Writes content
to filepath
only if the file content has changed or doesn't exist. If check
is false, no writing occurs.
Returns
- 1 if the file was written (or would be written).
- 0 if no writing was done due to
check == false
.