This is my first experiments with this sort of tooling, so it can be suboptimal, but instead of Cassette.jl one can use IRTools.jl.
Considering the same function myprogram
function myprogram()
alpha = 0.0
println("sin($(alpha)) is $(sin(alpha))")
println("cos($(alpha)) is $(cos(alpha))")
end
One can either work with IR directly
using IRTools: IR, xcall, isexpr
@dynamo function sin2cos(a...)
ir = IR(a...)
ir === nothing && return
for (x, stmt) in ir
isexpr(stmt.expr, :call) || continue
fn = stmt.expr.args[1]
if fn isa GlobalRef && fn.name == :sin
ir[x].expr.args[1] = GlobalRef(Base, :cos)
else
ir[x] = xcall(sin2cos, stmt.expr.args...)
end
end
return ir
end
julia> sin2cos() do
myprogram()
end
sin(0.0) is 1.0
cos(0.0) is 1.0
or even better, use multiple dispatch
using IRTools: IR, recurse!
mock(::typeof(sin), x) = cos(x)
@dynamo function mock(a...)
ir = IR(a...)
ir === nothing && return
recurse!(ir)
return ir
end
julia> mock() do
myprogram()
end
sin(0.0) is 1.0
cos(0.0) is 1.0
and of course it can substitute any number of function calls simultaneously
mock(::typeof(sin), x) = cos(x)
mock(::typeof(cos), x) = sin(x)
julia> mock() do
myprogram()
end
sin(0.0) is 1.0
cos(0.0) is 0.0