Skip to content

Commit 7735556

Browse files
Name LLVM function arguments (#50500)
This makes it easier to correlate LLVM IR with the originating source code by including both argument name and argument type in the LLVM argument variable. <details> <summary>Example 1</summary> ```julia julia> function f(a, b, c, d, g...) e = a + b + c + d f = does_not_exist(e) + e f end f (generic function with 1 method) julia> @code_llvm f(0,0,0,0,0) ``` ```llvm ; @ REPL[1]:1 within `f` define nonnull {}* @julia_f_141(i64 signext %"a::Int64", i64 signext %"b::Int64", i64 signext %"c::Int64", i64 signext %"d::Int64", i64 signext %"g[0]::Int64") #0 { top: %0 = alloca [2 x {}*], align 8 %gcframe3 = alloca [4 x {}*], align 16 %gcframe3.sub = getelementptr inbounds [4 x {}*], [4 x {}*]* %gcframe3, i64 0, i64 0 %1 = bitcast [4 x {}*]* %gcframe3 to i8* call void @llvm.memset.p0i8.i64(i8* align 16 %1, i8 0, i64 32, i1 true) %thread_ptr = call i8* asm "movq %fs:0, $0", "=r"() #7 %tls_ppgcstack = getelementptr i8, i8* %thread_ptr, i64 -8 %2 = bitcast i8* %tls_ppgcstack to {}**** %tls_pgcstack = load {}***, {}**** %2, align 8 ; @ REPL[1]:3 within `f` %3 = bitcast [4 x {}*]* %gcframe3 to i64* store i64 8, i64* %3, align 16 %4 = getelementptr inbounds [4 x {}*], [4 x {}*]* %gcframe3, i64 0, i64 1 %5 = bitcast {}** %4 to {}*** %6 = load {}**, {}*** %tls_pgcstack, align 8 store {}** %6, {}*** %5, align 8 %7 = bitcast {}*** %tls_pgcstack to {}*** store {}** %gcframe3.sub, {}*** %7, align 8 %Main.does_not_exist.cached = load atomic {}*, {}** @0 unordered, align 8 %iscached.not = icmp eq {}* %Main.does_not_exist.cached, null br i1 %iscached.not, label %notfound, label %found notfound: ; preds = %top %Main.does_not_exist.found = call {}* @ijl_get_binding_or_error({}* nonnull inttoptr (i64 139831437630272 to {}*), {}* nonnull inttoptr (i64 139831600565400 to {}*)) store atomic {}* %Main.does_not_exist.found, {}** @0 release, align 8 br label %found found: ; preds = %notfound, %top %Main.does_not_exist = phi {}* [ %Main.does_not_exist.cached, %top ], [ %Main.does_not_exist.found, %notfound ] %8 = bitcast {}* %Main.does_not_exist to {}** %does_not_exist.checked = load atomic {}*, {}** %8 unordered, align 8 %.not = icmp eq {}* %does_not_exist.checked, null br i1 %.not, label %err, label %ok err: ; preds = %found call void @ijl_undefined_var_error({}* inttoptr (i64 139831600565400 to {}*)) unreachable ok: ; preds = %found %.sub = getelementptr inbounds [2 x {}*], [2 x {}*]* %0, i64 0, i64 0 ; @ REPL[1]:2 within `f` ; ┌ @ operators.jl:587 within `+` @ int.jl:87 %9 = add i64 %"b::Int64", %"a::Int64" %10 = add i64 %9, %"c::Int64" ; │ @ operators.jl:587 within `+` ; │┌ @ operators.jl:544 within `afoldl` ; ││┌ @ int.jl:87 within `+` %11 = add i64 %10, %"d::Int64" %12 = getelementptr inbounds [4 x {}*], [4 x {}*]* %gcframe3, i64 0, i64 3 store {}* %does_not_exist.checked, {}** %12, align 8 ; └└└ ; @ REPL[1]:3 within `f` %13 = call nonnull {}* @ijl_box_int64(i64 signext %11) %14 = getelementptr inbounds [4 x {}*], [4 x {}*]* %gcframe3, i64 0, i64 2 store {}* %13, {}** %14, align 16 store {}* %13, {}** %.sub, align 8 %15 = call nonnull {}* @ijl_apply_generic({}* nonnull %does_not_exist.checked, {}** nonnull %.sub, i32 1) store {}* %15, {}** %12, align 8 %16 = call nonnull {}* @ijl_box_int64(i64 signext %11) store {}* %16, {}** %14, align 16 store {}* %15, {}** %.sub, align 8 %17 = getelementptr inbounds [2 x {}*], [2 x {}*]* %0, i64 0, i64 1 store {}* %16, {}** %17, align 8 %18 = call nonnull {}* @ijl_apply_generic({}* inttoptr (i64 139831370516384 to {}*), {}** nonnull %.sub, i32 2) %19 = load {}*, {}** %4, align 8 %20 = bitcast {}*** %tls_pgcstack to {}** store {}* %19, {}** %20, align 8 ; @ REPL[1]:4 within `f` ret {}* %18 } ``` </details> <details> <summary>Example 2</summary> ```julia julia> function g(a, b, c, d; kwarg=0) a + b + c + d + kwarg end g (generic function with 1 method) julia> @code_llvm g(0,0,0,0,kwarg=0) ``` ```llvm ; @ REPL[3]:1 within `g` define i64 @julia_g_160([1 x i64]* nocapture noundef nonnull readonly align 8 dereferenceable(8) %"#1::NamedTuple", i64 signext %"a::Int64", i64 signext %"b::Int64", i64 signext %"c::Int64", i64 signext %"d::Int64") #0 { top: %0 = getelementptr inbounds [1 x i64], [1 x i64]* %"#1::NamedTuple", i64 0, i64 0 ; ┌ @ REPL[3]:2 within `#g#1` ; │┌ @ operators.jl:587 within `+` @ int.jl:87 %1 = add i64 %"b::Int64", %"a::Int64" %2 = add i64 %1, %"c::Int64" ; ││ @ operators.jl:587 within `+` ; ││┌ @ operators.jl:544 within `afoldl` ; │││┌ @ int.jl:87 within `+` %3 = add i64 %2, %"d::Int64" ; │││└ ; │││ @ operators.jl:545 within `afoldl` ; │││┌ @ int.jl:87 within `+` %unbox = load i64, i64* %0, align 8 %4 = add i64 %3, %unbox ; └└└└ ret i64 %4 } ``` </details>
2 parents f15eb4e + 2984f9c commit 7735556

File tree

4 files changed

+92
-25
lines changed

4 files changed

+92
-25
lines changed

src/aotcompile.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2093,10 +2093,10 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, siz
20932093
// // Force imaging mode for names of pointers
20942094
// output.imaging = true;
20952095
// This would also be nice, but it seems to cause OOMs on the windows32 builder
2096-
// // Force at least medium debug info for introspection
2096+
// Force at least medium debug info for introspection
20972097
// No debug info = no variable names,
20982098
// max debug info = llvm.dbg.declare/value intrinsics which clutter IR output
2099-
output.debug_level = jl_options.debug_level;
2099+
output.debug_level = std::max(1, static_cast<int>(jl_options.debug_level));
21002100
auto decls = jl_emit_code(m, mi, src, jlrettype, output);
21012101
JL_UNLOCK(&jl_codegen_lock); // Might GC
21022102

src/codegen.cpp

Lines changed: 71 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1736,7 +1736,7 @@ jl_aliasinfo_t jl_aliasinfo_t::fromTBAA(jl_codectx_t &ctx, MDNode *tbaa) {
17361736
}
17371737

17381738
static Type *julia_type_to_llvm(jl_codectx_t &ctx, jl_value_t *jt, bool *isboxed = NULL);
1739-
static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value *fval, StringRef name, jl_value_t *sig, jl_value_t *jlrettype, bool is_opaque_closure, bool gcstack_arg);
1739+
static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value *fval, StringRef name, jl_value_t *sig, jl_value_t *jlrettype, bool is_opaque_closure, bool gcstack_arg, BitVector *used_arguments=nullptr, size_t *args_begin=nullptr);
17401740
static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval = -1);
17411741
static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t *s,
17421742
jl_binding_t **pbnd, bool assign);
@@ -2363,17 +2363,17 @@ std::unique_ptr<Module> jl_create_llvm_module(StringRef name, LLVMContext &conte
23632363

23642364
static void jl_name_jlfunc_args(jl_codegen_params_t &params, Function *F) {
23652365
assert(F->arg_size() == 3);
2366-
F->getArg(0)->setName("function");
2367-
F->getArg(1)->setName("args");
2368-
F->getArg(2)->setName("nargs");
2366+
F->getArg(0)->setName("function::Core.Function");
2367+
F->getArg(1)->setName("args::Any[]");
2368+
F->getArg(2)->setName("nargs::UInt32");
23692369
}
23702370

23712371
static void jl_name_jlfuncparams_args(jl_codegen_params_t &params, Function *F) {
23722372
assert(F->arg_size() == 4);
2373-
F->getArg(0)->setName("function");
2374-
F->getArg(1)->setName("args");
2375-
F->getArg(2)->setName("nargs");
2376-
F->getArg(3)->setName("sparams");
2373+
F->getArg(0)->setName("function::Core.Function");
2374+
F->getArg(1)->setName("args::Any[]");
2375+
F->getArg(2)->setName("nargs::UInt32");
2376+
F->getArg(3)->setName("sparams::Any");
23772377
}
23782378

23792379
static void jl_init_function(Function *F, const Triple &TT)
@@ -4359,7 +4359,7 @@ static jl_cgval_t emit_call_specfun_boxed(jl_codectx_t &ctx, jl_value_t *jlretty
43594359
}
43604360
jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const);
43614361
theFptr = ai.decorateInst(ctx.builder.CreateAlignedLoad(pfunc, GV, Align(sizeof(void*))));
4362-
setName(ctx.emission_context, theFptr, namep);
4362+
setName(ctx.emission_context, theFptr, specFunctionObject);
43634363
}
43644364
else {
43654365
theFptr = jl_Module->getOrInsertFunction(specFunctionObject, ctx.types().T_jlfunc).getCallee();
@@ -6935,10 +6935,11 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret
69356935
return w;
69366936
}
69376937

6938-
static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value *fval, StringRef name, jl_value_t *sig, jl_value_t *jlrettype, bool is_opaque_closure, bool gcstack_arg)
6938+
static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value *fval, StringRef name, jl_value_t *sig, jl_value_t *jlrettype, bool is_opaque_closure, bool gcstack_arg, BitVector *used_arguments, size_t *arg_offset)
69396939
{
69406940
jl_returninfo_t props = {};
69416941
SmallVector<Type*, 8> fsig;
6942+
SmallVector<std::string, 4> argnames;
69426943
Type *rt = NULL;
69436944
Type *srt = NULL;
69446945
if (jlrettype == (jl_value_t*)jl_bottom_type) {
@@ -6956,6 +6957,7 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value
69566957
props.cc = jl_returninfo_t::Union;
69576958
Type *AT = ArrayType::get(getInt8Ty(ctx.builder.getContext()), props.union_bytes);
69586959
fsig.push_back(AT->getPointerTo());
6960+
argnames.push_back("union_bytes_return");
69596961
Type *pair[] = { ctx.types().T_prjlvalue, getInt8Ty(ctx.builder.getContext()) };
69606962
rt = StructType::get(ctx.builder.getContext(), makeArrayRef(pair));
69616963
}
@@ -6980,6 +6982,7 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value
69806982
// sret is always passed from alloca
69816983
assert(M);
69826984
fsig.push_back(rt->getPointerTo(M->getDataLayout().getAllocaAddrSpace()));
6985+
argnames.push_back("sret_return");
69836986
srt = rt;
69846987
rt = getVoidTy(ctx.builder.getContext());
69856988
}
@@ -7018,6 +7021,7 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value
70187021
param.addAttribute(Attribute::NoUndef);
70197022
attrs.push_back(AttributeSet::get(ctx.builder.getContext(), param));
70207023
fsig.push_back(get_returnroots_type(ctx, props.return_roots)->getPointerTo(0));
7024+
argnames.push_back("return_roots");
70217025
}
70227026

70237027
if (gcstack_arg){
@@ -7026,9 +7030,16 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value
70267030
param.addAttribute(Attribute::NonNull);
70277031
attrs.push_back(AttributeSet::get(ctx.builder.getContext(), param));
70287032
fsig.push_back(PointerType::get(JuliaType::get_ppjlvalue_ty(ctx.builder.getContext()), 0));
7033+
argnames.push_back("pgcstack_arg");
70297034
}
70307035

7031-
for (size_t i = 0; i < jl_nparams(sig); i++) {
7036+
if (arg_offset)
7037+
*arg_offset = fsig.size();
7038+
size_t nparams = jl_nparams(sig);
7039+
if (used_arguments)
7040+
used_arguments->resize(nparams);
7041+
7042+
for (size_t i = 0; i < nparams; i++) {
70327043
jl_value_t *jt = jl_tparam(sig, i);
70337044
bool isboxed = false;
70347045
Type *ty = NULL;
@@ -7060,6 +7071,8 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value
70607071
}
70617072
attrs.push_back(AttributeSet::get(ctx.builder.getContext(), param));
70627073
fsig.push_back(ty);
7074+
if (used_arguments)
7075+
used_arguments->set(i);
70637076
}
70647077

70657078
AttributeSet FnAttrs;
@@ -7089,8 +7102,14 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value
70897102
else
70907103
fval = emit_bitcast(ctx, fval, ftype->getPointerTo());
70917104
}
7092-
if (gcstack_arg && isa<Function>(fval))
7093-
cast<Function>(fval)->setCallingConv(CallingConv::Swift);
7105+
if (auto F = dyn_cast<Function>(fval)) {
7106+
if (gcstack_arg)
7107+
F->setCallingConv(CallingConv::Swift);
7108+
assert(F->arg_size() >= argnames.size());
7109+
for (size_t i = 0; i < argnames.size(); i++) {
7110+
F->getArg(i)->setName(argnames[i]);
7111+
}
7112+
}
70947113
props.decl = FunctionCallee(ftype, fval);
70957114
props.attrs = attributes;
70967115
return props;
@@ -7316,11 +7335,49 @@ static jl_llvm_functions_t
73167335
Function *f = NULL;
73177336
bool has_sret = false;
73187337
if (specsig) { // assumes !va and !needsparams
7338+
BitVector used_args;
7339+
size_t args_begin;
73197340
returninfo = get_specsig_function(ctx, M, NULL, declarations.specFunctionObject, lam->specTypes,
7320-
jlrettype, ctx.is_opaque_closure, JL_FEAT_TEST(ctx,gcstack_arg));
7341+
jlrettype, ctx.is_opaque_closure, JL_FEAT_TEST(ctx,gcstack_arg), &used_args, &args_begin);
73217342
f = cast<Function>(returninfo.decl.getCallee());
73227343
has_sret = (returninfo.cc == jl_returninfo_t::SRet || returninfo.cc == jl_returninfo_t::Union);
73237344
jl_init_function(f, ctx.emission_context.TargetTriple);
7345+
if (ctx.emission_context.debug_level > 0) {
7346+
auto arg_typename = [&](size_t i) JL_NOTSAFEPOINT {
7347+
auto tp = jl_tparam(lam->specTypes, i);
7348+
return jl_is_datatype(tp) ? jl_symbol_name(((jl_datatype_t*)tp)->name->name) : "<unknown type>";
7349+
};
7350+
size_t nreal = 0;
7351+
for (size_t i = 0; i < std::min(nreq, static_cast<size_t>(used_args.size())); i++) {
7352+
jl_sym_t *argname = slot_symbol(ctx, i);
7353+
if (argname == jl_unused_sym)
7354+
continue;
7355+
if (used_args.test(i)) {
7356+
auto &arg = *f->getArg(args_begin++);
7357+
nreal++;
7358+
auto name = jl_symbol_name(argname);
7359+
if (!name[0]) {
7360+
arg.setName(StringRef("#") + Twine(nreal) + StringRef("::") + arg_typename(i));
7361+
} else {
7362+
arg.setName(name + StringRef("::") + arg_typename(i));
7363+
}
7364+
}
7365+
}
7366+
if (va && ctx.vaSlot != -1) {
7367+
size_t vidx = 0;
7368+
for (size_t i = nreq; i < used_args.size(); i++) {
7369+
if (used_args.test(i)) {
7370+
auto &arg = *f->getArg(args_begin++);
7371+
auto type = arg_typename(i);
7372+
const char *name = jl_symbol_name(slot_symbol(ctx, ctx.vaSlot));
7373+
if (!name[0])
7374+
name = "...";
7375+
vidx++;
7376+
arg.setName(name + StringRef("[") + Twine(vidx) + StringRef("]::") + type);
7377+
}
7378+
}
7379+
}
7380+
}
73247381

