# What is the difference between CodeInfo and Core.Compiler.IRCode?

**URL:** https://discourse.julialang.org/t/what-is-the-difference-between-codeinfo-and-core-compiler-ircode/106089
**Category:** Internals & Design
**Created:** [November 11, 2023, 7:52pm UTC](https://discourse.julialang.org/t/what-is-the-difference-between-codeinfo-and-core-compiler-ircode/106089 "2023-11-11T19:52:34Z")
**Posts on this page:** 9
**Page:** 1

<div class="post-metadata">

### Author: ![Tomas\_Pevny](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomas_pevny/32/25466_2.png) [@Tomas\_Pevny](https://discourse.julialang.org/u/Tomas_Pevny)
#### Post date: [November 11, 2023, 7:52pm UTC](https://discourse.julialang.org/t/what-is-the-difference-between-codeinfo-and-core-compiler-ircode/106089/1 "2023-11-11T19:52:34Z")

</div>

Dear All,

I wanted to update my lecture notes about IR modifications. Two years ago, when I have written them, I wanted to have some introductory material into mechanisms Zygote relies on. Now, I have the feeling that its time to expand the notes with `CompilerPlugins`, since this seems to be the future.

I have therefore started to look around which provokes some questions.

My first question is, what is the difference between `Core.CodeInfo` and `Core.Compiler.IRCode`?

Second question. It used to be the case that generated function could return `CodeInfo` instead of julia code. Can generated function return `IRCode`, or there is a different mechanism how to hook to the compiler (I think yes there is and I expect Diffractor might contain an example).

Third question. `IRTools.jl` are great for modifying `CodeInfo`. I have found two tools for modifying `IRCode` [CompilerPluginTools](https://github.com/JuliaCompilerPlugins/CompilerPluginTools.jl) and [CodeInfoTools.jl](https://github.com/JuliaCompilerPlugins/CodeInfoTools.jl). I wonder if they have been abandoned because they were both dead ends or because of lack of human labor. I am also aware of TKF’s [CodeInfo.jl](https://github.com/tkf/ShowCode.jl), but I do not think it is designed for manipulation.

I do not have at the moment big aspirations, but I believe making something like [Petite Zygote](https://github.com/FluxML/IRTools.jl/blob/master/examples/reverse.jl) with `IRCode` might be a useful educational material.

---

<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: [November 12, 2023, 7:44pm UTC](https://discourse.julialang.org/t/what-is-the-difference-between-codeinfo-and-core-compiler-ircode/106089/2 "2023-11-12T19:44:11Z")

</div>

Once upon a time Julia only had `CodeInfo`, which is produced by lowering and then updated/modified by abstract interpretation (nee inference). Then codegen takes it and translates it to LLVM IR.

When the iteration protocol got changed it became clear that we needed a layer upon which we can do optimizations a bit easier. I think everything in `base/compiler/ssair` operates over `IRCode`.

The translation routines are here: [https://github.com/JuliaLang/julia/blob/master/base/compiler/ssair/legacy.jl](https://github.com/JuliaLang/julia/blob/master/base/compiler/ssair/legacy.jl)

Besides some internal data-structure differences `IRCode` uses block indices for jump targets and PhiNodes over the statement indices in CodeInfo.

This makes working with the control-flow-graph a bit easier.

Now codegen still uses the old format so currently we translate from CodeInfo → IRCode, perform optimizations (sroa, inlining, simplification, and a few more a being worked on), and then translate back into a hybrid CodeInfo form to do codegen.

> It used to be the case that generated function could return `CodeInfo` instead of julia code.

You can still do this. Note that `@generated` functions have the requirement of returning un-inferred IR.

> there is a different mechanism how to hook to the compiler

If you have an inferred CodeInfo or IRCode you can use `Core.OpaqueClosure` to construct a call-able object.

IRTools has the issue that it goes to yet another different internal IR. This is why we never used it in Cassette.jl but rather worked with the tools provided by Core.Compiler directly.

A good example recently is [https://github.com/JuliaLang/julia/pull/51120](https://github.com/JuliaLang/julia/pull/51120) which adds a global-value-numbering pass to Julia or my own re-invigorated experiments to write a loop-invariant-code-motion pass for Julia ([GitHub - vchuravy/Loops.jl](https://github.com/vchuravy/Loops.jl) & [Manual LICM in Julia](https://vchuravy.dev/talks/licm/))

It would be interesting to move ShowCode.jl to an organization and make it work over the core base datastructures.

While I understand (and do it myself often enough) for tools to work with Julia IR to live in external repositories, I would encourage contributions to Julia Core.Compiler so that we can improve the common tooling and have less risk of things bit-rotting.

---

<div class="post-metadata">

### Author: ![ToucheSir](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/touchesir/32/14411_2.png) [@ToucheSir](https://discourse.julialang.org/u/ToucheSir)
#### Post date: [November 12, 2023, 8:31pm UTC](https://discourse.julialang.org/t/what-is-the-difference-between-codeinfo-and-core-compiler-ircode/106089/3 "2023-11-12T20:31:34Z")

</div>

> [@Tomas\_Pevny](#):
>
> I have found two tools for modifying `IRCode` [CompilerPluginTools](https://github.com/JuliaCompilerPlugins/CompilerPluginTools.jl) and [CodeInfoTools.jl](https://github.com/JuliaCompilerPlugins/CodeInfoTools.jl). I wonder if they have been abandoned because they were both dead ends or because of lack of human labor.

More the latter than the former, I think. But certainly the way `Core.Compiler` is structured and developed does not make these kinds of libraries easy to maintain. @Roger-luo probably has an opinion on this.

> [@vchuravy](#):
>
> While I understand (and do it myself often enough) for tools to work with Julia IR to live in external repositories, I would encourage contributions to Julia Core.Compiler so that we can improve the common tooling and have less risk of things bit-rotting.

Would it be feasible to define a somewhat stable subset of the Core.Compiler API and create a Compat.jl style polyfill library which allows users to target it across different Julia versions? At present, I think having to ride the nightly train to do any kind of non-trivial IRCode manipulation limits the appetite for doing compiler stuff outside of JuliaLang/julia (and by extension the number of people who would be comfortable helping with Julia-side compiler stuff in Core). IMO it’s also one reason IRTools still exists at all: despite its many flaws, one gets a stable and version-agnostic interface to work with.

---

<div class="post-metadata">

### Author: ![Roger-luo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/roger-luo/32/3399_2.png) [@Roger-luo](https://discourse.julialang.org/u/Roger-luo)
#### Post date: [November 13, 2023, 12:24am UTC](https://discourse.julialang.org/t/what-is-the-difference-between-codeinfo-and-core-compiler-ircode/106089/4 "2023-11-13T00:24:40Z")

</div>

Yeah, I later decided not to depend on Julia internals but just write my own IR, so that I don’t need that package anymore, which is why I’m not updating it. Keeping it up to date with internals is also a lot of work for me.

---

<div class="post-metadata">

### Author: ![Tomas\_Pevny](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomas_pevny/32/25466_2.png) [@Tomas\_Pevny](https://discourse.julialang.org/u/Tomas_Pevny)
#### Post date: [November 13, 2023, 7:03am UTC](https://discourse.julialang.org/t/what-is-the-difference-between-codeinfo-and-core-compiler-ircode/106089/5 "2023-11-13T07:03:58Z")

</div>

Big thanks for clarification to all. This is really useful. I will keep poking around.

---

<div class="post-metadata">

### Author: ![Tomas\_Pevny](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomas_pevny/32/25466_2.png) [@Tomas\_Pevny](https://discourse.julialang.org/u/Tomas_Pevny)
#### Post date: [November 13, 2023, 1:46pm UTC](https://discourse.julialang.org/t/what-is-the-difference-between-codeinfo-and-core-compiler-ircode/106089/6 "2023-11-13T13:46:16Z")

</div>

One more question.

Is there a function which will reassign SSAValue? I have modified IRCode (again through a silly example) and obtain something like this

```julia
2 1 ─ %1 = (_2 * _3)::Float64 │
3 │ %5 = Main.cos(_3)::Float64 │
  │ %2 = Main.sin(_2)::Float64
  │ %3 = (%1 + %5)::Float64 │
  │ %6 = (%4 + %2)::Float64 │
  │ %4 = return %6

```

which has the problem that SSA are not ordered. The IRCode now contains two elements in `ir.new_nodes`. I would like to have a function, which now “retires” the `new_nodes` to `stmts` and assign correct numbers. Do I have to write it by myself, or it is readily available?

Thanks a lot in advance for help.  
Best wishes,  
Tomas

---

<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: [November 13, 2023, 2:10pm UTC](https://discourse.julialang.org/t/what-is-the-difference-between-codeinfo-and-core-compiler-ircode/106089/7 "2023-11-13T14:10:37Z")

</div>

You want to run `compact!` over the IR.

---

<div class="post-metadata">

### Author: ![Tomas\_Pevny](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomas_pevny/32/25466_2.png) [@Tomas\_Pevny](https://discourse.julialang.org/u/Tomas_Pevny)
#### Post date: [November 13, 2023, 4:34pm UTC](https://discourse.julialang.org/t/what-is-the-difference-between-codeinfo-and-core-compiler-ircode/106089/8 "2023-11-13T16:34:16Z")

</div>

Thanks for the swift answer. I have tried that and i have not seen the effect. I will try again tonight and post mwe in case of success or failure.

---

<div class="post-metadata">

### Author: ![Tomas\_Pevny](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomas_pevny/32/25466_2.png) [@Tomas\_Pevny](https://discourse.julialang.org/u/Tomas_Pevny)
#### Post date: [November 13, 2023, 8:07pm UTC](https://discourse.julialang.org/t/what-is-the-difference-between-codeinfo-and-core-compiler-ircode/106089/9 "2023-11-13T20:07:47Z")

</div>

Below is an MWE that tries to modify the IRCode of a function and execute it. The goal is to change the function `foo` to `fooled`.  
By manipulating IRCode.

```julia
import Core.Compiler as CC
using Core: SSAValue, GlobalRef, ReturnNode

function foo(x,y) 
  z = x * y 
  z + sin(x)
end

function fooled(x,y) 
  z = x * y 
  z + sin(x) + cos(y)
end

(ir, rt) = only(Base.code_ircode(foo, (Float64, Float64), optimize_until = "compact 1"));
nr = CC.insert_node!(ir, 2, CC.NewInstruction(Expr(:call, Core.GlobalRef(Main, :cos), Core.Argument(3)), Float64))
nr2 = CC.insert_node!(ir, 4, CC.NewInstruction(Expr(:call, GlobalRef(Main, :+), SSAValue(3), nr), Float64))
CC.setindex!(ir.stmts[4], ReturnNode(nr2), :inst)
ir = CC.compact!(ir)
irfooled = Core.OpaqueClosure(ir)
irfooled(1.0, 2.0) == fooled(1.0, 2.0)

```

So what we did?

1. `(ir, rt) = only(Base.code_ircode(foo, (Float64, Float64), optimize_until = "compact 1"))` obtain the `IRCode` of the function `foo` when called with both arguments being `Float64`. `rt` contains the return type of the
2. A new instruction `cos` is inserted to the `ir` by `Core.Compiler.insert_node!`, which takes as an argument an `IRCode`, position (2 in our case), and new instruction. The new instruction is created by `NewInstruction` accepting as an input expression `Expr` and a return type. Here, we force it to be `Float64`, but ideally it should be inferred. (This would be the next stage). Or, may-be, we can run it through type inference? . The new instruction is added to the `ir.new_nodes` instruction stream and obtain a new SSAValue returned in `nr`, which can be then used further.
3. We add one more instruction `+` that uses output of the instruction we add in step 2, `nr` and SSAValue from statement 3 of the original IR (at this moment, the IR is still numbered with respect to the old IR, the renumbering will happen later.) The output of this second instruction is returned in `nr2`.
4. Then, we rewrite the return statement to return `nr2` instead of `SSAValue(3)`.
5. `ir = CC.compact!(ir)` is superimportant since it moves the newly added statements from `ir.new_stmts` to `ir.stmts` and importantly renumbers `SSAValues.` _Even though the function is mutating, the mutation here is meant that the argument is changed, but the new correct IRCode is returned and therefore has to be reassigned._
6. The function is created through `OpaqueClosure.`
7. The last line certifies that the function do what it should do.

I have mark this as an answer, but of course many interesting question remains unanswered. For a person without a proper compiler class (but who on highschool did some x86 assembly), this is very interesting avenue.
