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

JuliaLang / julia / 1572

01 Feb 2026 09:55PM UTC coverage: 76.677% (-0.07%) from 76.749%
1572

push

buildkite

web-flow
docs: clarify 'using A, B' semantics (#60856)

Resolves #36090

62889 of 82018 relevant lines covered (76.68%)

23269256.38 hits per line

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

96.89
/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))
3✔
29
        if isa(f, Core.Builtin)
549✔
30
            throw(ArgumentError("argument is not a generic function"))
6✔
31
        elseif f isa Core.OpaqueClosure
543✔
32
            return new(f, Base.to_tuple_type(t))
15✔
33
        else
34
            return new(nothing, signature_type(f, t))
528✔
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)
51✔
41
        return new(nothing, tt)
33✔
42
    end
43
end
44

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

50
# displaying type warnings
51

52
function warntype_type_printer(io::IO; @nospecialize(type), used::Bool, show_type::Bool=true, _...)
2,451✔
53
    (show_type && used) || return nothing
1,818✔
54
    str = "::$type"
1,080✔
55
    if !highlighting[:warntype]
1,080✔
56
        print(io, str)
18✔
57
    elseif type isa Union && Base.Compiler.IRShow.is_expected_union(type)
1,062✔
58
        Base.emphasize(io, str, Base.warn_color()) # more mild user notification
45✔
59
    elseif type isa Type && (!Base.isdispatchelem(type) || type == Core.Box)
1,629✔
60
        Base.emphasize(io, str)
21✔
61
    else
62
        Base.printstyled(io, str, color=:cyan) # show the "good" type
996✔
63
    end
64
    return nothing
1,080✔
65
end
66

67
function print_warntype_codeinfo(io::IO, src::Core.CodeInfo, @nospecialize(rettype), nargs::Int; lineprinter, label_dynamic_calls)
174✔
68
    if src.slotnames !== nothing
87✔
69
        slotnames = Base.sourceinfo_slotnames(src)
87✔
70
        io = IOContext(io, :SOURCE_SLOTNAMES => slotnames)
87✔
71
        slottypes = src.slottypes
87✔
72
        nargs > 0 && println(io, "Arguments")
87✔
73
        for i = 1:length(slotnames)
87✔
74
            if i == nargs + 1
267✔
75
                println(io, "Locals")
21✔
76
            end
77
            print(io, "  ", slotnames[i])
267✔
78
            if isa(slottypes, Vector{Any})
267✔
79
                warntype_type_printer(io; type=slottypes[i], used=true)
267✔
80
            end
81
            println(io)
267✔
82
        end
447✔
83
    end
84
    print(io, "Body")
87✔
85
    warntype_type_printer(io; type=rettype, used=true)
87✔
86
    println(io)
87✔
87
    irshow_config = Base.IRShow.IRShowConfig(lineprinter(src), warntype_type_printer; label_dynamic_calls)
87✔
88
    Base.IRShow.show_ir(io, src, irshow_config)
87✔
89
    println(io)
87✔
90
end
91

92
function print_warntype_mi(io::IO, mi::Core.MethodInstance)
84✔
93
    println(io, mi)
84✔
94
    print(io, "  from ")
84✔
95
    println(io, mi.def)
84✔
96
    if !isempty(mi.sparam_vals)
84✔
97
        println(io, "Static Parameters")
15✔
98
        sig = mi.def.sig
15✔
99
        warn_color = Base.warn_color() # more mild user notification
15✔
100
        for i = 1:length(mi.sparam_vals)
15✔
101
            sig = sig::UnionAll
27✔
102
            name = sig.var.name
27✔
103
            val = mi.sparam_vals[i]
27✔
104
            print_highlighted(io::IO, v::String, color::Symbol) =
57✔
105
                if highlighting[:warntype]
106
                    Base.printstyled(io, v; color)
30✔
107
                else
108
                    Base.print(io, v)
×
109
                end
110
            if val isa TypeVar
27✔
111
                if val.lb === Union{}
9✔
112
                    print(io, "  ", name, " <: ")
3✔
113
                    print_highlighted(io, "$(val.ub)", warn_color)
3✔
114
                elseif val.ub === Any
6✔
115
                    print(io, "  ", sig.var.name, " >: ")
3✔
116
                    print_highlighted(io, "$(val.lb)", warn_color)
3✔
117
                else
118
                    print(io, "  ")
3✔
119
                    print_highlighted(io, "$(val.lb)", warn_color)
