Skip to content

Copy instruction metadata in one call, iterate over call sites, and build a local LLVMExtra with optimization - #693

Merged
maleadt merged 4 commits into
mainfrom
tb/host-perf-3
Oct 19, 2026
Merged

maleadt merged 4 commits into
mainfrom
tb/host-perf-3

Conversation

@maleadt

@maleadt maleadt commented Oct 19, 2026

Copy link
Copy Markdown
Member

Three additions that came out of profiling passes written in Julia (JuliaIR.jl):

  • copy_metadata!(dest, src, kinds=()) copies the metadata of an instruction with LLVM's
    Instruction::copyMetadata (a new LLVMExtra function). Passes that replace an instruction
    did this through the metadata dictionary, which wraps every attachment and converts it
    through MetadataAsValue. The debug location is handled the way the LLVM version in use
    does: if src has none, LLVM 21 and later keep the one of dest, earlier versions remove
    it (unless src has no metadata at all). Copying the metadata of 5000 loads with !tbaa,
    !range, !noundef and a debug location: 5.4-9.2 ms and 4.7 MiB through the dictionary,
    0.37 ms and no allocations with copy_metadata!.
  • f.calls is a read-only view of the call, invoke and callbr instructions of a
    function, as CallBase values. It checks the opcode before wrapping an instruction, so
    passes that only look at calls don't wrap (and dynamically identify) every other
    instruction. Visiting the 1000 calls of a function with 10000 instructions: 0.13-0.23 ms
    when filtering bb.instructions, 0.09 ms with f.calls.
  • deps/build_local.jl now builds LLVMExtra as RelWithDebInfo, like the LLVMExtra_jll
    recipe. Without a build type CMake doesn't optimize, so local builds were -O0 and even
    one-line wrappers made out-of-line calls to unwrap. The build type is part of the
    directory the library is installed in, so an existing -O0 build of the same sources isn't
    reused. copy_metadata! on 5000 instructions: 0.53-0.56 ms with -O0, 0.37 ms optimized.

Measured with LLVM 18 on Julia 1.12, on a loaded machine; ranges are over separate processes.

New LLVMExtra function: LLVMExtraCopyMetadata, so this needs a new LLVMExtra_jll build
before release.

Passes that replace an instruction copy its metadata to the new one. Going
through the metadata dictionary for that wraps every attachment and converts
it through MetadataAsValue; Instruction::copyMetadata does it in one call, and
handles the debug location the way the LLVM version in use does (which
changed in LLVM 21).

Copying the metadata of 5000 loads with !tbaa, !range, !noundef and a debug
location (LLVM 18, Julia 1.12): 5.4-9.2 ms and 4.7 MiB through the
dictionary, 0.37 ms and no allocations with copy_metadata!.
Without a build type, CMake compiles without optimization, so a locally built
LLVMExtra was -O0, and even trivial wrappers made out-of-line calls to LLVM's
unwrap/wrap helpers. Use RelWithDebInfo, like the LLVMExtra_jll recipe.

copy_metadata! on 5000 instructions (LLVM 18, Julia 1.12): 0.53-0.56 ms with
the -O0 build, 0.37 ms with RelWithDebInfo.
Passes often only look at the calls in a function. Filtering the instructions
of every block wraps each of them, which identifies its concrete type with a
dynamic call; `f.calls` checks the opcode first and only wraps the calls.

Visiting the 1000 calls of a function with 10000 instructions and reading
their callee (LLVM 18, Julia 1.12): 0.13-0.23 ms and 158 KiB when filtering
`bb.instructions`, 0.09 ms and 125 KiB with `f.calls`.
@codecov

codecov Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

@@            Coverage Diff             @@
##             main     #693      +/-   ##
==========================================
+ Coverage   94.82%   94.84%   +0.01%     
==========================================
  Files          58       58              
  Lines        6817     6843      +26     
==========================================
+ Hits         6464     6490      +26     
  Misses        353      353              
Files with missing lines Coverage Δ
src/core/function.jl 99.58% <100.00%> (+0.04%) ⬆️
src/core/metadata.jl 90.32% <100.00%> (+0.14%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

LLVM is built without exceptions, and LLVMExtra neither throws nor
catches. Building it with exceptions enabled made GCC 14+ emit calls to
__cxa_call_terminate in optimized code, which the libstdc++ shipped with
Julia 1.10 and 1.11 on Windows doesn't export, so the RelWithDebInfo
library built by deps/build_local.jl failed to load there.
@maleadt
maleadt merged commit 23aff1e into main Oct 19, 2026
41 checks passed
@maleadt
maleadt deleted the tb/host-perf-3 branch October 19, 2026 17:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant