# Inferring MethodInstance from code\_llvm

**URL:** https://discourse.julialang.org/t/inferring-methodinstance-from-code-llvm/110330
**Category:** Internals & Design
**Tags:** llvm
**Created:** [February 17, 2024, 1:40am UTC](https://discourse.julialang.org/t/inferring-methodinstance-from-code-llvm/110330 "2024-02-17T01:40:18Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![dyatunin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dyatunin/32/45450_2.png) [@dyatunin](https://discourse.julialang.org/u/dyatunin)
#### Post date: [February 17, 2024, 1:40am UTC](https://discourse.julialang.org/t/inferring-methodinstance-from-code-llvm/110330/1 "2024-02-17T01:40:18Z")

</div>

I am trying to track down some performance issues by tracing through the output of `code_llvm`. Whenever the LLVM code contains a call to a Julia function, I would like to figure out which `MethodInstance` of that function is getting called, and then recursively analyze the output of `code_llvm` for that `MethodInstance`. However, I am unable to determine the `MethodInstance` in cases where different modules have functions with the same name, and my current strategy is unreliable for functions with multiple methods.

Here are some examples of relevant LLVM code:

```julia
call double @"j_^_18287"(double %0, i64 signext %10)
call i64 @j_mapreduce_impl_18993({}* nonnull %0, i64 signext 1, i64 signext %arraylen, i64 signext 1024)
call i64 @julia_mapreduce_impl_19022({}* %0, i64 signext %1, i64 signext %34, i64 signext %3)
call nonnull {}* @j1_print_to_string_14966({}* inttoptr (i64 5467587216 to {}*), {}** nonnull %.sub, i32 4)

```

Through some exploration, I’ve found that the LLVM name of a Julia function starts with `@julia_` when the function is called recursively, `@"j_` when the function name has non-standard characters, and `@j_` or `@j1_` otherwise. I have not been able to figure out what the string of numbers at the end of each function name corresponds to, though.

Does anyone know what this string of numbers is? Is it a unique identifier for each compiled `MethodInstance`, and can I obtain this identifier without delving into the underlying C++ code? If not, is there some other information in the `code_llvm` output that I could use to infer which `MethodInstance` of a function is getting called?

---

<div class="post-metadata">

### Author: ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)
#### Post date: [February 17, 2024, 7:24am UTC](https://discourse.julialang.org/t/inferring-methodinstance-from-code-llvm/110330/2 "2024-02-17T07:24:06Z")

</div>

Is there a particular reason why `@code_warntype` or other variants are unsuitable for the analysis?

There are also tools like `@which` to find the concrete method used and then Cthulhu.jl or JET.jl to get deeper insights.

---

<div class="post-metadata">

### Author: ![dyatunin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dyatunin/32/45450_2.png) [@dyatunin](https://discourse.julialang.org/u/dyatunin)
#### Post date: [February 17, 2024, 8:14am UTC](https://discourse.julialang.org/t/inferring-methodinstance-from-code-llvm/110330/3 "2024-02-17T08:14:01Z")

</div>

We’ve already run our code through JET, Aqua, Profile, and SnoopCompile. We’ve used Cthulhu to remove most type instabilities, and type inference is no longer a significant performance issue for us. I’m looking at `@code_llvm`, rather than `@code_warntype`, because I am trying to solve problems that depend on the rest of the compilation process:

- Some of our operations have very long code generation times (which show up as empty gaps in SnoopCompile flame graphs), and a few edge cases can cause out-of-memory issues during compilation on GPUs. I would like to understand what is going on at the level of LLVM code generation, if this is possible.
- We are interested in improving performance on GPUs by introducing loop fusion and shared memory caching to small groups of operations. I would like to automate the process of deciding which operations to fuse and where to insert cache synchronization pauses. If possible, I want to compute rough estimates of quantities like the register pressure and code generation time for a fused kernel by analyzing the LLVM code for its individual sub-kernels.

---

<div class="post-metadata">

### Author: ![vchuravy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vchuravy/32/8_2.png) [@vchuravy](https://discourse.julialang.org/u/vchuravy)
#### Post date: [February 17, 2024, 10:12pm UTC](https://discourse.julialang.org/t/inferring-methodinstance-from-code-llvm/110330/4 "2024-02-17T22:12:15Z")

</div>

On my phone so unable to give a full explanation, but the prefix of the function is related to it’s calling convention (e.g. are values passed boxed or unboxed, modulo some placeholder names for codegen).

The suffix is a global counter to disambiguate each llvm function. (Each function has many methods, each methods can have many instantiation s, each instantiation can be compiled multiple times).

If you see an raw integer embedded in the IR that is converted with `inttoptr` it more often than not is a global Julia object that is valid during the current session (so you can do unsafe\_object\_from\_ptr) on it.

---

<div class="post-metadata">

### Author: ![dyatunin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dyatunin/32/45450_2.png) [@dyatunin](https://discourse.julialang.org/u/dyatunin)
#### Post date: [February 20, 2024, 2:02am UTC](https://discourse.julialang.org/t/inferring-methodinstance-from-code-llvm/110330/5 "2024-02-20T02:02:03Z")

</div>

Looks like I can use pointers in the output of `@code_llvm` to get either the `MethodInstance` or the `Function` being called in certain cases. For example, I can use `unsafe_pointer_to_objref(Ptr{Nothing}(<integer>))` on the integers embedded in the following lines:

```julia
call nonnull {}* @j1_print_to_string_14966({}* inttoptr (i64 5467587216 to {}*), {}** nonnull %.sub, i32 4)
call nonnull {}* @ijl_apply_generic({}* inttoptr (i64 4761373168 to {}*), {}** nonnull %.sub, i32 2)
call nonnull {}* @ijl_invoke({}* inttoptr (i64 4751558928 to {}*), {}** nonnull %3, i32 1, {}* inttoptr (i64 4464873632 to {}*))

```

Dereferencing the first argument in each of these cases gives the `Function` being called/applied/invoked:

```julia
print_to_string (generic function with 1 method)
println (generic function with 3 methods)
__throw_rational_argerror_zero (generic function with 1 method)

```

In the last case, I can also dereference the final argument to get a specific `MethodInstance`:

```julia
MethodInstance for Base.__throw_rational_argerror_zero(::Type)

```

Although getting the `MethodInstance` for every call would make things a lot easier, getting the `Function` on its own should be enough for what I’m trying to do. This is because the output of `@code_llvm` has debug information that I can use to reconstruct the stacktrace of any function call. During the analysis of my program (which I’m doing with Cassette.jl), I can check the stacktrace of each call to determine whether I need to step into it or whether it was inlined into previously analyzed LLVM code. There are probably some edge cases where this won’t work (like if a function calls two different methods of another function, one of which gets inlined while the other doesn’t), but it should be enough to get started.

Is there any chance I can get pointers to all the other Julia functions being called from the LLVM code? Specifically, is it possible to get more information about calls that look like this:

```julia
call double @"j_^_18287"(double %0, i64 signext %10)
call void @"j_#84_13401"([4 x double]* noalias nocapture noundef nonnull sret([4 x double]) %1)
call i64 @j_mapreduce_impl_18993({}* nonnull %0, i64 signext 1, i64 signext %arraylen, i64 signext 1024)
call i64 @julia_mapreduce_impl_19022({}* %0, i64 signext %1, i64 signext %34, i64 signext %3)

```

If there aren’t raw pointers to the relevant functions floating around in the output of `@code_llvm`, I could also look at `@code_native` or somewhere else.

---

<div class="post-metadata">

### Author: ![vchuravy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vchuravy/32/8_2.png) [@vchuravy](https://discourse.julialang.org/u/vchuravy)
#### Post date: [February 20, 2024, 3:51am UTC](https://discourse.julialang.org/t/inferring-methodinstance-from-code-llvm/110330/6 "2024-02-20T03:51:38Z")

</div>

> [@dyatunin](#):
>
> Is there any chance I can get pointers to all the other Julia functions being called from the LLVM code? Specifically, is it possible to get more information about calls that look like this:

Those are fully devirtualized native calls. You need to look at code\_lowered for those (like Cthulhu does)

Inlining only happens on the Julia IR level so pre-codegen is probably the time you want to inspect the IR
