Advanced Debugging
This guide covers debugging techniques for issues that go beyond Mooncake's rule system – problems at the boundary between Mooncake's generated code and Julia's compiler, runtime, or type system.
For rule-level debugging (eg, wrong tangent types, incorrect gradients), see Debug Mode and Debugging and MWEs.
IR inspection
Mooncake's AD pipeline transforms IR through several stages. The SkillUtils module in src/skill_utils.jl provides IR inspection utilities to view each stage and diff consecutive transformations. These are internal developer tools accessible via the Mooncake.SkillUtils prefix.
# Inspect all stages of the reverse-mode pipeline
ins = inspect_ir(demo_fn, 1.0)
show_ir(ins) # all stages============================================================
IR Inspection: Tuple{typeof(Main.demo_fn), Float64}
Mode: reverse, World: 26965
============================================================
----------------------------------------
Stage: raw
Blocks: 1, Insts: 4, Edges: 0
----------------------------------------
3 1 ─ %1 = invoke Main.sin(_2::Float64)::Float64
│ %2 = invoke Main.cos(_2::Float64)::Float64
│ %3 = Base.mul_float(%1, %2)::Float64
└── return %3
----------------------------------------
Stage: normalized
Blocks: 1, Insts: 4, Edges: 0
----------------------------------------
3 1 ─ %1 = invoke Main.sin(_2::Float64)::Float64
│ %2 = invoke Main.cos(_2::Float64)::Float64
│ %3 = (Mooncake.IntrinsicsWrappers.mul_float)(%1, %2)::Float64
└── return %3
----------------------------------------
Stage: bbcode
Blocks: 1, Insts: 4, Edges: 0
----------------------------------------
Block 1 (id=Mooncake.BasicBlockCode.ID(4)):
Mooncake.BasicBlockCode.ID(0): $(Expr(:invoke, MethodInstance for sin(::Float64), :(Main.sin), Core.Argument(2))) :: Float64
Mooncake.BasicBlockCode.ID(1): $(Expr(:invoke, MethodInstance for cos(::Float64), :(Main.cos), Core.Argument(2))) :: Float64
Mooncake.BasicBlockCode.ID(2): (Mooncake.IntrinsicsWrappers.mul_float)(Mooncake.BasicBlockCode.ID(0), Mooncake.BasicBlockCode.ID(1)) :: Float64
Mooncake.BasicBlockCode.ID(3): return Mooncake.BasicBlockCode.ID(2) :: Any
----------------------------------------
Stage: fwd_ir
Blocks: 2, Insts: 23, Edges: 1
----------------------------------------
3 1 ─ (Mooncake.get_shared_data_field)(_1, 1)::Any
│ %2 = (Mooncake.get_shared_data_field)(_1, 2)::Any
│ %3 = (Mooncake.get_shared_data_field)(_1, 3)::Any
└── (Mooncake.__assemble_lazy_zero_rdata)(%2, _2, _3)::Any
2 ─ %5 = (Mooncake.uninit_fcodual)(sin)::Any
│ %6 = (Mooncake.rrule!!)(%5, _3)::Any
│ %7 = (getfield)(%6, 1)::Any
│ %8 = (getfield)(%6, 2)::Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}
│ %9 = (typeassert)(%7, Mooncake.CoDual{Float64, Mooncake.NoFData})::Any
│ %10 = (Mooncake.uninit_fcodual)(cos)::Any
│ %11 = (Mooncake.rrule!!)(%10, _3)::Any
│ %12 = (getfield)(%11, 1)::Any
│ %13 = (getfield)(%11, 2)::Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}
│ %14 = (typeassert)(%12, Mooncake.CoDual{Float64, Mooncake.NoFData})::Any
│ %15 = (Mooncake.uninit_fcodual)(Mooncake.IntrinsicsWrappers.mul_float)::Any
│ %16 = (Mooncake.rrule!!)(%15, %9, %14)::Any
│ %17 = (getfield)(%16, 1)::Any
│ %18 = (getfield)(%16, 2)::Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#12"{Float64, Float64}
│ %19 = (typeassert)(%17, Mooncake.CoDual{Float64, Mooncake.NoFData})::Any
│ %20 = (typeassert)(%19, Mooncake.CoDual{Float64, Mooncake.NoFData})::Any
│ %21 = (tuple)(%8, %13, %18)::Any
│ (push!)(%3, %21)::Any
└── return %20
----------------------------------------
Stage: rvs_ir
Blocks: 5, Insts: 57, Edges: 4
----------------------------------------
3 1 ─ (Mooncake.get_shared_data_field)(_1, 1)::Any
│ %2 = (Mooncake.get_shared_data_field)(_1, 2)::Any
│ %3 = (Mooncake.get_shared_data_field)(_1, 3)::Any
│ %4 = %new(Base.RefValue{Mooncake.NoRData}, $(QuoteNode(Mooncake.NoRData())))::Any
│ %5 = %new(Base.RefValue{Float64}, $(QuoteNode(0.0)))::Any
│ %6 = %new(Base.RefValue{Float64}, $(QuoteNode(0.0)))::Any
│ %7 = %new(Base.RefValue{Float64}, $(QuoteNode(0.0)))::Any
│ %8 = %new(Base.RefValue{Float64}, $(QuoteNode(0.0)))::Any
│ %new(Base.RefValue{Any}, $(QuoteNode(Mooncake.ZeroRData())))::Any
└── $(QuoteNode(Mooncake.BasicBlockCode.ID(4)))
2 ─ goto #3
3 ─ %12 = (pop!)(%3)::Any
│ %13 = (getfield)(%12, 1)::Any
│ %14 = (getfield)(%12, 2)::Any
│ %15 = (getfield)(%12, 3)::Any
│ %16 = (getfield)(%6, :x)::Any
│ %17 = (Mooncake.increment!!)(%16, _2)::Any
│ (setfield!)(%6, :x, %17)::Any
│ %19 = (getfield)(%6, :x)::Any
│ (setfield!)(%6, :x, 0.0)::Any
│ %21 = (%15)(%19)::Any
│ %22 = (getfield)(%21, 2)::Any
│ %23 = (getfield)(%7, :x)::Any
│ %24 = (Mooncake.increment!!)(%23, %22)::Any
│ (setfield!)(%7, :x, %24)::Any
│ %26 = (getfield)(%21, 3)::Any
│ %27 = (getfield)(%8, :x)::Any
│ %28 = (Mooncake.increment!!)(%27, %26)::Any
│ (setfield!)(%8, :x, %28)::Any
│ %30 = (getfield)(%8, :x)::Any
│ (setfield!)(%8, :x, 0.0)::Any
│ %32 = (%14)(%30)::Any
│ %33 = (getfield)(%32, 2)::Any
│ %34 = (getfield)(%5, :x)::Any
│ %35 = (Mooncake.increment!!)(%34, %33)::Any
│ (setfield!)(%5, :x, %35)::Any
│ %37 = (getfield)(%7, :x)::Any
│ (setfield!)(%7, :x, 0.0)::Any
│ %39 = (%13)(%37)::Any
│ %40 = (getfield)(%39, 2)::Any
│ %41 = (getfield)(%5, :x)::Any
│ %42 = (Mooncake.increment!!)(%41, %40)::Any
│ (setfield!)(%5, :x, %42)::Any
└── $(QuoteNode(Mooncake.BasicBlockCode.ID(6)))
4 ─ goto #5
5 ─ %46 = (getindex)(%2)::Any
│ %47 = (getfield)(%4, :x)::Any
│ %48 = (getfield)(%46, 1)::Any
│ %49 = (Mooncake.instantiate)(%48)::Any
│ %50 = (Mooncake.increment!!)(%47, %49)::Any
│ %51 = (getfield)(%5, :x)::Any
│ %52 = (getfield)(%46, 2)::Any
│ %53 = (Mooncake.instantiate)(%52)::Any
│ %54 = (Mooncake.increment!!)(%51, %53)::Any
│ %55 = (tuple)(%50, %54)::Any
│ %56 = (typeassert)(%55, Tuple{Mooncake.NoRData, Float64})::Any
└── return %56
----------------------------------------
Stage: optimized_fwd
Blocks: 11, Insts: 46, Edges: 12
----------------------------------------
3 1 ── %1 = Mooncake.getfield(_1, 2)::Base.RefValue{Tuple{Mooncake.LazyZeroRData{typeof(Main.demo_fn), Nothing}, Mooncake.LazyZeroRData{Float64, Nothing}}}
│ %2 = Mooncake.getfield(_1, 3)::Mooncake.Stack{Tuple{Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#12"{Float64, Float64}}}
└─── Base.setfield!(%1, :x, (Mooncake.LazyZeroRData{typeof(Main.demo_fn), Nothing}(nothing), Mooncake.LazyZeroRData{Float64, Nothing}(nothing)))::Tuple{Mooncake.LazyZeroRData{typeof(Main.demo_fn), Nothing}, Mooncake.LazyZeroRData{Float64, Nothing}}
2 ── %4 = Base.getfield(_3, :x)::Float64
│ %5 = Core.tuple(%4)::Tuple{Float64}
│ %6 = invoke sin(%4::Float64)::Float64
│ %7 = %new(Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, sin, %5, (Mooncake.NoFData(),), $(QuoteNode(Mooncake.NoFData())))::Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}
│ %8 = Base.getfield(_3, :x)::Float64
│ %9 = Core.tuple(%8)::Tuple{Float64}
│ %10 = invoke cos(%8::Float64)::Float64
│ %11 = %new(Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, cos, %9, (Mooncake.NoFData(),), $(QuoteNode(Mooncake.NoFData())))::Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}
│ %12 = %new(Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#12"{Float64, Float64}, %10, %6)::Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#12"{Float64, Float64}
│ %13 = Core.Intrinsics.mul_float(%6, %10)::Float64
│ %14 = %new(Mooncake.CoDual{Float64, Mooncake.NoFData}, %13, $(QuoteNode(Mooncake.NoFData())))::Mooncake.CoDual{Float64, Mooncake.NoFData}
│ %15 = (tuple)(%7, %11, %12)::Tuple{Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#12"{Float64, Float64}}
│ %16 = Base.getfield(%2, :position)::Int64
│ %17 = Base.add_int(%16, 1)::Int64
│ %18 = Base.getfield(%2, :memory)::Vector{Tuple{Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#12"{Float64, Float64}}}
│ Base.setfield!(%2, :position, %17)::Int64
│ %20 = Base.getfield(%18, :size)::Tuple{Int64}
│ %21 = $(Expr(:boundscheck, true))::Bool
│ %22 = Base.getfield(%20, 1, %21)::Int64
│ %23 = Base.sle_int(%17, %22)::Bool
└─── goto #9 if not %23
3 ── %25 = $(Expr(:boundscheck, false))::Bool
└─── goto #7 if not %25
4 ── %27 = Base.sub_int(%17, 1)::Int64
│ %28 = Base.bitcast(UInt64, %27)::UInt64
│ %29 = Base.getfield(%18, :size)::Tuple{Int64}
│ %30 = $(Expr(:boundscheck, true))::Bool
│ %31 = Base.getfield(%29, 1, %30)::Int64
│ %32 = Base.bitcast(UInt64, %31)::UInt64
│ %33 = Base.ult_int(%28, %32)::Bool
└─── goto #6 if not %33
5 ── goto #7
6 ── %36 = Core.tuple(%17)::Tuple{Int64}
│ invoke Base.throw_boundserror(%18::Vector{Tuple{Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#12"{Float64, Float64}}}, %36::Tuple{Int64})::Union{}
└─── unreachable
7 ┄─ %39 = Base.getfield(%18, :ref)::MemoryRef{Tuple{Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#12"{Float64, Float64}}}
│ %40 = Base.memoryrefnew(%39, %17, false)::MemoryRef{Tuple{Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#12"{Float64, Float64}}}
│ Base.memoryrefset!(%40, %15, :not_atomic, false)::Tuple{Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#12"{Float64, Float64}}
└─── goto #8
8 ── goto #10
9 ── invoke Mooncake.push!(%18::Vector{Tuple{Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#12"{Float64, Float64}}}, %15::Tuple{Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#12"{Float64, Float64}})::Vector{Tuple{Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#12"{Float64, Float64}}}
10 ┄ goto #11
11 ─ return %14
----------------------------------------
Stage: optimized_rvs
Blocks: 23, Insts: 74, Edges: 23
----------------------------------------
3 1 ── %1 = Mooncake.getfield(_1, 3)::Mooncake.Stack{Tuple{Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#12"{Float64, Float64}}}
2 ── goto #3
3 ── %3 = Base.getfield(%1, :position)::Int64
│ %4 = Base.getfield(%1, :memory)::Vector{Tuple{Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#12"{Float64, Float64}}}
│ %5 = $(Expr(:boundscheck, false))::Bool
└─── goto #7 if not %5
4 ── %7 = Base.sub_int(%3, 1)::Int64
│ %8 = Base.bitcast(Base.UInt, %7)::UInt64
│ %9 = Base.getfield(%4, :size)::Tuple{Int64}
│ %10 = $(Expr(:boundscheck, true))::Bool
│ %11 = Base.getfield(%9, 1, %10)::Int64
│ %12 = Base.bitcast(Base.UInt, %11)::UInt64
│ %13 = Base.ult_int(%8, %12)::Bool
└─── goto #6 if not %13
5 ── goto #7
6 ── %16 = Core.tuple(%3)::Tuple{Int64}
│ invoke Base.throw_boundserror(%4::Vector{Tuple{Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#12"{Float64, Float64}}}, %16::Tuple{Int64})::Union{}
└─── unreachable
7 ┄─ %19 = Base.getfield(%4, :ref)::MemoryRef{Tuple{Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#12"{Float64, Float64}}}
│ %20 = Base.memoryrefnew(%19, %3, false)::MemoryRef{Tuple{Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#12"{Float64, Float64}}}
│ %21 = Base.memoryrefget(%20, :not_atomic, false)::Tuple{Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}, Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#12"{Float64, Float64}}
└─── goto #8
8 ── %23 = Base.sub_int(%3, 1)::Int64
│ Base.setfield!(%1, :position, %23)::Int64
└─── goto #9
9 ── %26 = (getfield)(%21, 1)::Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}
│ %27 = (getfield)(%21, 2)::Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}
│ %28 = (getfield)(%21, 3)::Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#12"{Float64, Float64}
│ %29 = Base.add_float($(QuoteNode(0.0)), _2)::Float64
│ %30 = Core.getfield(%28, :_b)::Float64
│ %31 = Base.mul_float(%29, %30)::Float64
│ %32 = Core.getfield(%28, :_a)::Float64
│ %33 = Base.mul_float(%32, %29)::Float64
│ %34 = Base.add_float($(QuoteNode(0.0)), %31)::Float64
│ %35 = Base.add_float($(QuoteNode(0.0)), %33)::Float64
│ %36 = Base.getfield(%27, :primals)::Tuple{Float64}
│ %37 = $(Expr(:boundscheck, true))::Bool
│ %38 = Base.getfield(%36, 1, %37)::Float64
│ %39 = invoke Mooncake.Nfwd.sincos(%38::Float64)::Tuple{Float64, Float64}
│ %40 = Base.getfield(%39, 1)::Float64
│ %41 = Base.neg_float(%40)::Float64
│ %42 = Base.mul_float(%41, 1.0)::Float64
│ %43 = Base.eq_float(%35, 0.0)::Bool
│ %44 = Core.ifelse(%43, 0.0, %42)::Float64
│ %45 = Base.mul_float(%35, %44)::Float64
└─── goto #10
10 ─ goto #11
11 ─ goto #12
12 ─ %49 = Base.add_float(0.0, %45)::Float64
└─── goto #13
13 ─ goto #14
14 ─ goto #15
15 ─ %53 = Base.add_float($(QuoteNode(0.0)), %49)::Float64
│ %54 = Base.getfield(%26, :primals)::Tuple{Float64}
│ %55 = $(Expr(:boundscheck, true))::Bool
│ %56 = Base.getfield(%54, 1, %55)::Float64
│ %57 = invoke Mooncake.Nfwd.sincos(%56::Float64)::Tuple{Float64, Float64}
│ %58 = Base.getfield(%57, 2)::Float64
│ %59 = Base.mul_float(%58, 1.0)::Float64
│ %60 = Base.eq_float(%34, 0.0)::Bool
│ %61 = Core.ifelse(%60, 0.0, %59)::Float64
│ %62 = Base.mul_float(%34, %61)::Float64
└─── goto #16
16 ─ goto #17
17 ─ goto #18
18 ─ %66 = Base.add_float(0.0, %62)::Float64
└─── goto #19
19 ─ goto #20
20 ─ goto #21
21 ─ %70 = Base.add_float(%53, %66)::Float64
22 ─ goto #23
23 ─ %72 = Base.add_float(%70, 0.0)::Float64
│ %73 = (tuple)($(QuoteNode(Mooncake.NoRData())), %72)::Tuple{Mooncake.NoRData, Float64}
└─── return %73show_stage(ins, :raw) # one stage============================================================
IR Inspection: Tuple{typeof(Main.demo_fn), Float64}
Mode: reverse, World: 26965
============================================================
----------------------------------------
Stage: raw
Blocks: 1, Insts: 4, Edges: 0
----------------------------------------
3 1 ─ %1 = invoke Main.sin(_2::Float64)::Float64
│ %2 = invoke Main.cos(_2::Float64)::Float64
│ %3 = Base.mul_float(%1, %2)::Float64
└── return %3show_diff(ins; from=:raw, to=:normalized) # diff between stages============================================================
Diff: raw → normalized
============================================================
--- stage1
+++ stage2
- │ %3 = Base.mul_float(%1, %2)::Float64
+ │ %3 = (Mooncake.IntrinsicsWrappers.mul_float)(%1, %2)::Float64# Forward mode
ins = inspect_ir(demo_fn, 1.0; mode=:forward)
# World age info (useful for debugging stale code)
show_world_info(ins)============================================================
World Age Report
============================================================
Inspection world: 26965
Stage worlds:
bbcode: N/A
dual_ir: N/A
normalized: N/A
raw: N/A
optimized: N/AReverse mode stages
:raw → :normalized → :bbcode → :fwd_ir / :rvs_ir → :optimized_fwd / :optimized_rvs
Forward mode stages
:raw → :normalized → :dual_ir → :optimized
The inspection tool also shows a :bbcode stage for cross-mode comparison, but forward mode does not use BBCode internally.
Primitive signatures such as sin do not generate AD IR stages here. Mooncake dispatches those calls straight to build_primitive_frule / build_primitive_rrule, so inspect_ir reports that path in notes instead of forcing derived IR generation.
When something looks wrong in generated code, diff consecutive stages to find which transformation introduced the issue.
Allocations
Unexpected allocations are the most common performance issue. Diagnostic approach:
@code_typed optimize=true– check return types are concrete (noUnionorAny).@code_llvm debuginfo=:none– search forgc_pool_alloc,gc_alloc_obj,jl_box_*, orjl_apply_generic.Base.specializations– verify whether the runtime MethodInstance matches the concrete types you expect (see warning below).
@code_typed f(args...) creates a fresh, fully-concrete specialization on demand. If the issue is specialization widening, it will show optimized code while the runtime path uses a widened MethodInstance. Cross-check with Base.specializations(method).
World age
World age issues arise when compiled code references methods from a different world than expected. Symptoms include MethodError at runtime or stale compiled code not picking up new method definitions.
In Mooncake, this most commonly affects:
DerivedRule/DerivedFRule: compiled at a fixed world, can become stale if methods they depend on are redefined.LazyDerivedRule: compiles on first call (static dispatch via:invoke), obtaining a fresh interpreter at that point.DynamicDerivedRule: resolves per-call by runtime argument types (dynamic dispatch via:call), checking a cache and obtaining a fresh interpreter on miss.
To debug, inspect the world age of generated code:
ins = inspect_ir(demo_fn, 1.0)
show_world_info(ins)============================================================
World Age Report
============================================================
Inspection world: 26965
Stage worlds:
bbcode: N/A
optimized_fwd: N/A
optimized_rvs: N/A
normalized: N/A
fwd_ir: N/A
raw: N/A
rvs_ir: N/AThis reports the world at which each stage was compiled and flags mismatches.