• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #38196

23 Aug 2025 07:12AM UTC coverage: 78.226% (+0.3%) from 77.893%
#38196

push

local

web-flow
bpart: Fix reresolution logic on export value changes (#59368)

Fixes #59272. This code was originally introduced in
e7efe42ec. The design of the binding
partitions underwent several changes, so I'm not fully sure if it was
correct at the time, but regardless, it was rendered incorrect by
60a9f6992.
In the new design, even a change to the value of a binding (not just its
visibility) can affect the
resolution outcome, so a full re-resolution is always required.

Fix identified by Claude (in one of several rollouts). Correct fix among
them identified by me.
Actual change/test by me.

48661 of 62206 relevant lines covered (78.23%)

9603959.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.95
/stdlib/InteractiveUtils/src/codeview.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
# highlighting settings
4
const highlighting = Dict{Symbol, Bool}(
5
    :warntype => true,
6
    :llvm => true,
7
    :native => true,
8
)
9

10
const llstyle = Dict{Symbol, Tuple{Bool, Union{Symbol, Int}}}(
11
    :default     => (false, :normal), # e.g. comma, equal sign, unknown token
12
    :comment     => (false, :light_black),
13
    :label       => (false, :light_red),
14
    :instruction => ( true, :light_cyan),
15
    :type        => (false, :cyan),
16
    :number      => (false, :yellow),
17
    :bracket     => (false, :yellow),
18
    :variable    => (false, :normal), # e.g. variable, register
19
    :keyword     => (false, :light_magenta),
20
    :funcname    => (false, :light_yellow),
21
)
22

23
struct ArgInfo
24
    oc::Union{Core.OpaqueClosure,Nothing}
25
    tt::Type{<:Tuple}
26

27
    # Construct from a function object + argtypes
28
    function ArgInfo(@nospecialize(f), @nospecialize(t))
1✔
29
        if isa(f, Core.Builtin)
188✔
30
            throw(ArgumentError("argument is not a generic function"))
2✔
31
        elseif f isa Core.OpaqueClosure
186✔
32
            return new(f, Base.to_tuple_type(t))
5✔
33
        else
34
            return new(nothing, signature_type(f, t))
181✔
35
        end
36
    end
37

38
    # Construct from argtypes (incl. arg0)
39
    function ArgInfo(@nospecialize(argtypes::Union{Tuple,Type{<:Tuple}}))
40
        tt = Base.to_tuple_type(argtypes)
8✔
41
        return new(nothing, tt)
8✔
42
    end
43
end
44

45
function printstyled_ll(io::IO, x, s::Symbol, trailing_spaces="")
1,050✔
46
    printstyled(io, x, bold=llstyle[s][1], color=llstyle[s][2])
2,627✔
47
    print(io, trailing_spaces)
1,050✔
48
end
49

50
# displaying type warnings
51

52
function warntype_type_printer(io::IO; @nospecialize(type), used::Bool, show_type::Bool=true, _...)
817✔
53
    (show_type && used) || return nothing
606✔
54
    str = "::$type"
360✔
55
    if !highlighting[:warntype]
360✔
56
        print(io, str)
6✔
57
    elseif type isa Union && is_expected_union(type)
354✔
58
        Base.emphasize(io, str, Base.warn_color()) # more mild user notification
15✔
59
    elseif type isa Type && (!Base.isdispatchelem(type) || type == Core.Box)
543✔
60
        Base.emphasize(io, str)
7✔
61
    else
62
        Base.printstyled(io, str, color=:cyan) # show the "good" type
332✔
63
    end
64
    return nothing
360✔
65
end
66

67
# True if one can be pretty certain that the compiler handles this union well,
68
# i.e. must be small with concrete types.
69
function is_expected_union(u::Union)
23✔
70
    Base.unionlen(u) < 4 || return false
23✔
71
    for x in Base.uniontypes(u)
23✔
72
        if !Base.isdispatchelem(x) || x == Core.Box
93✔
73
            return false
3✔
74
        end
75
    end
45✔
76
    return true
20✔
77
end
78

79
function print_warntype_codeinfo(io::IO, src::Core.CodeInfo, @nospecialize(rettype), nargs::Int; lineprinter, label_dynamic_calls)
58✔
80
    if src.slotnames !== nothing
29✔
81
        slotnames = Base.sourceinfo_slotnames(src)
29✔
82
        io = IOContext(io, :SOURCE_SLOTNAMES => slotnames)
29✔
83
        slottypes = src.slottypes
29✔
84
        nargs > 0 && println(io, "Arguments")
29✔
85
        for i = 1:length(slotnames)
29✔
86
            if i == nargs + 1
89✔
87
                println(io, "Locals")
7✔
88
            end
89
            print(io, "  ", slotnames[i])
89✔
90
            if isa(slottypes, Vector{Any})
89✔
91
                warntype_type_printer(io; type=slottypes[i], used=true)
89✔
92
            end
93
            println(io)
89✔
94
        end
149✔
95
    end
96
    print(io, "Body")
29✔
97
    warntype_type_printer(io; type=rettype, used=true)
29✔
98
    println(io)
29✔
99
    irshow_config = Base.IRShow.IRShowConfig(lineprinter(src), warntype_type_printer; label_dynamic_calls)
29✔
100
    Base.IRShow.show_ir(io, src, irshow_config)
29✔
101
    println(io)
29✔
102
end
103

104
function print_warntype_mi(io::IO, mi::Core.MethodInstance)
28✔
105
    println(io, mi)
28✔
106
    print(io, "  from ")
28✔
107
    println(io, mi.def)
28✔
108
    if !isempty(mi.sparam_vals)
28✔
109
        println(io, "Static Parameters")
5✔
110
        sig = mi.def.sig
5✔
111
        warn_color = Base.warn_color() # more mild user notification
5✔
112
        for i = 1:length(mi.sparam_vals)
5✔
113
            sig = sig::UnionAll
9✔
114
            name = sig.var.name
9✔
115
            val = mi.sparam_vals[i]
9✔
116
            print_highlighted(io::IO, v::String, color::Symbol) =
19✔
117
                if highlighting[:warntype]
118
                    Base.printstyled(io, v; color)
10✔
119
                else
120
                    Base.print(io, v)
×
121
                end
122
            if val isa TypeVar
9✔
123
                if val.lb === Union{}
3✔
124
                    print(io, "  ", name, " <: ")
1✔
125
                    print_highlighted(io, "$(val.ub)", warn_color)
1✔
126
                elseif val.ub === Any
2✔
127
                    print(io, "  ", sig.var.name, " >: ")
1✔
128
                    print_highlighted(io, "$(val.lb)", warn_color)
1✔
129
                else
130
                    print(io, "  ")
1✔
131
                    print_highlighted(io, "$(val.lb)", warn_color)
1✔
132
                    print(io, " <: ", sig.var.name, " <: ")
1✔
133
                    print_highlighted(io, "$(val.ub)", warn_color)
1✔
134
                end
135
            elseif val isa typeof(Vararg)
6✔
136
                print(io, "  ", name, "::")
1✔
137
                print_highlighted(io, "Int", warn_color)
1✔
138
            else
139
                print(io, "  ", sig.var.name, " = ")
5✔
140
                print_highlighted(io, "$(val)", :cyan) # show the "good" type
5✔
141
            end
142
            println(io)
9✔
143
            sig = sig.body
9✔
144
        end
9✔
145
    end
146
end
147

148
"""
149
    code_warntype([io::IO], f, types; debuginfo=:default)
150

151
Prints lowered and type-inferred ASTs for the methods matching the given generic function
152
and type signature to `io` which defaults to `stdout`. The ASTs are annotated in such a way
153
as to cause non-concrete types which may be problematic for performance to be emphasized
154
(if color is available, displayed in red). This serves as a warning of potential type instability.
155

156
Not all non-concrete types are particularly problematic for performance, and the performance
157
characteristics of a particular type is an implementation detail of the compiler.
158
`code_warntype` will err on the side of coloring types red if they might be a performance
159
concern, so some types may be colored red even if they do not impact performance.
160
Small unions of concrete types are usually not a concern, so these are highlighted in yellow.
161

162
Keyword argument `debuginfo` may be one of `:source` or `:none` (default), to specify the verbosity of code comments.
163

164
See the [`@code_warntype`](@ref man-code-warntype) section in the Performance Tips page of the manual for more information.
165

166
See also: [`@code_warntype`](@ref), [`code_typed`](@ref), [`code_lowered`](@ref), [`code_llvm`](@ref), [`code_native`](@ref).
167
"""
168
function code_warntype(io::IO, arginfo::ArgInfo;
58✔
169
                       world=Base.get_world_counter(),
170
                       interp::Base.Compiler.AbstractInterpreter=Base.Compiler.NativeInterpreter(world),
171
                       debuginfo::Symbol=:default, optimize::Bool=false, kwargs...)
172
    (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) &&
29✔
173
        error("code reflection cannot be used from generated functions")
174
    debuginfo = Base.IRShow.debuginfo(debuginfo)
29✔
175
    lineprinter = Base.IRShow.__debuginfo[debuginfo]
29✔
176
    nargs::Int = 0
29✔
177
    if arginfo.oc !== nothing
29✔
178
        (; oc, tt) = arginfo
1✔
179
        isa(oc.source, Method) && (nargs = oc.source.nargs)
2✔
180
        print_warntype_codeinfo(io, Base.code_typed_opaque_closure(oc, tt)[1]..., nargs;
1✔
181
                                lineprinter, label_dynamic_calls = optimize)
182
        return nothing
1✔
183
    end
184
    tt = arginfo.tt
28✔
185
    matches = findall(tt, Base.Compiler.method_table(interp))
28✔
186
    matches === nothing && Base.raise_match_failure(:code_warntype, tt)
28✔
187
    for match in matches.matches
28✔
188
        match = match::Core.MethodMatch
28✔
189
        src = Base.Compiler.typeinf_code(interp, match, optimize)
56✔
190
        mi = Base.Compiler.specialize_method(match)
28✔
191
        mi.def isa Method && (nargs = (mi.def::Method).nargs)
28✔
192
        print_warntype_mi(io, mi)
28✔
193
        if src isa Core.CodeInfo
28✔
194
            print_warntype_codeinfo(io, src, src.rettype, nargs;
28✔
195
                                    lineprinter, label_dynamic_calls = optimize)
196
        else
197
            println(io, "  inference not successful")
×
198
        end
199
    end
28✔
200
    nothing
28✔
201
end
202
code_warntype(io::IO, @nospecialize(f), @nospecialize(tt=Base.default_tt(f)); kwargs...) = code_warntype(io, ArgInfo(f, tt); kwargs...)
56✔
203
code_warntype(io::IO, @nospecialize(argtypes::Union{Tuple,Type{<:Tuple}}); kwargs...) = code_warntype(io, ArgInfo(argtypes); kwargs...)
4✔
204
code_warntype(args...; kwargs...) = (@nospecialize; code_warntype(stdout, args...; kwargs...))
×
205

206
using Base: CodegenParams
207

208
const GENERIC_SIG_WARNING = "; WARNING: This code may not match what actually runs.\n"
209
const OC_MISMATCH_WARNING =
210
"""
211
; WARNING: The pre-inferred opaque closure is not callable with the given arguments
212
;          and will error on dispatch with this signature.
213
"""
214

215
# Printing code representations in IR and assembly
216

217
function _dump_function(arginfo::ArgInfo, native::Bool, wrapper::Bool,
166✔
218
                        raw::Bool, dump_module::Bool, syntax::Symbol,
219
                        optimize::Bool, debuginfo::Symbol, binary::Bool,
220
                        params::CodegenParams=CodegenParams(debug_info_kind=Cint(0), debug_info_level=Cint(2), safepoint_on_entry=raw, gcstack_arg=raw))
221
    ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions")
166✔
222
    warning = ""
159✔
223
    # get the MethodInstance for the method match
224
    if arginfo.oc === nothing
159✔
225
        world = Base.get_world_counter()
155✔
226
        match = Base._which(arginfo.tt; world)
155✔
227
        mi = Base.specialize_method(match)
154✔
228
        # TODO: use jl_is_cacheable_sig instead of isdispatchtuple
229
        isdispatchtuple(mi.specTypes) || (warning = GENERIC_SIG_WARNING)
154✔
230
    else
231
        (; oc, tt) = arginfo
4✔
232
        world = UInt64(oc.world)
4✔
233
        if !isdefined(oc.source, :source)
4✔
234
            # OC was constructed from inferred source. There's only one
235
            # specialization and we can't infer anything more precise either.
236
            world = oc.source.primary_world
4✔
237
            mi = oc.source.specializations::Core.MethodInstance
4✔
238
            Base.hasintersect(typeof(oc).parameters[1], tt) || (warning = OC_MISMATCH_WARNING)
4✔
239
        else
240
            mi = Base.specialize_method(oc.source, Tuple{typeof(oc.captures), tt.parameters...}, Core.svec())
×
241
            isdispatchtuple(mi.specTypes) || (warning = GENERIC_SIG_WARNING)
×
242
        end
243
    end
244
    # get the code for it
245
    if debuginfo === :default
158✔
246
        debuginfo = :source
110✔
247
    elseif debuginfo !== :source && debuginfo !== :none
48✔
248
        throw(ArgumentError("'debuginfo' must be either :source or :none"))
×
249
    end
250
    if native
158✔
251
        if syntax !== :att && syntax !== :intel
34✔
252
            throw(ArgumentError("'syntax' must be either :intel or :att"))
×
253
        end
254
        str = ""
34✔
255
        if !dump_module
34✔
256
            # if we don't want the module metadata, attempt to disassemble what our JIT has
257
            str = _dump_function_native_disassembly(mi, world, wrapper, syntax, debuginfo, binary)
5✔
258
        end
259
        if isempty(str)
34✔
260
            # if that failed (or we want metadata), use LLVM to generate more accurate assembly output
261
            if arginfo.oc === nothing
29✔
262
                src = Base.Compiler.typeinf_code(Base.Compiler.NativeInterpreter(world), mi, true)
58✔
263
            else
264
                src, rt = Base.get_oc_code_rt(nothing, arginfo.oc, arginfo.tt, true)
×
265
            end
266
            src isa Core.CodeInfo || error("failed to infer source for $mi")
29✔
267
            str = _dump_function_native_assembly(mi, src, wrapper, syntax, debuginfo, binary, raw, params)
29✔
268
        end
269
    else
270
        if arginfo.oc === nothing
124✔
271
            src = Base.Compiler.typeinf_code(Base.Compiler.NativeInterpreter(world), mi, true)
240✔
272
        else
273
            src, rt = Base.get_oc_code_rt(nothing, arginfo.oc, arginfo.tt, true)
4✔
274
        end
275
        src isa Core.CodeInfo || error("failed to infer source for $mi")
124✔
276
        str = _dump_function_llvm(mi, src, wrapper, !raw, dump_module, optimize, debuginfo, params)
124✔
277
    end
278
    str = warning * str
157✔
279
    return str
157✔
280
end
281

282
function _dump_function_native_disassembly(mi::Core.MethodInstance, world::UInt,
283
                                           wrapper::Bool, syntax::Symbol,
284
                                           debuginfo::Symbol, binary::Bool)
285
    str = @ccall jl_dump_method_asm(mi::Any, world::UInt, false::Bool, wrapper::Bool,
5✔
286
                                    syntax::Ptr{UInt8}, debuginfo::Ptr{UInt8},
287
                                    binary::Bool)::Ref{String}
288
    return str
5✔
289
end
290

291
struct LLVMFDump
292
    tsm::Ptr{Cvoid} # opaque
293
    f::Ptr{Cvoid} # opaque
294
end
295

296
function _dump_function_native_assembly(mi::Core.MethodInstance, src::Core.CodeInfo,
29✔
297
                                        wrapper::Bool, syntax::Symbol, debuginfo::Symbol,
298
                                        binary::Bool, raw::Bool, params::CodegenParams)
299
    llvmf_dump = Ref{LLVMFDump}()
29✔
300
    @ccall jl_get_llvmf_defn(llvmf_dump::Ptr{LLVMFDump}, mi::Any, src::Any, wrapper::Bool,
29✔
301
                             true::Bool, params::CodegenParams)::Cvoid
302
    llvmf_dump[].f == C_NULL && error("could not compile the specified method")
29✔
303
    str = @ccall jl_dump_function_asm(llvmf_dump::Ptr{LLVMFDump}, false::Bool,
28✔
304
                                      syntax::Ptr{UInt8}, debuginfo::Ptr{UInt8},
305
                                      binary::Bool, raw::Bool)::Ref{String}
306
    return str
28✔
307
end
308

309
function _dump_function_llvm(
124✔
310
        mi::Core.MethodInstance, src::Core.CodeInfo, wrapper::Bool,
311
        strip_ir_metadata::Bool, dump_module::Bool,
312
        optimize::Bool, debuginfo::Symbol,
313
        params::CodegenParams)
314
    llvmf_dump = Ref{LLVMFDump}()
124✔
315
    @ccall jl_get_llvmf_defn(llvmf_dump::Ptr{LLVMFDump}, mi::Any, src::Any,
124✔
316
                             wrapper::Bool, optimize::Bool, params::CodegenParams)::Cvoid
317
    llvmf_dump[].f == C_NULL && error("could not compile the specified method")
124✔
318
    str = @ccall jl_dump_function_ir(llvmf_dump::Ptr{LLVMFDump}, strip_ir_metadata::Bool,
124✔
319
                                     dump_module::Bool, debuginfo::Ptr{UInt8})::Ref{String}
320
    return str
124✔
321
end
322

323
"""
324
    code_llvm([io=stdout,], f, types; raw=false, dump_module=false, optimize=true, debuginfo=:default)
325

326
Prints the LLVM bitcodes generated for running the method matching the given generic
327
function and type signature to `io`.
328

329
If the `optimize` keyword is unset, the code will be shown before LLVM optimizations.
330
All metadata and dbg.* calls are removed from the printed bitcode. For the full IR, set the `raw` keyword to true.
331
To dump the entire module that encapsulates the function (with declarations), set the `dump_module` keyword to true.
332
Keyword argument `debuginfo` may be one of source (default) or none, to specify the verbosity of code comments.
333

334
See also: [`@code_llvm`](@ref), [`code_warntype`](@ref), [`code_typed`](@ref), [`code_lowered`](@ref), [`code_native`](@ref).
335
"""
336
function code_llvm(io::IO, arginfo::ArgInfo;
96✔
337
                   raw::Bool=false, dump_module::Bool=false, optimize::Bool=true, debuginfo::Symbol=:default,
338
                   params::CodegenParams=CodegenParams(debug_info_kind=Cint(0), debug_info_level=Cint(2), safepoint_on_entry=raw, gcstack_arg=raw))
339
    d = _dump_function(arginfo, false, false, raw, dump_module, :intel, optimize, debuginfo, false, params)
83✔
340
    if highlighting[:llvm] && get(io, :color, false)::Bool
80✔
341
        print_llvm(io, d)
1✔
342
    else
343
        print(io, d)
79✔
344
    end
345
end
346
code_llvm(io::IO, @nospecialize(argtypes::Union{Tuple,Type{<:Tuple}}); kwargs...) = code_llvm(io, ArgInfo(argtypes); kwargs...)
6✔
347
code_llvm(io::IO, @nospecialize(f), @nospecialize(types=Base.default_tt(f)); kwargs...) = code_llvm(io, ArgInfo(f, types); kwargs...)
164✔
348
code_llvm(args...; kwargs...) = (@nospecialize; code_llvm(stdout, args...; kwargs...))
12✔
349

350
"""
351
    code_native([io=stdout,], f, types; syntax=:intel, debuginfo=:default, binary=false, dump_module=true)
352

353
Prints the native assembly instructions generated for running the method matching the given
354
generic function and type signature to `io`.
355

356
* Set assembly syntax by setting `syntax` to `:intel` (default) for intel syntax or `:att` for AT&T syntax.
357
* Specify verbosity of code comments by setting `debuginfo` to `:source` (default) or `:none`.
358
* If `binary` is `true`, also print the binary machine code for each instruction precedented by an abbreviated address.
359
* If `dump_module` is `false`, do not print metadata such as rodata or directives.
360
* If `raw` is `false`, uninteresting instructions (like the safepoint function prologue) are elided.
361

362
See also: [`@code_native`](@ref), [`code_warntype`](@ref), [`code_typed`](@ref), [`code_lowered`](@ref), [`code_llvm`](@ref).
363
"""
364
function code_native(io::IO, arginfo::ArgInfo;
38✔
365
                     dump_module::Bool=true, syntax::Symbol=:intel, raw::Bool=false,
366
                     debuginfo::Symbol=:default, binary::Bool=false,
367
                     params::CodegenParams=CodegenParams(debug_info_kind=Cint(0), debug_info_level=Cint(2), safepoint_on_entry=raw, gcstack_arg=raw))
368
    d = _dump_function(arginfo, true, false, raw, dump_module, syntax, true, debuginfo, binary, params)
36✔
369
    if highlighting[:native] && get(io, :color, false)::Bool
33✔
370
        print_native(io, d)
1✔
371
    else
372
        print(io, d)
32✔
373
    end
374
end
375
code_native(io::IO, @nospecialize(argtypes::Union{Tuple,Type{<:Tuple}}); kwargs...) = code_native(io, ArgInfo(argtypes); kwargs...)
6✔
376
code_native(io::IO, @nospecialize(f), @nospecialize(types=Base.default_tt(f)); kwargs...) = code_native(io, ArgInfo(f, types); kwargs...)
74✔
377
code_native(args...; kwargs...) = (@nospecialize; code_native(stdout, args...; kwargs...))
14✔
378

379
## colorized IR and assembly printing
380

381
const num_regex = r"^(?:\$?-?\d+|0x[0-9A-Fa-f]+|-?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?)$"
382

383
function print_llvm(io::IO, code::String)
36✔
384
    buf = IOBuffer(code)
36✔
385
    for line in eachline(buf)
72✔
386
        m = match(r"^(\s*)((?:[^;]|;\")*)(.*)$", line)
45✔
387
        m === nothing && continue
45✔
388
        indent, tokens, comment = m.captures
45✔
389
        print(io, indent)
90✔
390
        print_llvm_tokens(io, tokens)
90✔
391
        printstyled_ll(io, comment, :comment)
90✔
392
        println(io)
45✔
393
    end
45✔
394
end
395

396
const llvm_types =
397
    r"^(?:void|half|float|double|x86_\w+|ppc_\w+|label|metadata|type|opaque|token|i\d+)$"
398
const llvm_cond = r"^(?:[ou]?eq|[ou]?ne|[uso][gl][te]|ord|uno)$" # true|false
399

400
function print_llvm_tokens(io, tokens)
45✔
401
    m = match(r"^((?:[^\"\s:]+:|\"[^\"]*\":)?)(\s*)(.*)", tokens)
45✔
402
    if m !== nothing
45✔
403
        label, spaces, tokens = m.captures
45✔
404
        printstyled_ll(io, label, :label, spaces)
90✔
405
    end
406
    m = match(r"^(%[^\s=]+)(\s*)=(\s*)(.*)", tokens)
45✔
407
    if m !== nothing
45✔
408
        result, spaces, spaces2, tokens = m.captures
22✔
409
        printstyled_ll(io, result, :variable, spaces)
44✔
410
        printstyled_ll(io, '=', :default, spaces2)
44✔
411
    end
412
    m = match(r"^([a-z]\w*)(\s*)(.*)", tokens)
45✔
413
    if m !== nothing
45✔
414
        inst, spaces, tokens = m.captures
35✔
415
        iskeyword = occursin(r"^(?:define|declare|type)$", inst) || occursin("=", tokens)
66✔
416
        printstyled_ll(io, inst, iskeyword ? :keyword : :instruction, spaces)
70✔
417
    end
418

419
    print_llvm_operands(io, tokens)
45✔
420
end
421

422
function print_llvm_operands(io, tokens)
×
423
    while !isempty(tokens)
246✔
424
        tokens = print_llvm_operand(io, tokens)
108✔
425
    end
108✔
426
    return tokens
69✔
427
end
428

429
function print_llvm_operand(io, tokens)
108✔
430
    islabel = false
108✔
431
    while !isempty(tokens)
644✔
432
        m = match(r"^,(\s*)(.*)", tokens)
270✔
433
        if m !== nothing
270✔
434
            spaces, tokens = m.captures
32✔
435
            printstyled_ll(io, ',', :default, spaces)
64✔
436
            break
32✔
437
        end
438
        m = match(r"^(\*+|=)(\s*)(.*)", tokens)
238✔
439
        if m !== nothing
238✔
440
            sym, spaces, tokens = m.captures
16✔
441
            printstyled_ll(io, sym, :default, spaces)
32✔
442
            continue
16✔
443
        end
444
        m = match(r"^(\"[^\"]*\")(\s*)(.*)", tokens)
222✔
445
        if m !== nothing
222✔
446
            str, spaces, tokens = m.captures
3✔
447
            printstyled_ll(io, str, :variable, spaces)
6✔
448
            continue
3✔
449
        end
450
        m = match(r"^([({\[<])(\s*)(.*)", tokens)
219✔
451
        if m !== nothing
219✔
452
            bracket, spaces, tokens = m.captures
24✔
453
            printstyled_ll(io, bracket, :bracket, spaces)
48✔
454
            tokens = print_llvm_operands(io, tokens) # enter
48✔
455
            continue
24✔
456
        end
457
        m = match(r"^([)}\]>])(\s*)(.*)", tokens)
195✔
458
        if m !== nothing
195✔
459
            bracket, spaces, tokens = m.captures
24✔
460
            printstyled_ll(io, bracket, :bracket, spaces)
48✔
461
            break # leave
24✔
462
        end
463

464
        m = match(r"^([^\s,*=(){}\[\]<>]+)(\s*)(.*)", tokens)
171✔
465
        m === nothing && break
171✔
466
        token, spaces, tokens = m.captures
171✔
467
        if occursin(llvm_types, token)
171✔
468
            printstyled_ll(io, token, :type)
52✔
469
            islabel = token == "label"
52✔
470
        elseif occursin(llvm_cond, token) # condition code is instruction-level
119✔
471
            printstyled_ll(io, token, :instruction)
1✔
472
        elseif occursin(num_regex, token)
118✔
473
            printstyled_ll(io, token, :number)
31✔
474
        elseif occursin(r"^@.+$", token)
87✔
475
            printstyled_ll(io, token, :funcname)
5✔
476
        elseif occursin(r"^%.+$", token)
82✔
477
            islabel |= occursin(r"^%[^\d].*$", token) & occursin(r"^\]", tokens)
38✔
478
            printstyled_ll(io, token, islabel ? :label : :variable)
38✔
479
            islabel = false
38✔
480
        elseif occursin(r"^[a-z]\w+$", token)
44✔
481
            printstyled_ll(io, token, :keyword)
39✔
482
        else
483
            printstyled_ll(io, token, :default)
5✔
484
        end
485
        print(io, spaces)
171✔
486
    end
214✔
487
    return tokens
108✔
488
end
489

490
function print_native(io::IO, code::String, arch::Symbol=sys_arch_category())
58✔
491
    archv = Val(arch)
59✔
492
    buf = IOBuffer(code)
58✔
493
    for line in eachline(buf)
116✔
494
        m = match(r"^(\s*)((?:[^;#/]|#\S|;\"|/[^/])*)(.*)$", line)
93✔
495
        m === nothing && continue
93✔
496
        indent, tokens, comment = m.captures
93✔
497
        print(io, indent)
186✔
498
        print_native_tokens(io, tokens, archv)
93✔
499
        printstyled_ll(io, comment, :comment)
186✔
500
        println(io)
93✔
501
    end
93✔
502
end
503

504
function sys_arch_category()
505
    if Sys.ARCH === :x86_64 || Sys.ARCH === :i686
1✔
506
        :x86
507
    elseif Sys.ARCH === :aarch64 || startswith(string(Sys.ARCH), "arm")
×
508
        :arm
509
    else
510
        :unsupported
511
    end
512
end
513

514
print_native_tokens(io, line, ::Val) = print(io, line)
1✔
515

516
const x86_ptr = r"^(?:(?:[xyz]mm|[dq])?word|byte|ptr|offset)$"
517
const avx512flags = r"^(?:z|r[nduz]-sae|sae|1to1?\d)$"
518
const arm_cond = r"^(?:eq|ne|cs|ho|cc|lo|mi|pl|vs|vc|hi|ls|[lg][te]|al|nv)$"
519
const arm_keywords = r"^(?:lsl|lsr|asr|ror|rrx|!|/[zm])$"
520

521
function print_native_tokens(io, tokens, arch::Union{Val{:x86}, Val{:arm}})
92✔
522
    x86 = arch isa Val{:x86}
92✔
523
    m = match(r"^((?:[^\s:]+:|\"[^\"]+\":)?)(\s*)(.*)", tokens)
92✔
524
    if m !== nothing
92✔
525
        label, spaces, tokens = m.captures
92✔
526
        printstyled_ll(io, label, :label, spaces)
184✔
527
    end
528
    haslabel = false
92✔
529
    m = match(r"^([a-z][\w.]*)(\s*)(.*)", tokens)
92✔
530
    if m !== nothing
92✔
531
        instruction, spaces, tokens = m.captures
57✔
532
        printstyled_ll(io, instruction, :instruction, spaces)
114✔
533
        haslabel = occursin(r"^(?:bl?|bl?\.\w{2,5}|[ct]bn?z)?$", instruction)
57✔
534
    end
535

536
    isfuncname = false
92✔
537
    while !isempty(tokens)
902✔
538
        m = match(r"^([,:*])(\s*)(.*)", tokens)
359✔
539
        if m !== nothing
359✔
540
            sym, spaces, tokens = m.captures
89✔
541
            printstyled_ll(io, sym, :default, spaces)
178✔
542
            isfuncname = false
89✔
543
            continue
89✔
544
        end
545
        m = match(r"^([(){}\[\]])(\s*)(.*)", tokens)
270✔
546
        if m !== nothing
270✔
547
            bracket, spaces, tokens = m.captures
56✔
548
            printstyled_ll(io, bracket, :bracket, spaces)
112✔
549
            continue
56✔
550
        end
551
        m = match(r"^#([0-9a-fx.-]+)(\s*)(.*)", tokens)
214✔
552
        if !x86 && m !== nothing && occursin(num_regex, m.captures[1])
214✔
553
            num, spaces, tokens = m.captures
6✔
554
            printstyled_ll(io, "#" * num, :number, spaces)
12✔
555
            continue
6✔
556
        end
557

558
        m = match(r"^([^\s,:*(){}\[\]][^\s,:*/(){}\[\]]*)(\s*)(.*)", tokens)
208✔
559
        m === nothing && break
208✔
560
        token, spaces, tokens = m.captures
208✔
561
        if occursin(num_regex, token)
208✔
562
            printstyled_ll(io, token, :number)
21✔
563
        elseif x86 && occursin(x86_ptr, token) || occursin(avx512flags, token)
314✔
564
            printstyled_ll(io, token, :keyword)
22✔
565
            isfuncname = token == "offset"
22✔
566
        elseif !x86 && (occursin(arm_keywords, token) || occursin(arm_cond, token))
204✔
567
            printstyled_ll(io, token, :keyword)
7✔
568
        elseif occursin(r"^L.+$", token)
158✔
569
            printstyled_ll(io, token, :label)
5✔
570
        elseif occursin(r"^\$.+$", token)
153✔
571
            printstyled_ll(io, token, :funcname)
2✔
572
        elseif occursin(r"^%?(?:[a-z][\w.]+|\"[^\"]+\")$", token)
151✔
573
            islabel = haslabel & !occursin(',', tokens)
190✔
574
            printstyled_ll(io, token, islabel ? :label : isfuncname ? :funcname : :variable)
230✔
575
            isfuncname = false
115✔
576
        else
577
            printstyled_ll(io, token, :default)
36✔
578
        end
579
        print(io, spaces)
208✔
580
    end
359✔
581
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc