Skip to content

Commit 58a0016

Browse files
authored
Merge pull request #31347 from JuliaLang/vc/loopinfo2
[Loopinfo] recursive conversion to metadata
2 parents d76a30a + 8f5ee84 commit 58a0016

File tree

4 files changed

+190
-27
lines changed

4 files changed

+190
-27
lines changed

src/cgutils.cpp

+25
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,31 @@ static Value *stringConstPtr(IRBuilder<> &irbuilder, const std::string &txt)
171171
}
172172
}
173173

174+
// --- MDNode ---
175+
Metadata *to_md_tree(jl_value_t *val) {
176+
if (val == jl_nothing)
177+
return nullptr;
178+
Metadata *MD = nullptr;
179+
if (jl_is_symbol(val)) {
180+
MD = MDString::get(jl_LLVMContext, jl_symbol_name((jl_sym_t*)val));
181+
} else if (jl_is_bool(val)) {
182+
MD = ConstantAsMetadata::get(ConstantInt::get(T_int1, jl_unbox_bool(val)));
183+
} else if (jl_is_long(val)) {
184+
MD = ConstantAsMetadata::get(ConstantInt::get(T_int64, jl_unbox_long(val)));
185+
} else if (jl_is_tuple(val)) {
186+
SmallVector<Metadata *, 8> MDs;
187+
for (int f = 0, nf = jl_nfields(val); f < nf; ++f) {
188+
MD = to_md_tree(jl_fieldref(val, f));
189+
if (MD)
190+
MDs.push_back(MD);
191+
}
192+
MD = MDNode::get(jl_LLVMContext, MDs);
193+
} else {
194+
jl_error("LLVM metadata needs to Symbol/Bool/Int or Tuple thereof");
195+
}
196+
return MD;
197+
}
198+
174199
// --- Debug info ---
175200

176201
static DIType *julia_type_to_di(jl_value_t *jt, DIBuilder *dbuilder, bool isboxed = false)

src/codegen.cpp

+1-26
Original file line numberDiff line numberDiff line change
@@ -4100,34 +4100,9 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval)
41004100
// parse Expr(:loopinfo, "julia.simdloop", ("llvm.loop.vectorize.width", 4))
41014101
SmallVector<Metadata *, 8> MDs;
41024102
for (int i = 0, ie = jl_expr_nargs(ex); i < ie; ++i) {
4103-
jl_value_t *arg = args[i];
4104-
Metadata *MD;
4105-
if (arg == jl_nothing)
4106-
continue;
4107-
if (jl_is_symbol(arg)) {
4108-
MD = MDString::get(jl_LLVMContext, jl_symbol_name((jl_sym_t*)arg));
4109-
} else if (jl_is_tuple(arg)) {
4110-
// TODO: are there loopinfo with more than one field?
4111-
if (jl_nfields(arg) != 2)
4112-
jl_error("loopinfo: only accept 2-arg tuple");
4113-
jl_value_t* name = jl_fieldref(arg, 0);
4114-
jl_value_t* value = jl_fieldref(arg, 1);
4115-
if (!jl_is_symbol(name))
4116-
jl_error("loopinfo: name needs to be a symbol");
4117-
Metadata *MDVal;
4118-
if(jl_is_bool(value))
4119-
MDVal = ConstantAsMetadata::get(ConstantInt::get(T_int1, jl_unbox_bool(value)));
4120-
if(jl_is_long(value))
4121-
MDVal = ConstantAsMetadata::get(ConstantInt::get(T_int64, jl_unbox_long(value)));
4122-
if(!MDVal)
4123-
jl_error("loopinfo: value can only be a bool or a long");
4124-
MD = MDNode::get(jl_LLVMContext,
4125-
{ MDString::get(jl_LLVMContext, jl_symbol_name((jl_sym_t*)name)), MDVal });
4126-
}
4103+
Metadata *MD = to_md_tree(args[i]);
41274104
if (MD)
41284105
MDs.push_back(MD);
4129-
else
4130-
jl_error("loopinfo: argument needs to be either a symbol or a tuple of type (Symbol, Union{Int, Bool}");
41314106
}
41324107

41334108
MDNode* MD = MDNode::get(jl_LLVMContext, MDs);

