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: 40051
============================================================
----------------------------------------
Stage: raw
Blocks: 1, Insts: 4, Edges: 0
Valid worlds: 0x0000000000009c73:0x0000000000009c73
----------------------------------------
3 1 ─ %1 = dynamic invoke Main.sin(_2::Float64)::Float64
│ %2 = dynamic invoke Main.cos(_2::Float64)::Float64
│ %3 = intrinsic Base.mul_float(%1, %2)::Float64
└── return %3
----------------------------------------
Stage: normalized
Blocks: 1, Insts: 4, Edges: 0
Valid worlds: 0x0000000000009c73:0x0000000000009c73
----------------------------------------
3 1 ─ %1 = dynamic invoke Main.sin(_2::Float64)::Float64
│ %2 = dynamic invoke Main.cos(_2::Float64)::Float64
│ %3 = dynamic (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
Valid worlds: 0x0000000000009c73:0x0000000000009c73
----------------------------------------
3 1 ─ dynamic (Mooncake.get_shared_data_field)(_1, 1)::Any
│ %2 = dynamic (Mooncake.get_shared_data_field)(_1, 2)::Any
│ %3 = dynamic (Mooncake.get_shared_data_field)(_1, 3)::Any
└── dynamic (Mooncake.__assemble_lazy_zero_rdata)(%2, _2, _3)::Any
2 ─ %5 = dynamic (Mooncake.uninit_fcodual)(sin)::Any
│ %6 = dynamic (Mooncake.rrule!!)(%5, _3)::Any
│ %7 = builtin (getfield)(%6, 1)::Any
│ %8 = builtin (getfield)(%6, 2)::Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}
│ %9 = builtin (typeassert)(%7, Mooncake.CoDual{Float64, Mooncake.NoFData})::Any
│ %10 = dynamic (Mooncake.uninit_fcodual)(cos)::Any
│ %11 = dynamic (Mooncake.rrule!!)(%10, _3)::Any
│ %12 = builtin (getfield)(%11, 1)::Any
│ %13 = builtin (getfield)(%11, 2)::Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}
│ %14 = builtin (typeassert)(%12, Mooncake.CoDual{Float64, Mooncake.NoFData})::Any
│ %15 = dynamic (Mooncake.uninit_fcodual)(Mooncake.IntrinsicsWrappers.mul_float)::Any
│ %16 = dynamic (Mooncake.rrule!!)(%15, %9, %14)::Any
│ %17 = builtin (getfield)(%16, 1)::Any
│ %18 = builtin (getfield)(%16, 2)::Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#rrule!!##15"{Float64, Float64}
│ %19 = builtin (typeassert)(%17, Mooncake.CoDual{Float64, Mooncake.NoFData})::Any
│ %20 = builtin (typeassert)(%19, Mooncake.CoDual{Float64, Mooncake.NoFData})::Any
│ %21 = builtin (tuple)(%8, %13, %18)::Any
│ dynamic (push!)(%3, %21)::Any
└── return %20
----------------------------------------
Stage: rvs_ir
Blocks: 5, Insts: 57, Edges: 4
Valid worlds: 0x0000000000009c73:0x0000000000009c73
----------------------------------------
3 1 ─ dynamic (Mooncake.get_shared_data_field)(_1, 1)::Any
│ %2 = dynamic (Mooncake.get_shared_data_field)(_1, 2)::Any
│ %3 = dynamic (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 = dynamic (pop!)(%3)::Any
│ %13 = builtin (getfield)(%12, 1)::Any
│ %14 = builtin (getfield)(%12, 2)::Any
│ %15 = builtin (getfield)(%12, 3)::Any
│ %16 = builtin (getfield)(%7, :x)::Any
│ %17 = dynamic (Mooncake.increment!!)(%16, _2)::Any
│ builtin (setfield!)(%7, :x, %17)::Any
│ %19 = builtin (getfield)(%7, :x)::Any
│ builtin (setfield!)(%7, :x, 0.0)::Any
│ %21 = dynamic (%15)(%19)::Any
│ %22 = builtin (getfield)(%21, 2)::Any
│ %23 = builtin (getfield)(%8, :x)::Any
│ %24 = dynamic (Mooncake.increment!!)(%23, %22)::Any
│ builtin (setfield!)(%8, :x, %24)::Any
│ %26 = builtin (getfield)(%21, 3)::Any
│ %27 = builtin (getfield)(%6, :x)::Any
│ %28 = dynamic (Mooncake.increment!!)(%27, %26)::Any
│ builtin (setfield!)(%6, :x, %28)::Any
│ %30 = builtin (getfield)(%6, :x)::Any
│ builtin (setfield!)(%6, :x, 0.0)::Any
│ %32 = dynamic (%14)(%30)::Any
│ %33 = builtin (getfield)(%32, 2)::Any
│ %34 = builtin (getfield)(%5, :x)::Any
│ %35 = dynamic (Mooncake.increment!!)(%34, %33)::Any
│ builtin (setfield!)(%5, :x, %35)::Any
│ %37 = builtin (getfield)(%8, :x)::Any
│ builtin (setfield!)(%8, :x, 0.0)::Any
│ %39 = dynamic (%13)(%37)::Any
│ %40 = builtin (getfield)(%39, 2)::Any
│ %41 = builtin (getfield)(%5, :x)::Any
│ %42 = dynamic (Mooncake.increment!!)(%41, %40)::Any
│ builtin (setfield!)(%5, :x, %42)::Any
└── $(QuoteNode(Mooncake.BasicBlockCode.ID(6)))
4 ─ goto #5
5 ─ %46 = dynamic (getindex)(%2)::Any
│ %47 = builtin (getfield)(%4, :x)::Any
│ %48 = builtin (getfield)(%46, 1)::Any
│ %49 = dynamic (Mooncake.instantiate)(%48)::Any
│ %50 = dynamic (Mooncake.increment!!)(%47, %49)::Any
│ %51 = builtin (getfield)(%5, :x)::Any
│ %52 = builtin (getfield)(%46, 2)::Any
│ %53 = dynamic (Mooncake.instantiate)(%52)::Any
│ %54 = dynamic (Mooncake.increment!!)(%51, %53)::Any
│ %55 = builtin (tuple)(%50, %54)::Any
│ %56 = builtin (typeassert)(%55, Tuple{Mooncake.NoRData, Float64})::Any
└── return %56
----------------------------------------
Stage: optimized_fwd
Blocks: 12, Insts: 47, Edges: 13
Valid worlds: 0x0000000000009c73:0x0000000000009c73
----------------------------------------
3 1 ── %1 = builtin Mooncake.getfield(_1, 2)::Base.RefValue{Tuple{Mooncake.LazyZeroRData{typeof(Main.demo_fn), Nothing}, Mooncake.LazyZeroRData{Float64, Nothing}}}
│ %2 = builtin 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!!#rrule!!##15"{Float64, Float64}}}
└─── builtin 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 = builtin Base.getfield(_3, :x)::Float64
│ %5 = builtin 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 = builtin Base.getfield(_3, :x)::Float64
│ %9 = builtin 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!!#rrule!!##15"{Float64, Float64}, %10, %6)::Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#rrule!!##15"{Float64, Float64}
│ %13 = intrinsic (Core.Intrinsics.mul_float)(%6, %10)::Float64
│ %14 = %new(Mooncake.CoDual{Float64, Mooncake.NoFData}, %13, $(QuoteNode(Mooncake.NoFData())))::Mooncake.CoDual{Float64, Mooncake.NoFData}
│ %15 = builtin (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!!#rrule!!##15"{Float64, Float64}}
│ %16 = builtin Base.getfield(%2, :position)::Int64
│ %17 = intrinsic Base.add_int(%16, 1)::Int64
│ %18 = builtin 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!!#rrule!!##15"{Float64, Float64}}}
│ builtin Base.setfield!(%2, :position, %17)::Int64
│ %20 = builtin Base.getfield(%18, :size)::Tuple{Int64}
│ %21 = $(Expr(:boundscheck, true))::Bool
│ %22 = builtin Base.getfield(%20, 1, %21)::Int64
│ %23 = intrinsic Base.sle_int(%17, %22)::Bool
└─── goto #10 if not %23
3 ── %25 = $(Expr(:boundscheck, false))::Bool
└─── goto #7 if not %25
4 ── %27 = intrinsic Base.sub_int(%17, 1)::Int64
│ %28 = intrinsic Base.bitcast(UInt64, %27)::UInt64
│ %29 = builtin Base.getfield(%18, :size)::Tuple{Int64}
│ %30 = $(Expr(:boundscheck, true))::Bool
│ %31 = builtin Base.getfield(%29, 1, %30)::Int64
│ %32 = intrinsic Base.bitcast(UInt64, %31)::UInt64
│ %33 = intrinsic Base.ult_int(%28, %32)::Bool
└─── goto #6 if not %33
5 ── goto #7
6 ── %36 = builtin 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!!#rrule!!##15"{Float64, Float64}}}, %36::Tuple{Int64})::Union{}
└─── unreachable
7 ┄─ %39 = builtin 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!!#rrule!!##15"{Float64, Float64}}}
│ %40 = builtin 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!!#rrule!!##15"{Float64, Float64}}}
│ builtin 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!!#rrule!!##15"{Float64, Float64}}
└─── goto #8
8 ── goto #9
9 ── goto #11
10 ─ 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!!#rrule!!##15"{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!!#rrule!!##15"{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!!#rrule!!##15"{Float64, Float64}}}
11 ┄ goto #12
12 ─ return %14
----------------------------------------
Stage: optimized_rvs
Blocks: 23, Insts: 74, Edges: 23
Valid worlds: 0x0000000000009c73:0x0000000000009c73
----------------------------------------
3 1 ── %1 = builtin 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!!#rrule!!##15"{Float64, Float64}}}
2 ── goto #3
3 ── %3 = builtin Base.getfield(%1, :position)::Int64
│ %4 = builtin 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!!#rrule!!##15"{Float64, Float64}}}
│ %5 = $(Expr(:boundscheck, false))::Bool
└─── goto #7 if not %5
4 ── %7 = intrinsic Base.sub_int(%3, 1)::Int64
│ %8 = intrinsic Base.bitcast(Base.UInt, %7)::UInt64
│ %9 = builtin Base.getfield(%4, :size)::Tuple{Int64}
│ %10 = $(Expr(:boundscheck, true))::Bool
│ %11 = builtin Base.getfield(%9, 1, %10)::Int64
│ %12 = intrinsic Base.bitcast(Base.UInt, %11)::UInt64
│ %13 = intrinsic Base.ult_int(%8, %12)::Bool
└─── goto #6 if not %13
5 ── goto #7
6 ── %16 = builtin 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!!#rrule!!##15"{Float64, Float64}}}, %16::Tuple{Int64})::Union{}
└─── unreachable
7 ┄─ %19 = builtin 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!!#rrule!!##15"{Float64, Float64}}}
│ %20 = builtin 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!!#rrule!!##15"{Float64, Float64}}}
│ %21 = builtin 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!!#rrule!!##15"{Float64, Float64}}
└─── goto #8
8 ── %23 = intrinsic Base.sub_int(%3, 1)::Int64
│ builtin Base.setfield!(%1, :position, %23)::Int64
└─── goto #9
9 ── %26 = builtin (getfield)(%21, 1)::Mooncake.NfwdMooncake.Pullback{typeof(sin), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}
│ %27 = builtin (getfield)(%21, 2)::Mooncake.NfwdMooncake.Pullback{typeof(cos), 1, Tuple{Float64}, Tuple{Mooncake.NoFData}, Mooncake.NoFData}
│ %28 = builtin (getfield)(%21, 3)::Mooncake.IntrinsicsWrappers.var"#mul_float_pb!!#rrule!!##15"{Float64, Float64}
│ %29 = intrinsic Base.add_float($(QuoteNode(0.0)), _2)::Float64
│ %30 = builtin Core.getfield(%28, :_b)::Float64
│ %31 = intrinsic Base.mul_float(%29, %30)::Float64
│ %32 = builtin Core.getfield(%28, :_a)::Float64
│ %33 = intrinsic Base.mul_float(%32, %29)::Float64
│ %34 = intrinsic Base.add_float($(QuoteNode(0.0)), %31)::Float64
│ %35 = intrinsic Base.add_float($(QuoteNode(0.0)), %33)::Float64
│ %36 = builtin Base.getfield(%27, :primals)::Tuple{Float64}
│ %37 = $(Expr(:boundscheck, true))::Bool
│ %38 = builtin Base.getfield(%36, 1, %37)::Float64
│ %39 = invoke Mooncake.Nfwd.sincos(%38::Float64)::Tuple{Float64, Float64}
│ %40 = builtin Base.getfield(%39, 1)::Float64
│ %41 = intrinsic Base.neg_float(%40)::Float64
│ %42 = intrinsic Base.mul_float(%41, 1.0)::Float64
│ %43 = intrinsic Base.eq_float(%35, 0.0)::Bool
│ %44 = builtin (Core.ifelse)(%43, 0.0, %42)::Float64
│ %45 = intrinsic Base.mul_float(%35, %44)::Float64
└─── goto #10
10 ─ goto #11
11 ─ goto #12
12 ─ %49 = intrinsic Base.add_float(0.0, %45)::Float64
└─── goto #13
13 ─ goto #14
14 ─ goto #15
15 ─ %53 = intrinsic Base.add_float($(QuoteNode(0.0)), %49)::Float64
│ %54 = builtin Base.getfield(%26, :primals)::Tuple{Float64}
│ %55 = $(Expr(:boundscheck, true))::Bool
│ %56 = builtin Base.getfield(%54, 1, %55)::Float64
│ %57 = invoke Mooncake.Nfwd.sincos(%56::Float64)::Tuple{Float64, Float64}
│ %58 = builtin Base.getfield(%57, 2)::Float64
│ %59 = intrinsic Base.mul_float(%58, 1.0)::Float64
│ %60 = intrinsic Base.eq_float(%34, 0.0)::Bool
│ %61 = builtin (Core.ifelse)(%60, 0.0, %59)::Float64
│ %62 = intrinsic Base.mul_float(%34, %61)::Float64
└─── goto #16
16 ─ goto #17
17 ─ goto #18
18 ─ %66 = intrinsic Base.add_float(0.0, %62)::Float64
└─── goto #19
19 ─ goto #20
20 ─ goto #21
21 ─ %70 = intrinsic Base.add_float(%53, %66)::Float64
22 ─ goto #23
23 ─ %72 = intrinsic Base.add_float(%70, 0.0)::Float64
│ %73 = builtin (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: 40051
============================================================
----------------------------------------
Stage: raw
Blocks: 1, Insts: 4, Edges: 0
Valid worlds: 0x0000000000009c73:0x0000000000009c73
----------------------------------------
3 1 ─ %1 = dynamic invoke Main.sin(_2::Float64)::Float64
│ %2 = dynamic invoke Main.cos(_2::Float64)::Float64
│ %3 = intrinsic Base.mul_float(%1, %2)::Float64
└── return %3show_diff(ins; from=:raw, to=:normalized) # diff between stages============================================================
Diff: raw → normalized
============================================================
--- stage1
+++ stage2
- │ %3 = intrinsic Base.mul_float(%1, %2)::Float64
+ │ %3 = dynamic (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: 40053
Stage worlds:
bbcode: N/A
dual_ir: 40053
normalized: 40053
raw: 40053
optimized: 40053Reverse 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: 40053
Stage worlds:
bbcode: N/A
optimized_fwd: 40053
optimized_rvs: 40053
normalized: 40053
fwd_ir: 40053
raw: 40053
rvs_ir: 40053This reports the world at which each stage was compiled and flags mismatches.