3✔
120
                    print(io, " <: ", sig.var.name, " <: ")
3✔
121
                    print_highlighted(io, "$(val.ub)", warn_color)
3✔
122
                end
123
            elseif val isa typeof(Vararg)
18✔
124
                print(io, "  ", name, "::")
3✔
125
                print_highlighted(io, "Int", warn_color)
3✔
126
            else
127
                print(io, "  ", sig.var.name, " = ")
15✔
128
                print_highlighted(io, "$(val)", :cyan) # show the "good" type
15✔
129
            end
130
            println(io)
27✔
131
            sig = sig.body
27✔
132
        end
27✔
133
    end
134
end
135

136
"""
137
    code_warntype([io::IO], f, types; debuginfo=:default)
138

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

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

150
Keyword argument `debuginfo` may be one of `:source`, `:none` or `:default`, to specify the verbosity of code comments.
151
Unless the user changes `Base.IRShow.default_debuginfo[]`, the value `:default` is equivalent to `:source`.
152

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

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

196
using Base: CodegenParams
197

198
const GENERIC_SIG_WARNING = "; WARNING: This code may not match what actually runs.\n"
199
const OC_MISMATCH_WARNING =
200
"""
201
; WARNING: The pre-inferred opaque closure is not callable with the given arguments
202
;          and will error on dispatch with this signature.
203
"""
204

205
# Printing code representations in IR and assembly
206

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

272
function _dump_function_native_disassembly(mi::Core.MethodInstance, world::UInt,
273
                                           wrapper::Bool, syntax::Symbol,
274
                                           debuginfo::Symbol, binary::Bool)
275
    str = @ccall jl_dump_method_asm(mi::Any, world::UInt, false::Bool, wrapper::Bool,
11✔
276
                                    syntax::Ptr{UInt8}, debuginfo::Ptr{UInt8},
277
                                    binary::Bool)::Ref{String}
278
    return str
11✔
279
end
280

281
struct LLVMFDump
282
    tsm::Ptr{Cvoid} # opaque
283
    f::Ptr{Cvoid} # opaque
284
end
285

286
function _dump_function_native_assembly(mi::Core.MethodInstance, src::Core.CodeInfo,
84✔
287
                                        wrapper::Bool, syntax::Symbol, debuginfo::Symbol,
288
                                        binary::Bool, raw::Bool, params::CodegenParams)
289
    llvmf_dump = Ref{LLVMFDump}()
84✔
290
    @ccall jl_get_llvmf_defn(llvmf_dump::Ptr{LLVMFDump}, mi::Any, src::Any, wrapper::Bool,
84✔
291
                             true::Bool, params::CodegenParams)::Cvoid
292
    llvmf_dump[].f == C_NULL && error("could not compile the specified method")
84✔
293
    str = @ccall jl_dump_function_asm(llvmf_dump::Ptr{LLVMFDump}, false::Bool,
81✔
294
                                      syntax::Ptr{UInt8}, debuginfo::Ptr{UInt8},
295
                                      binary::Bool, raw::Bool)::Ref{String}
296
    return str
81✔
297
end
298

299
function _dump_function_llvm(
373✔
300
        mi::Core.MethodInstance, src::Core.CodeInfo, wrapper::Bool,
301
        strip_ir_metadata::Bool, dump_module::Bool,
302
        optimize::Bool, debuginfo::Symbol,
303
        params::CodegenParams)
304
    llvmf_dump = Ref{LLVMFDump}()
373✔
305
    @ccall jl_get_llvmf_defn(llvmf_dump::Ptr{LLVMFDump}, mi::Any, src::Any,
373✔
306
                             wrapper::Bool, optimize::Bool, params::CodegenParams)::Cvoid
307
    llvmf_dump[].f == C_NULL && error("could not compile the specified method")
373✔
308
    str = @ccall jl_dump_function_ir(llvmf_dump::Ptr{LLVMFDump}, strip_ir_metadata::Bool,
373✔
309
                                     dump_module::Bool, debuginfo::Ptr{UInt8})::Ref{String}
310
    return str
373✔
311
end
312

313
"""
314
    code_llvm([io=stdout,], f, types; raw=false, dump_module=false, optimize=true, debuginfo=:default)
315

316
Prints the LLVM bitcodes generated for running the method matching the given generic
317
function and type signature to `io`.
318

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

324
See also: [`@code_llvm`](@ref), [`code_warntype`](@ref), [`code_typed`](@ref), [`code_lowered`](@ref), [`code_native`](@ref).
325
"""
326
function code_llvm(io::IO, arginfo::ArgInfo;
292✔
327
                   raw::Bool=false, dump_module::Bool=false, optimize::Bool=true, debuginfo::Symbol=:default,
328
                   params::CodegenParams=CodegenParams(debug_info_kind=Cint(0), debug_info_level=Cint(2), safepoint_on_entry=raw, gcstack_arg=raw))
329
    d = _dump_function(arginfo, false, false, raw, dump_module, :intel, optimize, debuginfo, false, params)
250✔
330
    if highlighting[:llvm] && get(io, :color, false)::Bool
241✔
331
        print_llvm(io, d)
6✔
332
    else
333
        print(io, d)
235✔
334
    end
335
end
336
code_llvm(io::IO, @nospecialize(argtypes::Union{Tuple,Type{<:Tuple}}); kwargs...) = code_llvm(io, ArgInfo(argtypes); kwargs...)
24✔
337
code_llvm(io::IO, @nospecialize(f), @nospecialize(types=Base.default_tt(f)); kwargs...) = code_llvm(io, ArgInfo(f, types); kwargs...)
488✔
338
code_llvm(args...; kwargs...) = (@nospecialize; code_llvm(stdout, args...; kwargs...))
36✔
339

340
"""
341
    code_native([io=stdout,], f, types; syntax=:intel, debuginfo=:default, binary=false, dump_module=true, raw=false)
342

343
Prints the native assembly instructions generated for running the method matching the given
344
generic function and type signature to `io`.
345

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

