diff --git a/src/bbcode.jl b/src/bbcode.jl index 843877a..79f834c 100644 --- a/src/bbcode.jl +++ b/src/bbcode.jl @@ -140,22 +140,44 @@ end collect_stmts(bb::BBlock)::Vector{IDInstPair} = collect(zip(bb.inst_ids, bb.insts)) -struct BBCode - blocks::Vector{BBlock} - argtypes::Vector{Any} - sptypes::Vector{CC.VarState} - linetable::Vector{Core.LineInfoNode} - meta::Vector{Expr} -end +@static if VERSION >= v"1.12-" + struct BBCode + blocks::Vector{BBlock} + argtypes::Vector{Any} + sptypes::Vector{CC.VarState} + debuginfo::CC.DebugInfoStream + meta::Vector{Expr} + valid_worlds::CC.WorldRange + end -function BBCode(ir::Union{IRCode,BBCode}, new_blocks::Vector{BBlock}) - return BBCode( - new_blocks, - CC.copy(ir.argtypes), - CC.copy(ir.sptypes), - CC.copy(ir.linetable), - CC.copy(ir.meta), - ) + function BBCode(ir::Union{IRCode,BBCode}, new_blocks::Vector{BBlock}) + return BBCode( + new_blocks, + CC.copy(ir.argtypes), + CC.copy(ir.sptypes), + CC.copy(ir.debuginfo), + CC.copy(ir.meta), + ir.valid_worlds, + ) + end +else + struct BBCode + blocks::Vector{BBlock} + argtypes::Vector{Any} + sptypes::Vector{CC.VarState} + linetable::Vector{Core.LineInfoNode} + meta::Vector{Expr} + end + + function BBCode(ir::Union{IRCode,BBCode}, new_blocks::Vector{BBlock}) + return BBCode( + new_blocks, + CC.copy(ir.argtypes), + CC.copy(ir.sptypes), + CC.copy(ir.linetable), + CC.copy(ir.meta), + ) + end end # Makes use of the above outer constructor for `BBCode`. @@ -346,20 +368,42 @@ function CC.IRCode(bb_code::BBCode) insts = _ids_to_line_numbers(bb_code) cfg = control_flow_graph(bb_code) insts = _lines_to_blocks(insts, cfg) - return IRCode( - CC.InstructionStream( - map(x -> x.stmt, insts), - map(x -> x.type, insts), - map(x -> x.info, insts), - map(x -> x.line, insts), - map(x -> x.flag, insts), - ), - cfg, - CC.copy(bb_code.linetable), - CC.copy(bb_code.argtypes), - CC.copy(bb_code.meta), - CC.copy(bb_code.sptypes), - ) + @static if VERSION >= v"1.12-" + # See e.g. here for how the NTuple{3,Int}s get flattened for InstructionStream: + # https://github.com/JuliaLang/julia/blob/16a2bf0a3b106b03dda23b8c9478aab90ffda5e1/Compiler/src/ssair/ir.jl#L299 + lines = map(x -> x.line, insts) + lines = collect(Iterators.flatten(lines)) + return IRCode( + CC.InstructionStream( + map(x -> x.stmt, insts), + collect(Any, map(x -> x.type, insts)), + collect(CC.CallInfo, map(x -> x.info, insts)), + lines, + map(x -> x.flag, insts), + ), + cfg, + CC.copy(bb_code.debuginfo), + CC.copy(bb_code.argtypes), + CC.copy(bb_code.meta), + CC.copy(bb_code.sptypes), + bb_code.valid_worlds, + ) + else + return IRCode( + CC.InstructionStream( + map(x -> x.stmt, insts), + map(x -> x.type, insts), + map(x -> x.info, insts), + map(x -> x.line, insts), + map(x -> x.flag, insts), + ), + cfg, + CC.copy(bb_code.linetable), + CC.copy(bb_code.argtypes), + CC.copy(bb_code.meta), + CC.copy(bb_code.sptypes), + ) + end end function _lower_switch_statements(bb_code::BBCode) diff --git a/src/copyable_task.jl b/src/copyable_task.jl index ac54901..973b618 100644 --- a/src/copyable_task.jl +++ b/src/copyable_task.jl @@ -377,7 +377,11 @@ expression, otherwise `false`. """ function is_produce_stmt(x)::Bool if Meta.isexpr(x, :invoke) && length(x.args) == 3 && x.args[1] isa Core.MethodInstance + # This branch is hit on Julia 1.11 and earlier. return x.args[1].specTypes <: Tuple{typeof(produce),Any} + elseif Meta.isexpr(x, :invoke) && length(x.args) == 3 && x.args[1] isa Core.CodeInstance + # This branch is hit on Julia 1.12. + return x.args[1].def.specTypes <: Tuple{typeof(produce),Any} elseif Meta.isexpr(x, :call) && length(x.args) == 2 return get_value(x.args[1]) === produce else @@ -400,7 +404,13 @@ function stmt_might_produce(x, ret_type::Type)::Bool # Statement will terminate in the usual fashion, so _do_ bother recusing. is_produce_stmt(x) && return true - Meta.isexpr(x, :invoke) && return might_produce(x.args[1].specTypes) + @static if VERSION >= v"1.12-" + # On Julia 1.12 x.args has CodeInstances rather than MethodInstances. We use .def + # to get the MethodInstances. + Meta.isexpr(x, :invoke) && return might_produce(x.args[1].def.specTypes) + else + Meta.isexpr(x, :invoke) && return might_produce(x.args[1].specTypes) + end if Meta.isexpr(x, :call) # This is a hack -- it's perfectly possible for `DataType` calls to produce in general. f = get_function(x.args[1]) @@ -964,7 +974,13 @@ function derive_copyable_task_ir(ir::BBCode)::Tuple{BBCode,Tuple,Vector{Any}} # Derive TapedTask for this statement. (callable, callable_args) = if Meta.isexpr(stmt, :invoke) - sig = stmt.args[1].specTypes + @static if VERSION >= v"1.12-" + # On Julia 1.12 stmt.args has CodeInstances rather than + # MethodInstances. We use .def to get the MethodInstances. + sig = stmt.args[1].def.specTypes + else + sig = stmt.args[1].specTypes + end v = Any[Any] (LazyCallable{sig,callable_ret_type(sig, v)}(), stmt.args[2:end]) elseif Meta.isexpr(stmt, :call) @@ -1079,7 +1095,13 @@ function derive_copyable_task_ir(ir::BBCode)::Tuple{BBCode,Tuple,Vector{Any}} new_argtypes = vcat(typeof(refs), copy(ir.argtypes)) # Return BBCode and the `Ref`s. - new_ir = BBCode(new_bblocks, new_argtypes, ir.sptypes, ir.linetable, ir.meta) + @static if VERSION >= v"1.12-" + new_ir = BBCode( + new_bblocks, new_argtypes, ir.sptypes, ir.debuginfo, ir.meta, ir.valid_worlds + ) + else + new_ir = BBCode(new_bblocks, new_argtypes, ir.sptypes, ir.linetable, ir.meta) + end return new_ir, refs, possible_produce_types end diff --git a/src/test_utils.jl b/src/test_utils.jl index 98e3abc..069f2f2 100644 --- a/src/test_utils.jl +++ b/src/test_utils.jl @@ -59,7 +59,11 @@ function (case::Testcase)() end for _ in iteration_results - @test count_allocs(consume, t) == 0 + # TODO(mhauru) We seem to be causing more allocations than expected on + # v1.12, needs investigating. + @static if VERSION < v"1.12-" + @test count_allocs(consume, t) == 0 + end end end end diff --git a/src/utils.jl b/src/utils.jl index e444965..1612995 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -68,7 +68,11 @@ function optimise_ir!(ir::IRCode; show_ir=false, do_inline=true) ir = CC.compact!(ir) # CC.verify_ir(ir, true, false, CC.optimizer_lattice(local_interp)) - CC.verify_linetable(ir.linetable, true) + @static if VERSION >= v"1.12-" + CC.verify_linetable(ir.debuginfo, div(length(ir.debuginfo.codelocs), 3), true) + else + CC.verify_linetable(ir.linetable, true) + end if show_ir println("Post-optimization") display(ir) @@ -96,13 +100,29 @@ end # Run type inference and constant propagation on the ir. Credit to @oxinabox: # https://gist.github.com/oxinabox/cdcffc1392f91a2f6d80b2524726d802#file-example-jl-L54 function __infer_ir!(ir, interp::CC.AbstractInterpreter, mi::CC.MethodInstance) - method_info = CC.MethodInfo(true, nothing) #=propagate_inbounds=# - min_world = world = get_inference_world(interp) - max_world = Base.get_world_counter() - irsv = CC.IRInterpretationState( - interp, method_info, ir, mi, ir.argtypes, world, min_world, max_world - ) - rt = CC._ir_abstract_constant_propagation(interp, irsv) + # TODO(mhauru) Why is this line here? This function is no longer defined in 1.12 + @static if VERSION >= v"1.12-" + nargs = length(ir.argtypes) - 1 + # TODO(mhauru) How do we figure out isva? I don't think it's in ir or mi, see above + # prints. + isva = false + propagate_inbounds = true + spec_info = CC.SpecInfo(nargs, isva, propagate_inbounds, nothing) + min_world = world = get_inference_world(interp) + max_world = Base.get_world_counter() + irsv = CC.IRInterpretationState( + interp, spec_info, ir, mi, ir.argtypes, world, min_world, max_world + ) + rt = CC.ir_abstract_constant_propagation(interp, irsv) + else + method_info = CC.MethodInfo(true, nothing) #=propagate_inbounds=# + min_world = world = get_inference_world(interp) + max_world = Base.get_world_counter() + irsv = CC.IRInterpretationState( + interp, method_info, ir, mi, ir.argtypes, world, min_world, max_world + ) + rt = CC._ir_abstract_constant_propagation(interp, irsv) + end return ir end @@ -168,13 +188,28 @@ function opaque_closure( ) # This implementation is copied over directly from `Core.OpaqueClosure`. ir = CC.copy(ir) - nargs = length(ir.argtypes) - 1 - sig = Base.Experimental.compute_oc_signature(ir, nargs, isva) + @static if VERSION >= v"1.12-" + ir.debuginfo.def === nothing && + (ir.debuginfo.def = :var"generated IR for OpaqueClosure") + # On v1.12 OpaqueClosure expects the first arg to be the environment. + ir.argtypes[1] = typeof(env) + end + nargtypes = length(ir.argtypes) + nargs = nargtypes - 1 + @static if VERSION >= v"1.12-" + sig = CC.compute_oc_signature(ir, nargs, isva) + else + sig = Base.Experimental.compute_oc_signature(ir, nargs, isva) + end src = ccall(:jl_new_code_info_uninit, Ref{CC.CodeInfo}, ()) - src.slotnames = fill(:none, nargs + 1) - src.slotflags = fill(zero(UInt8), length(ir.argtypes)) + src.slotnames = fill(:none, nargtypes) + src.slotflags = fill(zero(UInt8), nargtypes) src.slottypes = copy(ir.argtypes) src.rettype = ret_type + @static if VERSION >= v"1.12-" + src.isva = isva + src.nargs = UInt(nargs + 1) + end src = CC.ir_to_codeinf!(src, ir) return Base.Experimental.generate_opaque_closure( sig, Union{}, ret_type, src, nargs, isva, env...; do_compile