Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions docs/src/lib/analyses.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ PostDomTree
dispose(::PostDomTree)
dominates
recalculate!
split_block!
split_block_and_insert_if_then!
split_block_and_insert_if_then_else!
```

## Constant folding and simplification
Expand Down
2 changes: 0 additions & 2 deletions docs/src/lib/instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ remove!(::Instruction)
erase!(::Instruction)
is_trivially_dead
erase_trivially_dead!
is_promotable
promote_mem_to_reg!
comes_before
may_read_from_memory
may_write_to_memory
Expand Down
15 changes: 15 additions & 0 deletions docs/src/lib/transforms.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,21 @@ clone_into!
clone
```

## Control flow

```@docs
split_block!
split_block_and_insert_if_then!
split_block_and_insert_if_then_else!
```

## Promoting memory to registers

```@docs
is_promotable
promote_mem_to_reg!
```

## Inlining

```@docs
Expand Down
48 changes: 48 additions & 0 deletions docs/src/man/analyses.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,51 @@ function my_pass!(f::LLVM.Function, am)
return false
end
```


## Loops 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 (`SCEV`s),
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:

```jldoctest
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.
41 changes: 41 additions & 0 deletions docs/src/man/metadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,47 @@ ModuleMetadataIterator for module :
```


Assigning to `obj.metadata[kind]` replaces the attachments of that kind. Global objects can
have several attachments of one kind, e.g., a global variable that is described by several
`!dbg` expressions, and `add_metadata!` adds one while keeping the others:

```jldoctest
julia> gv = GlobalVariable(mod, LLVM.Int32Type(), "gv");

julia> add_metadata!(gv, "annotation", MDNode([MDString("first")]));

julia> add_metadata!(gv, "annotation", MDNode([MDString("second")]))
@gv = external global i32, !annotation !0, !annotation !1
```

Some metadata of an instruction only holds where the instruction is: e.g., a load can be
annotated with the `!range` of values it loads because a branch checked it. A pass that
moves such an instruction elsewhere needs to drop that metadata, which
`drop_unknown_metadata!` does for all kinds except for debug information and the kinds
that are passed to it:

```jldoctest
julia> mod = parse(LLVM.Module, """
define i32 @f(ptr %p) {
%v = load i32, ptr %p, !range !0, !noundef !1
ret i32 %v
}
!0 = !{i32 0, i32 10}
!1 = !{}""");

julia> load = first(only(mod.functions).entry.instructions)
%v = load i32, ptr %p, align 4, !range !0, !noundef !1

julia> drop_unknown_metadata!(load, ("noundef",));

julia> load
%v = load i32, ptr %p, align 4, !noundef !0
```

When IR is cloned or remapped (see [IR cloning](transforms.md#IR-cloning)), the metadata it refers to is
mapped as well, using `map_metadata`.


## Debug information

When generating IR, it is possible to add debug information metadata to the generated code.
Expand Down
115 changes: 115 additions & 0 deletions docs/src/man/transforms.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,121 @@ the conservative baseline).
See the API reference for the full list of overridable queries.


## IR transforms

Besides running passes, LLVM.jl wraps several of LLVM's transformation utilities, which
change specific parts of the IR. They are useful to write custom passes, or to make
targeted changes to IR without running a full pass. Unlike passes, they don't keep
analyses up to date, unless they take an analysis to update as an argument.

```@meta
DocTestSetup = quote
using LLVM, LLVM.IR, LLVM.Build, LLVM.Passes, LLVM.Analysis, LLVM.ORC

if context(; throw_error=false) === nothing
Context()
end
end
```

`promote_mem_to_reg!` does what the `mem2reg` pass does for a selection of allocas:
replacing them by SSA values, inserting phi nodes where needed. It needs the function's
dominator tree, which it does not change. `inline!` inlines the function that a call
instruction calls, returning `nothing` when it succeeded or a message explaining why the
call could not be inlined. Finally, `simplify_instruction` returns a simpler value that an
instruction is equivalent to, which can then replace the instruction:

```jldoctest ir_transforms
julia> mod = parse(LLVM.Module, """
define i32 @square(i32 %x) {
entry:
%y = mul i32 %x, %x
ret i32 %y
}
define i32 @caller(i32 %a) {
entry:
%p = alloca i32
store i32 %a, ptr %p
%v = load i32, ptr %p
%s = call i32 @square(i32 %v)
%r = add i32 %s, 0
ret i32 %r
}""");

julia> f = mod.functions["caller"];

julia> allocas = AllocaInst[inst for inst in f.entry.instructions if inst isa AllocaInst];

julia> all(is_promotable, allocas)
true

julia> @dispose domtree=DomTree(f) begin
promote_mem_to_reg!(allocas, domtree)
end;

julia> call = first(inst for inst in f.entry.instructions if inst isa CallInst);

julia> inline!(call) === nothing
true

julia> add = first(inst for inst in f.entry.instructions if inst isa LLVM.AddInst);

julia> val = simplify_instruction(add)
%y.i = mul i32 %a, %a

julia> replace_uses!(add, val);

julia> erase!(add);

julia> f
define i32 @caller(i32 %a) {
entry:
%y.i = mul i32 %a, %a
ret i32 %y.i
}
```

To change the control flow of a function, `split_block!` splits a basic block in two,
with the first part branching to the second. `split_block_and_insert_if_then!` also
inserts a block that only executes when a condition holds, and returns its terminator
before which code can be inserted. Its `weights` keyword argument attaches branch weights
to the new conditional branch, while passing a dominator tree as `domtree` updates that
tree for the new control flow. `split_block_and_insert_if_then_else!` does the same with a
block for either outcome:

```jldoctest ir_transforms
julia> mod = parse(LLVM.Module, """
define void @store_if(i1 %c, ptr %p) {
entry:
ret void
}""");

julia> f = only(mod.functions);

julia> c, p = f.parameters;

julia> term = split_block_and_insert_if_then!(c, f.entry.terminator; weights=(1, 9));

julia> @dispose builder=IRBuilder() begin
position!(builder, LLVM.before(term))
store!(builder, ConstantInt(Int32(42)), p)
end;

julia> f
define void @store_if(i1 %c, ptr %p) {
entry:
br i1 %c, label %0, label %1, !prof !0

0: ; preds = %entry
store i32 42, ptr %p, align 4
br label %1

1: ; preds = %entry, %0
ret void
}
```


## IR cloning

Somewhat distinct from IR passes, it is also possible to clone bits of the IR. This can be
Expand Down
Loading