From 630817b00013ef170823d82c9914d11b35a6bd2a Mon Sep 17 00:00:00 2001 From: Simeon Schaub Date: Wed, 23 Jun 2021 13:33:21 +0200 Subject: [PATCH] add option to print inlining costs This allows printing inlining costs alongside optimized IR similar to `Base.print_statement_costs`, which I found useful for debugging why some functions are not inlined. They are highlighted green, because I found them hard to distinguish otherwise, but I am open to suggestions regarding formatting. This relies on JuliaLang/julia#41257, which I hope can be backported to 1.7, so we don't have to worry about compatibility. --- src/Cthulhu.jl | 6 +++-- src/codeview.jl | 70 +++++++++++++++++++++++++++++-------------------- src/ui.jl | 5 +++- 3 files changed, 49 insertions(+), 32 deletions(-) diff --git a/src/Cthulhu.jl b/src/Cthulhu.jl index a250d5ad..f0dcdb06 100644 --- a/src/Cthulhu.jl +++ b/src/Cthulhu.jl @@ -199,7 +199,7 @@ using .DInfo: DebugInfo # src/reflection.jl has the tools to discover methods # src/ui.jl provides the user facing interface to which _descend responds ## -function _descend(interp::CthulhuInterpreter, mi::MethodInstance; override::Union{Nothing, InferenceResult} = nothing, iswarn::Bool, params=current_params(), optimize::Bool=true, interruptexc::Bool=true, verbose=true, kwargs...) +function _descend(interp::CthulhuInterpreter, mi::MethodInstance; override::Union{Nothing, InferenceResult} = nothing, iswarn::Bool, params=current_params(), optimize::Bool=true, interruptexc::Bool=true, verbose=true, inline_cost::Bool=false, kwargs...) debuginfo = DInfo.compact # default is compact debuginfo if :debuginfo in keys(kwargs) selected = kwargs[:debuginfo] @@ -244,7 +244,7 @@ function _descend(interp::CthulhuInterpreter, mi::MethodInstance; override::Unio preprocess_ci!(codeinf, mi, optimize, CONFIG) callsites = find_callsites(interp, codeinf, infos, mi, slottypes, optimize; params, kwargs...) - display_CI && cthulu_typed(stdout, debuginfo_key, codeinf, rt, mi, iswarn, verbose) + display_CI && cthulu_typed(stdout, debuginfo_key, codeinf, rt, mi, iswarn, verbose, inline_cost) display_CI = true end @@ -327,6 +327,8 @@ function _descend(interp::CthulhuInterpreter, mi::MethodInstance; override::Unio continue elseif toggle === :debuginfo debuginfo = DebugInfo((Int(debuginfo) + 1) % 3) + elseif toggle === :inline_cost + inline_cost ⊻= true elseif toggle === :highlighter CONFIG.enable_highlighter ⊻= true if CONFIG.enable_highlighter diff --git a/src/codeview.jl b/src/codeview.jl index 1b22ab1e..ec026499 100644 --- a/src/codeview.jl +++ b/src/codeview.jl @@ -72,53 +72,65 @@ function is_type_unstable(code::Union{IRCode, CodeInfo}, idx::Int, used::BitSet) end cthulhu_warntype(args...) = cthulhu_warntype(stdout, args...) -function cthulhu_warntype(io::IO, src, rettype, debuginfo, stable_code) - debuginfo = IRShow.debuginfo(debuginfo) - lineprinter = __debuginfo[debuginfo] - lambda_io::IOContext = io - if hasfield(typeof(src), :slotnames) && src.slotnames !== nothing - slotnames = Base.sourceinfo_slotnames(src) - lambda_io = IOContext(lambda_io, :SOURCE_SLOTNAMES => slotnames) - show_variables(io, src, slotnames) +function cthulhu_warntype(io::IO, src, rettype, debuginfo, stable_code, inline_cost=false) + if inline_cost + error("Need a MethodInstance to show inlining costs. Call `cthulhu_typed` directly instead.") end - print(io, "Body") - InteractiveUtils.warntype_type_printer(io, rettype, true) - println(io) - - should_print_stmt = (src isa IRCode || stable_code) ? Returns(true) : is_type_unstable - bb_color = (src isa IRCode && debuginfo === :compact) ? :normal : :light_black - irshow_config = IRShowConfig( - lineprinter(src), InteractiveUtils.warntype_type_printer; - should_print_stmt, bb_color, - ) - show_ir(io, src, irshow_config) + cthulhu_typed(io, debuginfo, src, rettype, nothing, false, stable_code, inline_cost) return nothing end - -function cthulu_typed(io::IO, debuginfo, src, rt, mi, iswarn, stable_code) +function cthulu_typed(io::IO, debuginfo, src, rt, mi, iswarn, stable_code, inline_cost=false) debuginfo = IRShow.debuginfo(debuginfo) lineprinter = __debuginfo[debuginfo] rettype = ignorelimited(rt) + lambda_io::IOContext = io if isa(src, Core.CodeInfo) # we're working on pre-optimization state, need to ignore `LimitedAccuracy` src = copy(src) src.ssavaluetypes = ignorelimited.(src.ssavaluetypes::Vector{Any}) src.rettype = ignorelimited(src.rettype) - end - println(io) - println(io, "│ ─ $(string(Callsite(-1, MICallInfo(mi, rettype), :invoke)))") + if src.slotnames !== nothing + slotnames = Base.sourceinfo_slotnames(src) + lambda_io = IOContext(lambda_io, :SOURCE_SLOTNAMES => slotnames) + iswarn && show_variables(io, src, slotnames) + end + end if iswarn - cthulhu_warntype(io, src, rettype, debuginfo, stable_code) + print(io, "Body") + InteractiveUtils.warntype_type_printer(io, rettype, true) + println(io) else - bb_color = (src isa IRCode && debuginfo === :compact) ? :normal : :light_black - irshow_config = IRShowConfig(lineprinter(src); bb_color) - show_ir(io, src, irshow_config) + println(io, "│ ─ $(string(Callsite(-1, MICallInfo(mi, rettype), :invoke)))") end - println(io) + + if src isa IRCode && inline_cost + code = src isa IRCode ? src.stmts.inst : src.code + cst = Vector{Int}(undef, length(code)) + params = Core.Compiler.OptimizationParams(Core.Compiler.NativeInterpreter()) + maxcost = Core.Compiler.statement_costs!(cst, code, src, Any[mi.sparam_vals...], false, params) + nd = ndigits(maxcost) + _lineprinter = lineprinter(src) + function preprinter(io, linestart, idx) + str = idx > 0 ? lpad(cst[idx], nd+1) : " "^(nd+1) + str = sprint(io -> Base.printstyled(io, str; color=:green); context=:color=>true) + return str * _lineprinter(io, linestart, idx) + end + else + preprinter = lineprinter(src) + end + postprinter = iswarn ? InteractiveUtils.warntype_type_printer : IRShow.default_expr_type_printer + + should_print_stmt = (iswarn || src isa IRCode || stable_code) ? Returns(true) : is_type_unstable + bb_color = (src isa IRCode && debuginfo === :compact) ? :normal : :light_black + + irshow_config = IRShowConfig(preprinter, postprinter; should_print_stmt, bb_color) + + show_ir(io, src, irshow_config) + return nothing end function show_variables(io, src, slotnames) diff --git a/src/ui.jl b/src/ui.jl index b7045742..eab76d98 100644 --- a/src/ui.jl +++ b/src/ui.jl @@ -46,7 +46,7 @@ function TerminalMenus.header(m::CthulhuMenu) m.sub_menu && return "" """ Select a call to descend into or ↩ to ascend. [q]uit. [b]ookmark. - Toggles: [o]ptimize, [w]arn, [v]erbose printing for warntype code, [d]ebuginfo, [s]yntax highlight for Source/LLVM/Native. + Toggles: [o]ptimize, [w]arn, [v]erbose printing for warntype code, [d]ebuginfo, [i]nlining costs, [s]yntax highlight for Source/LLVM/Native. Show: [S]ource code, [A]ST, [L]LVM IR, [N]ative code Actions: [E]dit source code, [R]evise and redisplay Advanced: dump [P]arams cache. @@ -67,6 +67,9 @@ function TerminalMenus.keypress(m::CthulhuMenu, key::UInt32) elseif key == UInt32('d') m.toggle = :debuginfo return true + elseif key == UInt32('i') + m.toggle = :inline_cost + return true elseif key == UInt32('s') m.toggle = :highlighter return true