Analyses
LLVM.jl provides access to several of LLVM's analyses, which are part of the LLVM.Analysis vocabulary. Most of them are available to custom passes, through the analysis manager of a pass pipeline (see FunctionAnalysisManager).
IR verification
IR contained in modules and functions can be verified using the verify function, throwing a Julia exception when the IR is invalid:
julia> mod = parse(LLVM.Module, """
define i32 @example(i1 %cond, i32 %val) {
entry:
br i1 %cond, label %foo, label %bar
foo:
%ret = add i32 %val, 1
br label %bar
bar:
ret i32 %ret
}""");
julia> verify(mod)
ERROR: LLVM error: Instruction does not dominate all uses!
%ret = add i32 %val, 1
ret i32 %retFunctions can be verified in the same way, using verify(f). To handle invalid IR without catching an exception, e.g., to report it with more context, use verification_error, which returns the verifier's message, or nothing if the IR is valid.
Dominator and post-dominator
Dominator and post-dominator analyses can be performed on functions by constructing respectively a DomTree and PostDomTree object, and using the dominates function:
julia> fun
define i32 @example(i1 %cond, i32 %val) {
entry:
br i1 %cond, label %foo, label %bar
foo: ; preds = %entry
%ret = add i32 %val, 1
br label %bar
bar: ; preds = %foo, %entry
ret i32 %ret
}
julia> tree = DomTree(fun);
julia> dominates(tree, first(entry.instructions), first(foo.instructions))
true
julia> dominates(tree, first(foo.instructions), first(bar.instructions))
false
julia> tree = PostDomTree(fun);
julia> dominates(tree, first(bar.instructions), first(foo.instructions))
true
julia> dominates(tree, first(foo.instructions), first(entry.instructions))
falseConstant ranges and known bits
Many analyses describe the possible values of integers as a range, or as the bits that are known to be zero or one. These are represented by the ConstantRange and KnownBits values, which can also be used on their own. Computations on them are performed by LLVM:
julia> r = ConstantRange(64, 0, 100)
ConstantRange(64, 0, 100)
julia> r.unsigned_max
0x0000000000000063
julia> r * ConstantRange(64, 4) + ConstantRange(64, 1)
ConstantRange(64, 1, 398)
julia> intersect_with(r, ConstantRange(64, 50, 200))
ConstantRange(64, 50, 100)
julia> ConstantRange(KnownBits(64, ~UInt64(0xff), 0))
ConstantRange(64, 0, 256)The range and known bits of an integer value can be computed using LLVM's value tracking, by passing the value to the ConstantRange or KnownBits constructor. To also use the assumptions (llvm.assume calls) that hold at a certain point, pass the instruction at that point together with the function's assumption cache and dominator tree, which are typically obtained from the analysis manager of a custom pass:
function my_pass!(f::LLVM.Function, am)
ac, dt = am[AssumptionCache], am[DomTree]
for bb in f.blocks, inst in bb.instructions
inst isa GetElementPtrInst || continue
idx = last(inst.operands)
r = ConstantRange(idx; at=inst, assumptions=ac, domtree=dt)
# ...
end
return false
endLoops and scalar evolution
The loops of a function are described by the LoopInfo analysis, which iterates over the outermost loops of a function. Each Loop has properties for its structure, like its header, latch, preheader, exit_blocks and nested subloops. LLVM's scalar evolution analysis (ScalarEvolution) represents integer values as symbolic expressions (SCEVs), e.g., an induction variable as an add recurrence {start,+,step}<loop>. It can compute the number of times a loop's backedge is taken (backedge_taken_count) and the step of an induction variable (step_recurrence). Both analyses are obtained from the analysis manager of a custom pass:
julia> function trip_counts!(f::LLVM.Function, am)
li, se = am[LoopInfo], am[ScalarEvolution]
for loop in li
println(loop.header.name, " (depth ", loop.depth, "): ",
backedge_taken_count(se, loop))
for inst in loop.header.instructions
inst isa PHIInst || continue
s = se[inst]
println(" ", inst.name, " = ", s, ", step ", step_recurrence(se, s))
end
end
return PreservedAnalyses(AllAnalyses)
end;
julia> mod = parse(LLVM.Module, """
define void @count(i64 %n) {
entry:
br label %loop
loop:
%i = phi i64 [ 0, %entry ], [ %next, %loop ]
%next = add nuw nsw i64 %i, 2
%done = icmp uge i64 %next, %n
br i1 %done, label %exit, label %loop
exit:
ret void
}""");
julia> run!(FunctionPass("trip-counts", trip_counts!; analyses=true), mod)
loop (depth 1): SCEVUDivExpr(((-1 + (2 umax %n)) /u 2))
i = SCEVAddRecExpr({0,+,2}<nuw><nsw><%loop>), step SCEVConstant(2)Like other analysis results, loops and expressions are owned by the analysis manager, and must not be used after the pass returns.