test/llvmpasses/Makefile

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@ SRCDIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
22
JULIAHOME := $(abspath $(SRCDIR)/../..)
33
include $(JULIAHOME)/Make.inc
44
test:
5-
PATH=$(build_bindir):$(build_depsbindir):$$PATH $(build_depsbindir)/lit/lit.py -v $(SRCDIR)
5+
PATH=$(build_bindir):$(build_depsbindir):$$PATH \
6+
LD_LIBRARY_PATH=${build_libdir}:$$LD_LIBRARY_PATH \
7+
$(build_depsbindir)/lit/lit.py -v $(SRCDIR)
68
.PHONY: test

test/llvmpasses/loopinfo.jl

+161
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# This file is a part of Julia. License is MIT: https://julialang.org/license
2+
3+
# RUN: julia --startup-file=no %s %t && llvm-link -S %t/* -o %t/module.ll
4+
# RUN: cat %t/module.ll | FileCheck %s
5+
# RUN: cat %t/module.ll | opt -load libjulia%shlibext -LowerSIMDLoop -S - | FileCheck %s -check-prefix=LOWER
6+
# RUN: julia --startup-file=no %s %t -O && llvm-link -S %t/* -o %t/module.ll
7+
# RUN: cat %t/module.ll | FileCheck %s -check-prefix=FINAL
8+
using InteractiveUtils
9+
using Printf
10+
11+
## Notes:
12+
# This script uses the `emit` function (defined at the end) to emit either
13+
# optimized or unoptimized LLVM IR. Each function is emitted individually and
14+
# `llvm-link` is used to create a single module that can be passed to opt.
15+
# The order in which files are emitted and linked is important since `lit` will
16+
# process the test cases in order.
17+
#
18+
# There are three different test prefixes defined:
19+
# - `CHECK`: Checks the result of codegen
20+
# - `LOWER`: Checks the result of -LowerSIMDLoop
21+
# - `FINAL`: Checks the result of running the entire pipeline
22+
23+
# get a temporary directory
24+
dir = ARGS[1]
25+
rm(dir, force=true, recursive=true)
26+
mkdir(dir)
27+
28+
# toggle between unoptimized (CHECK/LOWER) and optimized IR (FINAL).
29+
optimize=false
30+
if length(ARGS) >= 2
31+
optimize = ARGS[2]=="-O"
32+
end
33+
34+
# CHECK-LABEL: @julia_simdf_
35+
# LOWER-LABEL: @julia_simdf_
36+
# FINAL-LABEL: @julia_simdf_
37+
function simdf(X)
38+
acc = zero(eltype(X))
39+
@simd for x in X
40+
acc += x
41+
# CHECK: call void @julia.loopinfo_marker(), {{.*}}, !julia.loopinfo [[LOOPINFO:![0-9]+]]
42+
# LOWER-NOT: llvm.mem.parallel_loop_access
43+
# LOWER: fadd fast double
44+
# LOWER-NOT: call void @julia.loopinfo_marker()
45+
# LOWER: br {{.*}}, !llvm.loop [[LOOPID:![0-9]+]]
46+
# FINAL: fadd fast <{{[0-9]+}} x double>
47+
end
48+
acc
49+
end
50+
51+
# CHECK-LABEL: @julia_simdf2_
52+
# LOWER-LABEL: @julia_simdf2_
53+
function simdf2(X)
54+
acc = zero(eltype(X))
55+
@simd ivdep for x in X
56+
acc += x
57+
# CHECK: call void @julia.loopinfo_marker(), {{.*}}, !julia.loopinfo [[LOOPINFO2:![0-9]+]]
58+
# LOWER: llvm.mem.parallel_loop_access
59+
# LOWER-NOT: call void @julia.loopinfo_marker()
60+
# LOWER: fadd fast double
61+
# LOWER: br {{.*}}, !llvm.loop [[LOOPID2:![0-9]+]]
62+
end
63+
acc
64+
end
65+
66+
@noinline iteration(i) = (@show(i); return nothing)
67+
68+
# CHECK-LABEL: @julia_loop_unroll
69+
# LOWER-LABEL: @julia_loop_unroll
70+
# FINAL-LABEL: @julia_loop_unroll
71+
@eval function loop_unroll(N)
72+
for i in 1:N
73+
iteration(i)
74+
$(Expr(:loopinfo, (Symbol("llvm.loop.unroll.count"), 3)))
75+
# CHECK: call void @julia.loopinfo_marker(), {{.*}}, !julia.loopinfo [[LOOPINFO3:![0-9]+]]
76+
# LOWER-NOT: call void @julia.loopinfo_marker()
77+
# LOWER: br {{.*}}, !llvm.loop [[LOOPID3:![0-9]+]]
78+
# FINAL: call void @julia_iteration
79+
# FINAL: call void @julia_iteration
80+
# FINAL: call void @julia_iteration
81+
# FINAL-NOT: call void @julia_iteration
82+
# FINAL: br
83+
end
84+
end
85+
86+
# Example from a GPU kernel where we want to unroll the outer loop
87+
# and the inner loop is a boundschecked single iteration loop.
88+
# The `@show` is used to bloat the loop and `FINAL-COUNT-10:` seems
89+
# not to be working so we duplicate the checks.
90+
# CHECK-LABEL: @julia_loop_unroll2
91+
# LOWER-LABEL: @julia_loop_unroll2
92+
# FINAL-LABEL: @julia_loop_unroll2
93+
@eval function loop_unroll2(J, I)
94+
for i in 1:10
95+
for j in J
96+
1 <= j <= I && continue
97+
@show (i,j)
98+
iteration(i)
99+
end
100+
$(Expr(:loopinfo, (Symbol("llvm.loop.unroll.full"),)))
101+
# CHECK: call void @julia.loopinfo_marker(), {{.*}}, !julia.loopinfo [[LOOPINFO4:![0-9]+]]
102+
# LOWER-NOT: call void @julia.loopinfo_marker()
103+
# LOWER: br {{.*}}, !llvm.loop [[LOOPID4:![0-9]+]]
104+
# FINAL: call void @julia_iteration
105+
# FINAL: call void @julia_iteration
106+
# FINAL: call void @julia_iteration
107+
# FINAL: call void @julia_iteration
108+
# FINAL: call void @julia_iteration
109+
# FINAL: call void @julia_iteration
110+
# FINAL: call void @julia_iteration
111+
# FINAL: call void @julia_iteration
112+
# FINAL: call void @julia_iteration
113+
# FINAL: call void @julia_iteration
114+
# FINAL-NOT: call void @julia_iteration
115+
end
116+
end
117+
118+
# FINAL-LABEL: @julia_notunroll
119+
function notunroll(J, I)
120+
for i in 1:10
121+
for j in J
122+
1 <= j <= I && continue
123+
@show (i,j)
124+
iteration(i)
125+
# FINAL: call void @julia_iteration
126+
# FINAL-NOT: call void @julia_iteration
127+
end
128+
end
129+
end
130+
131+
## Check all the MD nodes
132+
# CHECK: [[LOOPINFO]] = !{!"julia.simdloop"}
133+
# CHECK: [[LOOPINFO2]] = !{!"julia.simdloop", !"julia.ivdep"}
134+
# CHECK: [[LOOPINFO3]] = !{[[LOOPUNROLL:![0-9]+]]}
135+
# CHECK: [[LOOPUNROLL]] = !{!"llvm.loop.unroll.count", i64 3}
136+
# CHECK: [[LOOPINFO4]] = !{[[LOOPUNROLL2:![0-9]+]]}
137+
# CHECK: [[LOOPUNROLL2]] = !{!"llvm.loop.unroll.full"}
138+
# LOWER: [[LOOPID]] = distinct !{[[LOOPID]]}
139+
# LOWER: [[LOOPID2]] = distinct !{[[LOOPID2]]}
140+
# LOWER: [[LOOPID3]] = distinct !{[[LOOPID3]], [[LOOPUNROLL:![0-9]+]]}
141+
# LOWER: [[LOOPUNROLL]] = !{!"llvm.loop.unroll.count", i64 3}
142+
# LOWER: [[LOOPID4]] = distinct !{[[LOOPID4]], [[LOOPUNROLL2:![0-9]+]]}
143+
# LOWER: [[LOOPUNROLL2]] = !{!"llvm.loop.unroll.full"}
144+
145+
# Emit LLVM IR to dir
146+
counter = 0
147+
function emit(f, tt...)
148+
global counter
149+
name = nameof(f)
150+
open(joinpath(dir, @sprintf("%05d-%s.ll", counter, name)), "w") do io
151+
code_llvm(io, f, tt, raw=true, optimize=optimize, dump_module=true, debuginfo=:none)
152+
end
153+
counter+=1
154+
end
155+
156+
# Maintaining the order is important
157+
emit(simdf, Vector{Float64})
158+
emit(simdf2, Vector{Float64})
159+
emit(loop_unroll, Int64)
160+
emit(loop_unroll2, Int64, Int64)
161+
emit(notunroll, Int64, Int64)

0 commit comments

Comments
 (0)