Force recompiling functions in conditional compilation

This thread seems to be relevant

In it, you’ll find the following technique, which also forms the core of the implementation of ToggleableAsserts. Re-writing your example, it would look like this:

julia> mode_on() = true
mode_on (generic function with 1 method)

julia> function inc!(a)
           if mode_on()
               a .+= 1
           else
               a .+= 2
           end
       end
inc! (generic function with 1 method)

You’ll notice that lowered code is not entirely free of branches:

julia> @code_lowered inc!([1,2,3])
CodeInfo(
1 ─ %1 = Main.mode_on()
└──      goto #3 if not %1
2 ─ %3 = Base.broadcasted(Main.:+, a, 1)
│   %4 = Base.materialize!(a, %3)
└──      return %4
3 ─ %6 = Base.broadcasted(Main.:+, a, 2)
│   %7 = Base.materialize!(a, %6)
└──      return %7
)

But later stages of compilation (starting with typed code) do eliminate branches (note how the second block below is considered unreachable):

julia> @code_warntype inc!([1,2,3])
MethodInstance for inc!(::Vector{Int64})
  from inc!(a) in Main at REPL[3]:1
Arguments
  #self#::Core.Const(inc!)
  a::Vector{Int64}
Body::Vector{Int64}
1 ─ %1 = Main.mode_on()::Core.Const(true)
│        Core.typeassert(%1, Core.Bool)
│   %3 = Base.broadcasted(Main.:+, a, 1)::Core.PartialStruct(Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(+), Tuple{Vector{Int64}, Int64}}, Any[Core.Const(+), Core.PartialStruct(Tuple{Vector{Int64}, Int64}, Any[Vector{Int64}, Core.Const(1)]), Core.Const(nothing)])
│   %4 = Base.materialize!(a, %3)::Vector{Int64}
└──      return %4
2 ─      Core.Const(:(Base.broadcasted(Main.:+, a, 2)))
│        Core.Const(:(Base.materialize!(a, %6)))
└──      Core.Const(:(return %7))

So I’d say this qualifies as conditional compilation. Furthermore, if the mode_on() method is redefined, then it will invalidate inc! and force its re-compilation next time it is called (note how the else clause is now the only reachable part of the code):

julia> mode_on() = false
mode_on (generic function with 1 method)

julia> @code_warntype inc!([1,2,3])
MethodInstance for inc!(::Vector{Int64})
  from inc!(a) in Main at REPL[3]:1
Arguments
  #self#::Core.Const(inc!)
  a::Vector{Int64}
Body::Vector{Int64}
1 ─ %1 = Main.mode_on()::Core.Const(false)
└──      goto #3 if not %1
2 ─      Core.Const(:(Base.broadcasted(Main.:+, a, 1)))
│        Core.Const(:(Base.materialize!(a, %3)))
└──      Core.Const(:(return %4))
3 ┄ %6 = Base.broadcasted(Main.:+, a, 2)::Core.PartialStruct(Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(+), Tuple{Vector{Int64}, Int64}}, Any[Core.Const(+), Core.PartialStruct(Tuple{Vector{Int64}, Int64}, Any[Vector{Int64}, Core.Const(2)]), Core.Const(nothing)])
│   %7 = Base.materialize!(a, %6)::Vector{Int64}
└──      return %7
2 Likes