352
See also: [`@code_native`](@ref), [`code_warntype`](@ref), [`code_typed`](@ref), [`code_lowered`](@ref), [`code_llvm`](@ref).
353
"""
354
function code_native(io::IO, arginfo::ArgInfo;
113✔
355
                     dump_module::Bool=true, syntax::Symbol=:intel, raw::Bool=false,
356
                     debuginfo::Symbol=:default, binary::Bool=false,
357
                     params::CodegenParams=CodegenParams(debug_info_kind=Cint(0), debug_info_level=Cint(2), safepoint_on_entry=raw, gcstack_arg=raw))
358
    d = _dump_function(arginfo, true, false, raw, dump_module, syntax, true, debuginfo, binary, params)
101✔
359
    if highlighting[:native] && get(io, :color, false)::Bool
92✔
360
        print_native(io, d)
3✔
361
    else
362
        print(io, d)
89✔
363
    end
364
end
365
code_native(io::IO, @nospecialize(argtypes::Union{Tuple,Type{<:Tuple}}); kwargs...) = code_native(io, ArgInfo(argtypes); kwargs...)
30✔
366
code_native(io::IO, @nospecialize(f), @nospecialize(types=Base.default_tt(f)); kwargs...) = code_native(io, ArgInfo(f, types); kwargs...)
196✔
367
code_native(args...; kwargs...) = (@nospecialize; code_native(stdout, args...; kwargs...))
42✔
368

369
## colorized IR and assembly printing
370

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

373
function print_llvm(io::IO, code::String)
111✔
374
    buf = IOBuffer(code)
111✔
375
    for line in eachline(buf)
222✔
376
        m = match(r"^(\s*)((?:[^;]|;\")*)(.*)$", line)
4,099✔
377
        m === nothing && continue
4,099✔
378
        indent, tokens, comment = m.captures
4,099✔
379
        print(io, indent)
8,198✔
380
        print_llvm_tokens(io, tokens)
8,198✔
381
        printstyled_ll(io, comment, :comment)
8,198✔
382
        println(io)
4,099✔
383
    end
4,099✔
384
end
385

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

390
function print_llvm_tokens(io, tokens)
4,099✔
391
    m = match(r"^((?:[^\"\s:]+:|\"[^\"]*\":)?)(\s*)(.*)", tokens)
4,099✔
392
    if m !== nothing
4,099✔
393
        label, spaces, tokens = m.captures
4,099✔
394
        printstyled_ll(io, label, :label, spaces)
8,198✔
395
    end
396
    m = match(r"^(%[^\s=]+)(\s*)=(\s*)(.*)", tokens)
4,099✔
397
    if m !== nothing
4,099✔
398
        result, spaces, spaces2, tokens = m.captures
1,706✔
399
        printstyled_ll(io, result, :variable, spaces)
3,412✔
400
        printstyled_ll(io, '=', :default, spaces2)
3,412✔
401
    end
402
    m = match(r"^([a-z]\w*)(\s*)(.*)", tokens)
4,099✔
403
    if m !== nothing
4,099✔
404
        inst, spaces, tokens = m.captures
1,901✔
405
        iskeyword = occursin(r"^(?:define|declare|type)$", inst) || occursin("=", tokens)
3,787✔
406
        printstyled_ll(io, inst, iskeyword ? :keyword : :instruction, spaces)
3,802✔
407
    end
408

409
    print_llvm_operands(io, tokens)
4,099✔
410
end
411

412
function print_llvm_operands(io, tokens)
×
413
    while !isempty(tokens)
19,698✔
414
        tokens = print_llvm_operand(io, tokens)
8,400✔
415
    end
8,400✔
416
    return tokens
5,649✔
417
end
418

419
function print_llvm_operand(io, tokens)
8,400✔
420
    islabel = false
8,400✔
421
    while !isempty(tokens)
58,274✔
422
        m = match(r"^,(\s*)(.*)", tokens)
25,774✔
423
        if m !== nothing
25,774✔
424
            spaces, tokens = m.captures
3,487✔
425
            printstyled_ll(io, ',', :default, spaces)
6,974✔
426
            break
3,487✔
427
        end
428
        m = match(r"^(\*+|=)(\s*)(.*)", tokens)
22,287✔
429
        if m !== nothing
22,287✔
430
            sym, spaces, tokens = m.captures
48✔
431
            printstyled_ll(io, sym, :default, spaces)
96✔
432
            continue
48✔
433
        end
434
        m = match(r"^(\"[^\"]*\")(\s*)(.*)", tokens)
22,239✔
435
        if m !== nothing
22,239✔
436
            str, spaces, tokens = m.captures
11✔
437
            printstyled_ll(io, str, :variable, spaces)
22✔
438
            continue
11✔
439
        end
440
        m = match(r"^([({\[<])(\s*)(.*)", tokens)
22,228✔
441
        if m !== nothing
22,228✔
442
            bracket, spaces, tokens = m.captures
1,550✔
443
            printstyled_ll(io, bracket, :bracket, spaces)
3,100✔
444
            tokens = print_llvm_operands(io, tokens) # enter
3,100✔
445
            continue
1,550✔
446
        end
447
        m = match(r"^([)}\]>])(\s*)(.*)", tokens)
20,678✔
448
        if m !== nothing
20,678✔
449
            bracket, spaces, tokens = m.captures
1,550✔
450
            printstyled_ll(io, bracket, :bracket, spaces)
3,100✔
451
            break # leave
1,550✔
452
        end
453

454
        m = match(r"^([^\s,*=(){}\[\]<>]+)(\s*)(.*)", tokens)
19,128✔
455
        m === nothing && break
19,128✔
456
        token, spaces, tokens = m.captures
19,128✔
457
        if occursin(llvm_types, token)
19,128✔
458
            printstyled_ll(io, token, :type)
3,410✔
459
            islabel = token == "label"
3,410✔
460
        elseif occursin(llvm_cond, token) # condition code is instruction-level
15,718✔
461
            printstyled_ll(io, token, :instruction)
51✔
462
        elseif occursin(num_regex, token)
15,667✔
463
            printstyled_ll(io, token, :number)
4,473✔
464
        elseif occursin(r"^@.+$", token)
11,194✔
465
            printstyled_ll(io, token, :funcname)
54✔
466
        elseif occursin(r"^%.+$", token)
11,140✔
467
            islabel |= occursin(r"^%[^\d].*$", token) & occursin(r"^\]", tokens)
791✔
468
            printstyled_ll(io, token, islabel ? :label : :variable)
791✔
469
            islabel = false
791✔
470
        elseif occursin(r"^[a-z]\w+$", token)
10,349✔
471
            printstyled_ll(io, token, :keyword)
10,301✔
472
        else
473
            printstyled_ll(io, token, :default)
48✔
474
        end
475
        print(io, spaces)
19,128✔
476
    end
20,737✔
477
    return tokens
8,400✔
478
end
479

480
function print_native(io::IO, code::String, arch::Symbol=sys_arch_category())
174✔
481
    archv = Val(arch)
177✔
482
    buf = IOBuffer(code)
174✔
483
    for line in eachline(buf)
348✔
484
        m = match(r"^(\s*)((?:[^;#/]|#\S|;\"|/[^/])*)(.*)$", line)
278✔
485
        m === nothing && continue
278✔
486
        indent, tokens, comment = m.captures
278✔
487
        print(io, indent)
556✔
488
        print_native_tokens(io, tokens, archv)
278✔
489
        printstyled_ll(io, comment, :comment)
556✔
490
        println(io)
278✔
491
    end
278✔
492
end
493

494
function sys_arch_category()
495
    if Sys.ARCH === :x86_64 || Sys.ARCH === :i686
3✔
496
        :x86
497
    elseif Sys.ARCH === :aarch64 || startswith(string(Sys.ARCH), "arm")
1✔
498
        :arm
499
    else
500
        :unsupported
501
    end
502
end
503

504
print_native_tokens(io, line, ::Val) = print(io, line)
3✔
505

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

511
function print_native_tokens(io, tokens, arch::Union{Val{:x86}, Val{:arm}})
275✔
512
    x86 = arch isa Val{:x86}
275✔
513
    m = match(r"^((?:[^\s:]+:|\"[^\"]+\":)?)(\s*)(.*)", tokens)
275✔
514
    if m !== nothing
275✔
515
        label, spaces, tokens = m.captures
275✔
516
        printstyled_ll(io, label, :label, spaces)
550✔
517
    end
518
    haslabel = false
275✔
519
    m = match(r"^([a-z][\w.]*)(\s*)(.*)", tokens)
275✔
520
    if m !== nothing
275✔
521
        instruction, spaces, tokens = m.captures
174✔
522
        printstyled_ll(io, instruction, :instruction, spaces)
348✔
523
        haslabel = occursin(r"^(?:bl?|bl?\.\w{2,5}|[ct]bn?z)?$", instruction)
174✔
524
    end
525

526
    isfuncname = false
275✔
527
    while !isempty(tokens)
2,706✔
528
        m = match(r"^([,:*])(\s*)(.*)", tokens)
1,078✔
529
        if m !== nothing
1,078✔
530
            sym, spaces, tokens = m.captures
271✔
531
            printstyled_ll(io, sym, :default, spaces)
542✔
532
            isfuncname = false
271✔
533
            continue
271✔
534
        end
535
        m = match(r"^([(){}\[\]])(\s*)(.*)", tokens)
807✔
536
        if m !== nothing
807✔
537
            bracket, spaces, tokens = m.captures
170✔
538
            printstyled_ll(io, bracket, :bracket, spaces)
340✔
539
            continue
170✔
540
        end
541
        m = match(r"^#([0-9a-fx.-]+)(\s*)(.*)", tokens)
637✔
542
        if !x86 && m !== nothing && occursin(num_regex, m.captures[1])
637✔
543
            num, spaces, tokens = m.captures
26✔
544
            printstyled_ll(io, "#" * num, :number, spaces)
52✔
545
            continue
26✔
546
        end
547

548
        m = match(r"^([^\s,:*(){}\[\]][^\s,:*/(){}\[\]]*)(\s*)(.*)", tokens)
611✔
549
        m === nothing && break
611✔
550
        token, spaces, tokens = m.captures
611✔
551
        if occursin(num_regex, token)
611✔
552
            printstyled_ll(io, token, :number)
59✔
553
        elseif x86 && occursin(x86_ptr, token) || occursin(avx512flags, token)
890✔
554
            printstyled_ll(io, token, :keyword)
61✔
555
            isfuncname = token == "offset"
61✔
556
        elseif !x86 && (occursin(arm_keywords, token) || occursin(arm_cond, token))
644✔
557
            printstyled_ll(io, token, :keyword)
24✔
558
        elseif occursin(r"^L.+$", token)
467✔
559
            printstyled_ll(io, token, :label)
15✔
560
        elseif occursin(r"^\$.+$", token)
452✔
561
            printstyled_ll(io, token, :funcname)
5✔
562
        elseif occursin(r"^%?(?:[a-z][\w.]+|\"[^\"]+\")$", token)
447✔
563
            islabel = haslabel & !occursin(',', tokens)
582✔
564
            printstyled_ll(io, token, islabel ? :label : isfuncname ? :funcname : :variable)
704✔
565
            isfuncname = false
352✔
566
        else
567
            printstyled_ll(io, token, :default)
95✔
568
        end
569
        print(io, spaces)
611✔
570
    end
1,078✔
571
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