Transform CodeInfo of a function to get a new function by purging unused args from its arg list

Hello,

I wish to purge an unused argument from the arglist of a function. Following CodeTransformation.jl, I wrote a function to do this task:

function assign_purge_argument!(@nospecialize(p::Pair{<:Function,<:Function}), rule::Pair{Int,<:Any})
    (src, dst) = p
    (nth, value) = rule
    @assert length(methods(src)) == 1 "assign_purge_argument!(src=>dst, n=>val) requires src to have only one method."
    mod = getmodule(dst)
    for m in methods(src).ms
        # to CodeInfo
        ci = uncompressed_ast(m)
        nth_slot = Core.SlotNumber(nth + 1)
        # transform CodeInfo by replacing the nth argument to value
        for ex in ci.code
            if ex isa Expr
                map!(x -> (x === nth_slot ? value : x), ex.args, ex.args)
            end
        end
        # purge nth argument from slots
        ci.slotnames = ci.slotnames[[i for i = 1:length(ci.slotnames) if i!=nth+1]]
        ci.slotflags = ci.slotflags[[i for i = 1:length(ci.slotflags) if i!=nth+1]]
        # purge nth argument from m.sig
        sig1 = Tuple{[m.sig.parameters[i] for i=1:length(m.sig.parameters) if i!=nth+1]...}
        # inject CodeInfo to mod = getmodule(dst)
        jl_method_def(argdata(sig1, dst), ci, mod)
    end
end

I must have done an incomplete job because, when I test it against the simplest case:

function g(x::Int, unused_var_to_purge)
    a = 3
    b = x
    (q,p) = (a*b,b)
    return q+p
end

function g2 end

assign_purge_argument!(g => g2, 2 => 0)

when I call the new function g2 as g2(3), oh my God I got segmentation fault which is far beyond my ability to trace out the problem:

(base) dabajabaza@XXXXXX:~/.........../BooleanAlgebraTools$ julia
               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.8.0 (2022-08-17)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

(@v1.8) pkg> activate .
  Activating project at `~/........../BooleanAlgebraTools`

julia> using BooleanAlgebraTools

julia> using BooleanAlgebraTools.CodeTransformation
julia> function g(x::Int,y)
           a = 3
           b = x
           (q,p) = (a*b,b)
           return q+p
       end
g (generic function with 1 method)

julia> function g2 end
g2 (generic function with 0 methods)

julia> assign_purge_argument!(g => g2, 2 => 0)

julia> g2(1)