73257382
// common pattern: see if all return statements are an argument in that
73267383
// case the apply-generic call can re-use the original box for the return

test/compiler/codegen.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ if opt_level > 0
253253
load_dummy_ref_ir = get_llvm(load_dummy_ref, Tuple{Int})
254254
@test !occursin("jl_gc_pool_alloc", load_dummy_ref_ir)
255255
# Hopefully this is reliable enough. LLVM should be able to optimize this to a direct return.
256-
@test occursin("ret $Iptr %0", load_dummy_ref_ir)
256+
@test occursin("ret $Iptr %\"x::$(Int)\"", load_dummy_ref_ir)
257257
end
258258

259259
# Issue 22770

test/llvmpasses/fastmath.jl

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
# RUN: julia --startup-file=no %s %t && llvm-link -S %t/* -o %t/module.ll
3+
# RUN: julia --startup-file=no %s %t -O && llvm-link -S %t/* -o %t/module.ll
44
# RUN: cat %t/module.ll | FileCheck %s
55

66
## Notes:
@@ -14,21 +14,31 @@ include(joinpath("..", "testhelpers", "llvmpasses.jl"))
1414

1515
import Base.FastMath
1616

17-
# CHECK: call fast float @llvm.sqrt.f32(float %{{[0-9]+}})
17+
# CHECK: call fast float @llvm.sqrt.f32(float %"x::Float32")
1818
emit(FastMath.sqrt_fast, Float32)
1919

