Displaying outputs of @code_lowered from two methods side by side

I am trying to compare the output of @code_lowered from two methods of a function. It struck me that displaying them side by side would make it easier to compare them line-by-line. However something like this doesn’t seem to work:

julia> a = @code_lowered sin(1)
CodeInfo(
1 ─ %1 = Base.Math.float(x)
│   %2 = Base.Math.sin(%1)
└──      return %2
)

julia> hcat(a, a)
1×2 Array{Core.CodeInfo,2}:
 CodeInfo(
1 ─ %1 = Base.Math.float(x)
│   %2 = Base.Math.sin(%1)
└──      return %2
)  …  CodeInfo(
1 ─ %1 = Base.Math.float(x)
│   %2 = Base.Math.sin(%1)
└──      return %2
)

I wonder if there’s a way to get them to display next to each other?

It appears that working with a.code is one way to proceed:

julia> a = @code_lowered sin(1);

julia> [a.code a.code]
3×2 Array{Expr,2}:
 :(Base.Math.float(_2))  :(Base.Math.float(_2))
 :(Base.Math.sin(%1))    :(Base.Math.sin(%1))
 :(return %2)            :(return %2)

That won’t work if the functions have different lengths:

julia> a = @code_lowered sin(1)
CodeInfo(
1 ─ %1 = Base.Math.float(x)
│   %2 = Base.Math.sin(%1)
└──      return %2
)

julia> b = @code_lowered 1*2*5*4*6 + 3*4*2*3
CodeInfo(
1 ─ %1 = Base.add_int(x, y)
└──      return %1
)

julia> [a.code b.code]
ERROR: DimensionMismatch("vectors must have same lengths")
Stacktrace:
 [1] hcat(::Array{Any,1}, ::Array{Any,1}) at ./array.jl:1665
 [2] top-level scope at REPL[5]:1

Printing differing texts side by side is, in general, a very hard problem - you might be interested in the diff program or various layouting algorithms.

Here is one way to do it using bash and diff:

julia> a = @code_lowered sin(1.);

julia> b = @code_lowered cos(1.);

shell> bash -c "diff --color <(echo '$a') <(echo '$b') --side-by-side"
CodeInfo(                                                       CodeInfo(
1 ──       Core.NewvarNode(:(@_4))                              1 ──       Core.NewvarNode(:(@_4))
│          Core.NewvarNode(:(y))                                │          Core.NewvarNode(:(y))
│          Core.NewvarNode(:(n))                                │          Core.NewvarNode(:(n))
│          absx = Base.Math.abs(x)                              │          absx = Base.Math.abs(x)
│    %5  = absx                                                 │    %5  = absx
│    %6  = ($(Expr(:static_parameter, 1)))(Base.Math.pi)        │    %6  = ($(Expr(:static_parameter, 1)))(Base.Math.pi)
│    %7  = %6 / 4                                               │    %7  = %6 / 4
│    %8  = %5 < %7                                              │    %8  = %5 < %7
└───       goto #5 if not %8                                    └───       goto #5 if not %8
2 ── %10 = absx                                                 2 ── %10 = absx
│    %11 = Base.Math.eps($(Expr(:static_parameter, 1)))         │    %11 = Base.Math.eps($(Expr(:static_parameter, 1)))
│    %12 = Base.Math.sqrt(%11)                                | │    %12 = ($(Expr(:static_parameter, 1)))(2.0)
│    %13 = %10 < %12                                          | │    %13 = %11 / %12
└───       goto #4 if not %13                                 | │    %14 = Base.Math.sqrt(%13)
3 ──       return x                                           | │    %15 = %10 < %14
4 ── %16 = Base.Math.sin_kernel(x)                            | └───       goto #4 if not %15
└───       return %16                                         | 3 ── %17 = ($(Expr(:static_parameter, 1)))(1.0)
5 ── %18 = Base.Math.isnan(x)                                 | └───       return %17
└───       goto #7 if not %18                                 | 4 ── %19 = Base.Math.cos_kernel(x)
6 ── %20 = ($(Expr(:static_parameter, 1)))(Base.Math.NaN)     | └───       return %19
└───       return %20                                         | 5 ── %21 = Base.Math.isnan(x)
7 ── %22 = Base.Math.isinf(x)                                 | └───       goto #7 if not %21
└───       goto #9 if not %22                                 | 6 ── %23 = ($(Expr(:static_parameter, 1)))(Base.Math.NaN)
8 ──       Base.Math.sin_domain_error(x)                      | └───       return %23
9 ┄─ %25 = Base.Math.rem_pio2_kernel(x)                       | 7 ── %25 = Base.Math.isinf(x)
│    %26 = Base.indexed_iterate(%25, 1)                       | └───       goto #9 if not %25
│          n = Core.getfield(%26, 1)                          | 8 ── %27 = Base.Math.cos_domain_error(x)
│          @_4 = Core.getfield(%26, 2)                        | └───       return %27
│    %29 = Base.indexed_iterate(%25, 2, @_4)                  | 9 ── %29 = Base.Math.rem_pio2_kernel(x)
│          y = Core.getfield(%29, 1)                          | │    %30 = Base.indexed_iterate(%29, 1)
                                                              > │          n = Core.getfield(%30, 1)
                                                              > │          @_4 = Core.getfield(%30, 2)
                                                              > │    %33 = Base.indexed_iterate(%29, 2, @_4)
                                                              > │          y = Core.getfield(%33, 1)
│          n = n & 3                                            │          n = n & 3
│    %32 = n == 0                                             | │    %36 = n == 0
└───       goto #11 if not %32                                | └───       goto #11 if not %36
10 ─ %34 = Base.Math.sin_kernel(y)                            | 10 ─ %38 = Base.Math.cos_kernel(y)
└───       return %34                                         <
11 ─ %36 = n == 1                                             <
└───       goto #13 if not %36                                <
12 ─ %38 = Base.Math.cos_kernel(y)                            <
└───       return %38                                           └───       return %38
13 ─ %40 = n == 2                                             | 11 ─ %40 = n == 1
└───       goto #15 if not %40                                | └───       goto #13 if not %40
14 ─ %42 = Base.Math.sin_kernel(y)                            | 12 ─ %42 = Base.Math.sin_kernel(y)
│    %43 = -%42                                                 │    %43 = -%42
└───       return %43                                           └───       return %43
15 ─ %45 = Base.Math.cos_kernel(y)                            | 13 ─ %45 = n == 2
│    %46 = -%45                                               | └───       goto #15 if not %45
└───       return %46                                         | 14 ─ %47 = Base.Math.cos_kernel(y)
                                                              > │    %48 = -%47
                                                              > └───       return %48
                                                              > 15 ─ %50 = Base.Math.sin_kernel(y)
                                                              > └───       return %50
)                                                               )

This likely won’t work on Windows though, since you need to have bash and diff installed.

3 Likes