Skip to content

Commit 5e1c5cf

Browse files
tkfaviatesk
andauthored
Add code_ircode (JuliaLang#45306)
To match `typeinf_ircode` with how typeinf lock is used ATM (i.e., optimizer is run inside the lock), we can manually lock it because the lock is re-entrant. Co-authored-by: Shuhei Kadowaki <[email protected]>
1 parent 5554676 commit 5e1c5cf

File tree

4 files changed

+181
-10
lines changed

4 files changed

+181
-10
lines changed

base/compiler/optimize.jl

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -534,21 +534,44 @@ function ipo_escape_cache(mi_cache::MICache) where MICache
534534
end
535535
null_escape_cache(linfo::Union{InferenceResult,MethodInstance}) = nothing
536536

537-
function run_passes(ci::CodeInfo, sv::OptimizationState, caller::InferenceResult)
538-
@timeit "convert" ir = convert_to_ircode(ci, sv)
539-
@timeit "slot2reg" ir = slot2reg(ir, ci, sv)
537+
macro pass(name, expr)
538+
optimize_until = esc(:optimize_until)
539+
stage = esc(:__stage__)
540+
macrocall = :(@timeit $(esc(name)) $(esc(expr)))
541+
macrocall.args[2] = __source__ # `@timeit` may want to use it
542+
quote
543+
$macrocall
544+
matchpass($optimize_until, ($stage += 1), $(esc(name))) && $(esc(:(@goto __done__)))
545+
end
546+
end
547+
548+
matchpass(optimize_until::Int, stage, _name) = optimize_until < stage
549+
matchpass(optimize_until::String, _stage, name) = optimize_until == name
550+
matchpass(::Nothing, _, _) = false
551+
552+
function run_passes(
553+
ci::CodeInfo,
554+
sv::OptimizationState,
555+
caller::InferenceResult,
556+
optimize_until = nothing, # run all passes by default
557+
)
558+
__stage__ = 1 # used by @pass
559+
# NOTE: The pass name MUST be unique for `optimize_until::AbstractString` to work
560+
@pass "convert" ir = convert_to_ircode(ci, sv)
561+
@pass "slot2reg" ir = slot2reg(ir, ci, sv)
540562
# TODO: Domsorting can produce an updated domtree - no need to recompute here
541-
@timeit "compact 1" ir = compact!(ir)
542-
@timeit "Inlining" ir = ssa_inlining_pass!(ir, ir.linetable, sv.inlining, ci.propagate_inbounds)
563+
@pass "compact 1" ir = compact!(ir)
564+
@pass "Inlining" ir = ssa_inlining_pass!(ir, ir.linetable, sv.inlining, ci.propagate_inbounds)
543565
# @timeit "verify 2" verify_ir(ir)
544-
@timeit "compact 2" ir = compact!(ir)
545-
@timeit "SROA" ir = sroa_pass!(ir)
546-
@timeit "ADCE" ir = adce_pass!(ir)
547-
@timeit "type lift" ir = type_lift_pass!(ir)
548-
@timeit "compact 3" ir = compact!(ir)
566+
@pass "compact 2" ir = compact!(ir)
567+
@pass "SROA" ir = sroa_pass!(ir)
568+
@pass "ADCE" ir = adce_pass!(ir)
569+
@pass "type lift" ir = type_lift_pass!(ir)
570+
@pass "compact 3" ir = compact!(ir)
549571
if JLOptions().debug_level == 2
550572
@timeit "verify 3" (verify_ir(ir); verify_linetable(ir.linetable))
551573
end
574+
@label __done__ # used by @pass
552575
return ir
553576
end
554577

base/compiler/typeinfer.jl

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,39 @@ function typeinf_code(interp::AbstractInterpreter, method::Method, @nospecialize
918918
return code, rt
919919
end
920920

921+
"""
922+
typeinf_ircode(
923+
interp::AbstractInterpreter,
924+
method::Method,
925+
atype,
926+
sparams::SimpleVector,
927+
optimize_until::Union{Integer,AbstractString,Nothing},
928+
) -> (ir::Union{IRCode,Nothing}, returntype::Type)
929+
930+
Infer a `method` and return an `IRCode` with inferred `returntype` on success.
931+
"""
932+
function typeinf_ircode(
933+
interp::AbstractInterpreter,
934+
method::Method,
935+
@nospecialize(atype),
936+
sparams::SimpleVector,
937+
optimize_until::Union{Integer,AbstractString,Nothing},
938+
)
939+
ccall(:jl_typeinf_begin, Cvoid, ())
940+
frame = typeinf_frame(interp, method, atype, sparams, false)
941+
if frame === nothing
942+
ccall(:jl_typeinf_end, Cvoid, ())
943+
return nothing, Any
944+
end
945+
(; result) = frame
946+
opt_params = OptimizationParams(interp)
947+
opt = OptimizationState(frame, opt_params, interp)
948+
ir = run_passes(opt.src, opt, result, optimize_until)
949+
rt = widenconst(ignorelimited(result.result))
950+
ccall(:jl_typeinf_end, Cvoid, ())
951+
return ir, rt
952+
end
953+
921954
# compute an inferred frame
922955
function typeinf_frame(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector, run_optimizer::Bool)
923956
mi = specialize_method(method, atype, sparams)::MethodInstance

base/reflection.jl

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,6 +1291,104 @@ function code_typed_opaque_closure(@nospecialize(oc::Core.OpaqueClosure);
12911291
end
12921292
end
12931293

1294+
"""
1295+
code_ircode(f, [types])
1296+
1297+
Return an array of pairs of `IRCode` and inferred return type if type inference succeeds.
1298+
The `Method` is included instead of `IRCode` otherwise.
1299+
1300+
See also: [`code_typed`](@ref)
1301+
1302+
# Internal Keyword Arguments
1303+
1304+
This section should be considered internal, and is only for who understands Julia compiler
1305+
internals.
1306+
1307+
- `world=Base.get_world_counter()`: optional, controls the world age to use when looking up
1308+
methods, use current world age if not specified.
1309+
- `interp=Core.Compiler.NativeInterpreter(world)`: optional, controls the interpreter to
1310+
use, use the native interpreter Julia uses if not specified.
1311+
- `optimize_until`: optional, controls the optimization passes to run. If it is a string,
1312+
it specifies the name of the pass up to which the optimizer is run. If it is an integer,
1313+
it specifies the number of passes to run. If it is `nothing` (default), all passes are
1314+
run.
1315+
1316+
# Example
1317+
1318+
One can put the argument types in a tuple to get the corresponding `code_ircode`.
1319+
1320+
```jldoctest
1321+
julia> Base.code_ircode(+, (Float64, Int64))
1322+
1-element Vector{Any}:
1323+
388 1 ─ %1 = Base.sitofp(Float64, _3)::Float64
1324+
│ %2 = Base.add_float(_2, %1)::Float64
1325+
└── return %2
1326+
=> Float64
1327+
1328+
julia> Base.code_ircode(+, (Float64, Int64); optimize_until = "compact 1")
1329+
1-element Vector{Any}:
1330+
388 1 ─ %1 = Base.promote(_2, _3)::Tuple{Float64, Float64}
1331+
│ %2 = Core._apply_iterate(Base.iterate, Base.:+, %1)::Float64
1332+
└── return %2
1333+
=> Float64
1334+
```
1335+
"""
1336+
function code_ircode(
1337+
@nospecialize(f),
1338+
@nospecialize(types = default_tt(f));
1339+
world = get_world_counter(),
1340+
interp = Core.Compiler.NativeInterpreter(world),
1341+
optimize_until::Union{Integer,AbstractString,Nothing} = nothing,
1342+
)
1343+
if isa(f, Core.OpaqueClosure)
1344+
error("OpaqueClosure not supported")
1345+
end
1346+
ft = Core.Typeof(f)
1347+
if isa(types, Type)
1348+
u = unwrap_unionall(types)
1349+
tt = rewrap_unionall(Tuple{ft,u.parameters...}, types)
1350+
else
1351+
tt = Tuple{ft,types...}
1352+
end
1353+
return code_ircode_by_type(tt; world, interp, optimize_until)
1354+
end
1355+
1356+
"""
1357+
code_ircode_by_type(types::Type{<:Tuple}; ...)
1358+
1359+
Similar to [`code_ircode`](@ref), except the argument is a tuple type describing
1360+
a full signature to query.
1361+
"""
1362+
function code_ircode_by_type(
1363+
@nospecialize(tt::Type);
1364+
world = get_world_counter(),
1365+
interp = Core.Compiler.NativeInterpreter(world),
1366+
optimize_until::Union{Integer,AbstractString,Nothing} = nothing,
1367+
)
1368+
ccall(:jl_is_in_pure_context, Bool, ()) &&
1369+
error("code reflection cannot be used from generated functions")
1370+
tt = to_tuple_type(tt)
1371+
matches = _methods_by_ftype(tt, -1, world)::Vector
1372+
asts = []
1373+
for match in matches
1374+
match = match::Core.MethodMatch
1375+
meth = func_for_method_checked(match.method, tt, match.sparams)
1376+
(code, ty) = Core.Compiler.typeinf_ircode(
1377+
interp,
1378+
meth,
1379+
match.spec_types,
1380+
match.sparams,
1381+
optimize_until,
1382+
)
1383+
if code === nothing
1384+
push!(asts, meth => Any)
1385+
else
1386+
push!(asts, code => ty)
1387+
end
1388+
end
1389+
return asts
1390+
end
1391+
12941392
function return_types(@nospecialize(f), @nospecialize(types=default_tt(f));
12951393
world = get_world_counter(),
12961394
interp = Core.Compiler.NativeInterpreter(world))

test/compiler/ssair.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,23 @@ f_if_typecheck() = (if nothing; end; unsafe_load(Ptr{Int}(0)))
335335
success(pipeline(Cmd(cmd); stdout=stdout, stderr=stderr)) && isempty(String(take!(stderr)))
336336
end
337337

338+
@testset "code_ircode" begin
339+
@test first(only(Base.code_ircode(+, (Float64, Float64)))) isa Compiler.IRCode
340+
@test first(only(Base.code_ircode(+, (Float64, Float64); optimize_until = 3))) isa
341+
Compiler.IRCode
342+
@test first(only(Base.code_ircode(+, (Float64, Float64); optimize_until = "SROA"))) isa
343+
Compiler.IRCode
344+
345+
function demo(f)
346+
f()
347+
f()
348+
f()
349+
end
350+
@test first(only(Base.code_ircode(demo))) isa Compiler.IRCode
351+
@test first(only(Base.code_ircode(demo; optimize_until = 3))) isa Compiler.IRCode
352+
@test first(only(Base.code_ircode(demo; optimize_until = "SROA"))) isa Compiler.IRCode
353+
end
354+
338355
let
339356
function test_useref(stmt, v, op)
340357
if isa(stmt, Expr)

0 commit comments

Comments
 (0)