2020

2121
# Float16 operations should be performed as Float32, unless @fastmath is specified
2222
# TODO: this is not true for platforms that natively support Float16
2323

2424
foo(x::T,y::T) where T = x-y == zero(T)
25-
# LOWER: fsub half %0, %1
26-
# FINAL: %2 = fpext half %0 to float
27-
# FINAL: %3 = fpext half %1 to float
28-
# FINAL: fsub half %2, %3
25+
# CHECK: define {{(swiftcc )?}}i8 @julia_foo_{{[0-9]+}}({{.*}}half %[[X:"x::Float16"]], half %[[Y:"y::Float16"]]) {{.*}}{
26+
# CHECK-DAG: %[[XEXT:[0-9]+]] = fpext half %[[X]] to float
27+
# CHECK-DAG: %[[YEXT:[0-9]+]] = fpext half %[[Y]] to float
28+
# CHECK: %[[DIFF:[0-9]+]] = fsub float %[[XEXT]], %[[YEXT]]
29+
# CHECK: %[[TRUNC:[0-9]+]] = fptrunc float %[[DIFF]] to half
30+
# CHECK: %[[DIFFEXT:[0-9]+]] = fpext half %[[TRUNC]] to float
31+
# CHECK: %[[CMP:[0-9]+]] = fcmp oeq float %[[DIFFEXT]], 0.000000e+00
32+
# CHECK: %[[ZEXT:[0-9]+]] = zext i1 %[[CMP]] to i8
33+
# CHECK: ret i8 %[[ZEXT]]
34+
# CHECK: }
2935
emit(foo, Float16, Float16)
3036

3137
@fastmath foo(x::T,y::T) where T = x-y == zero(T)
32-
# LOWER: fsub fast half %0, %1
33-
# FINAL: fsub fast half %0, %1
38+
# CHECK: define {{(swiftcc )?}}i8 @julia_foo_{{[0-9]+}}({{.*}}half %[[X:"x::Float16"]], half %[[Y:"y::Float16"]]) {{.*}}{
39+
# CHECK: %[[DIFF:[0-9]+]] = fsub fast half %[[X]], %[[Y]]
40+
# CHECK: %[[CMP:[0-9]+]] = fcmp fast oeq half %[[DIFF]], 0xH0000
41+
# CHECK: %[[ZEXT:[0-9]+]] = zext i1 %[[CMP]] to i8
42+
# CHECK: ret i8 %[[ZEXT]]
43+
# CHECK: }
3444
emit(foo, Float16, Float16)

0 commit comments

Comments
 (0)