Internal error: encountered unexpected error in runtime:
BoundsError(a=Array{Core.Compiler.VarState, (6,)}[Core.Compiler.VarState(typ=Core.Const(val=Main.g2), undef=false), Core.Compiler.VarState(typ=Int64, undef=false), Core.Compiler.VarState(typ=Union{}, undef=true), Core.Compiler.VarState(typ=Union{}, undef=true), Core.Compiler.VarState(typ=Union{}, undef=true), Core.Compiler.VarState(typ=Union{}, undef=true)], i=(7,))
ijl_bounds_error_ints at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/rtutils.c:194
setindex! at ./array.jl:966 [inlined]
stupdate! at ./compiler/typelattice.jl:380
typeinf_local at ./compiler/abstractinterpretation.jl:2404
typeinf_nocycle at ./compiler/abstractinterpretation.jl:2462
_typeinf at ./compiler/typeinfer.jl:230
typeinf at ./compiler/typeinfer.jl:213
typeinf_ext at ./compiler/typeinfer.jl:967
typeinf_ext_toplevel at ./compiler/typeinfer.jl:1000
typeinf_ext_toplevel at ./compiler/typeinfer.jl:996
jfptr_typeinf_ext_toplevel_12553.clone_1 at /home/dabajabaza/julia/lib/julia/sys.so (unknown line)
_jl_invoke at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2367 [inlined]
ijl_apply_generic at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2549
jl_apply at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/julia.h:1838 [inlined]
jl_type_infer at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:319
jl_generate_fptr_impl at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/jitlayers.cpp:319
jl_compile_method_internal at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2081 [inlined]
jl_compile_method_internal at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2025
_jl_invoke at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2359 [inlined]
ijl_apply_generic at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2549
jl_apply at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/julia.h:1838 [inlined]
do_call at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/interpreter.c:126
eval_value at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/interpreter.c:215
eval_stmt_value at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/interpreter.c:166 [inlined]
eval_body at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/interpreter.c:612
jl_interpret_toplevel_thunk at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/interpreter.c:750
top-level scope at REPL[7]:1
jl_toplevel_eval_flex at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/toplevel.c:906
jl_toplevel_eval_flex at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/toplevel.c:850
ijl_toplevel_eval_in at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/toplevel.c:965
eval at ./boot.jl:368 [inlined]
eval_user_input at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/usr/share/julia/stdlib/v1.8/REPL/src/REPL.jl:151
repl_backend_loop at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/usr/share/julia/stdlib/v1.8/REPL/src/REPL.jl:247
start_repl_backend at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/usr/share/julia/stdlib/v1.8/REPL/src/REPL.jl:232
#run_repl#47 at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/usr/share/julia/stdlib/v1.8/REPL/src/REPL.jl:369
run_repl at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/usr/share/julia/stdlib/v1.8/REPL/src/REPL.jl:355
jfptr_run_repl_67214.clone_1 at /home/dabajabaza/julia/lib/julia/sys.so (unknown line)
_jl_invoke at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2367 [inlined]
ijl_apply_generic at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2549
#966 at ./client.jl:419
jfptr_YY.966_46273.clone_1 at /home/dabajabaza/julia/lib/julia/sys.so (unknown line)
_jl_invoke at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2367 [inlined]
ijl_apply_generic at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2549
jl_apply at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/julia.h:1838 [inlined]
jl_f__call_latest at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/builtins.c:774
#invokelatest#2 at ./essentials.jl:729 [inlined]
invokelatest at ./essentials.jl:726 [inlined]
run_main_repl at ./client.jl:404
exec_options at ./client.jl:318
_start at ./client.jl:522
jfptr__start_30463.clone_1 at /home/dabajabaza/julia/lib/julia/sys.so (unknown line)
_jl_invoke at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2367 [inlined]
ijl_apply_generic at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2549
jl_apply at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/julia.h:1838 [inlined]
true_main at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/jlapi.c:575
jl_repl_entrypoint at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/jlapi.c:719
main at julia (unknown line)
unknown function (ip: 0x7fa24f31fd8f)
__libc_start_main at /lib/x86_64-linux-gnu/libc.so.6 (unknown line)
unknown function (ip: 0x401098)

signal (11): Segmentation fault
in expression starting at REPL[7]:1
_ZN4llvm9StoreInstC1EPNS_5ValueES2_bNS_5AlignENS_14AtomicOrderingEhPNS_11InstructionE at /home/dabajabaza/julia/bin/../lib/julia/libLLVM-13jl.so (unknown line)
_ZN4llvm9StoreInstC2EPNS_5ValueES2_bNS_5AlignEPNS_11InstructionE at /home/dabajabaza/julia/bin/../lib/julia/libLLVM-13jl.so (unknown line)
CreateAlignedStore at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/usr/include/llvm/IR/IRBuilder.h:1753
CreateStore at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/usr/include/llvm/IR/IRBuilder.h:1696 [inlined]
emit_varinfo_assign at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/codegen.cpp:4379
emit_assignment at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/codegen.cpp:4418 [inlined]
emit_expr at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/codegen.cpp:4760
emit_ssaval_assign at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/codegen.cpp:4315
emit_stmtpos at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/codegen.cpp:4562 [inlined]
emit_function at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/codegen.cpp:7416
jl_emit_code at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/codegen.cpp:7778
jl_emit_codeinst at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/codegen.cpp:7823
_jl_compile_codeinst at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/jitlayers.cpp:119
jl_generate_fptr_for_unspecialized_impl at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/jitlayers.cpp:380
jl_compile_method_internal at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2094 [inlined]
jl_compile_method_internal at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2025
_jl_invoke at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2359 [inlined]
ijl_apply_generic at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2549
jl_apply at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/julia.h:1838 [inlined]
do_call at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/interpreter.c:126
eval_value at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/interpreter.c:215
eval_stmt_value at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/interpreter.c:166 [inlined]
eval_body at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/interpreter.c:612
jl_interpret_toplevel_thunk at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/interpreter.c:750
jl_toplevel_eval_flex at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/toplevel.c:906
jl_toplevel_eval_flex at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/toplevel.c:850
ijl_toplevel_eval_in at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/toplevel.c:965
eval at ./boot.jl:368 [inlined]
eval_user_input at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/usr/share/julia/stdlib/v1.8/REPL/src/REPL.jl:151
repl_backend_loop at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/usr/share/julia/stdlib/v1.8/REPL/src/REPL.jl:247
start_repl_backend at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/usr/share/julia/stdlib/v1.8/REPL/src/REPL.jl:232
#run_repl#47 at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/usr/share/julia/stdlib/v1.8/REPL/src/REPL.jl:369
run_repl at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/usr/share/julia/stdlib/v1.8/REPL/src/REPL.jl:355
jfptr_run_repl_67214.clone_1 at /home/dabajabaza/julia/lib/julia/sys.so (unknown line)
_jl_invoke at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2367 [inlined]
ijl_apply_generic at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2549
#966 at ./client.jl:419
jfptr_YY.966_46273.clone_1 at /home/dabajabaza/julia/lib/julia/sys.so (unknown line)
_jl_invoke at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2367 [inlined]
ijl_apply_generic at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2549
jl_apply at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/julia.h:1838 [inlined]
jl_f__call_latest at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/builtins.c:774
#invokelatest#2 at ./essentials.jl:729 [inlined]
invokelatest at ./essentials.jl:726 [inlined]
run_main_repl at ./client.jl:404
exec_options at ./client.jl:318
_start at ./client.jl:522
jfptr__start_30463.clone_1 at /home/dabajabaza/julia/lib/julia/sys.so (unknown line)
_jl_invoke at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2367 [inlined]
ijl_apply_generic at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/gf.c:2549
jl_apply at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/julia.h:1838 [inlined]
true_main at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/jlapi.c:575
jl_repl_entrypoint at /cache/build/default-amdci4-3/julialang/julia-release-1-dot-8/src/jlapi.c:719
main at julia (unknown line)
unknown function (ip: 0x7fa24f31fd8f)
__libc_start_main at /lib/x86_64-linux-gnu/libc.so.6 (unknown line)
unknown function (ip: 0x401098)
Allocations: 8587055 (Pool: 8581268; Big: 5787); GC: 7
Segmentation fault (core dumped)

Can anyone give me just a little bit of hit on what’s going on here? As you may realize, I am trying to write a BDD to represent a boolean function and the purge idea comes from the implementation of Shannon expansion …

This code should work.


function assign_purge_argument!(@nospecialize(p::Pair{<:Function,<:Function}), rule::Pair{Int,<:Any})
    (src, dst) = p
    (nth, value) = rule
    @assert length(methods(src)) == 1 "assign_purge_argument!(src=>dst, n=>val) requires src to have only one method."
    mod = getmodule(dst)
    function change_slotnumber_after_nth(s)
        if s isa Core.SlotNumber
            return (s.id==nth+1) ? value : (s.id>nth+1 ? Core.SlotNumber(s.id-1) : s)
        else
            return s
        end
    end
    for m in methods(src).ms
        # to CodeInfo
        ci = uncompressed_ast(m)
        # transform CodeInfo
        for ex in ci.code
            if ex isa Expr
                map!(change_slotnumber_after_nth, ex.args, ex.args)
            end
        end
        # purge nth argument from slots
        ci.slotnames = ci.slotnames[[i for i = 1:length(ci.slotnames) if i!=nth+1]]
        ci.slotflags = ci.slotflags[[i for i = 1:length(ci.slotflags) if i!=nth+1]]
        # purge nth argument from m.sig
        sig1 = Tuple{[m.sig.parameters[i] for i=1:length(m.sig.parameters) if i!=nth+1]...}
        # inject CodeInfo to mod = getmodule(dst)
        jl_method_def(argdata(sig1, dst), ci, mod)
    end
end

After debuging with @code_lowered and @code_typed I just realized that I forgot to decrease the id of each slot number after the n’th one (which is assigned and then purged). Now the updated version of assign_purge_argument! works (at least for all test cases I have at hand).

1 Like