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

JuliaLang / julia / 1576

09 Feb 2026 02:05PM UTC coverage: 75.058% (-1.7%) from 76.711%
1576

push

buildkite

web-flow
Supporting sticky (generational) immix (#57327)

Adding support for sticky immix. Needs to be merged after:

- https://github.com/JuliaLang/julia/pull/57237
- https://github.com/JuliaLang/julia/pull/57176
- https://github.com/JuliaLang/julia/pull/57252
- https://github.com/JuliaLang/julia/pull/57769

---------

Co-authored-by: Luis Eduardo de Souza Amorim <eduardo@bear.moma>

57362 of 76424 relevant lines covered (75.06%)

7438320.97 hits per line

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

96.58
/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)
190✔
30
            throw(ArgumentError("argument is not a generic function"))
2✔
31
        elseif f isa Core.OpaqueClosure
188✔
32
            return new(f, Base.to_tuple_type(t))
7✔
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)
17✔
41
        return new(nothing, tt)
11✔
42
    end
43
end
44

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

50
# displaying type warnings
51

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

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

92
function print_warntype_mi(io::IO, mi::Core.MethodInstance)
28✔
93
    println(io, mi)
28✔
94
    print(io, "  from ")
28✔
95
    println(io, mi.def)
28✔
96
    if !isempty(mi.sparam_vals)
28✔
97
        println(io, "Static Parameters")
5✔
98
        sig = mi.def.sig
5✔
99
        warn_color = Base.warn_color() # more mild user notification
5✔
100
        for i = 1:length(mi.sparam_vals)
5✔
101
            sig = sig::UnionAll
9✔
102
            name = sig.var.name
9✔
103
            val = mi.sparam_vals[i]
9✔
104
            print_highlighted(io::IO, v::String, color::Symbol) =
19✔
105
                if highlighting[:warntype]
106
                    Base.printstyled(io, v; color)
10✔
107
                else
108
                    Base.print(io, v)
×
109
                end
110
            if val isa TypeVar
9✔
111
                if val.lb === Union{}
3✔
112
                    print(io, "  ", name, " <: ")
1✔
113
                    print_highlighted(io, "$(val.ub)", warn_color)
1✔
114
                elseif val.ub === Any
2✔
115
                    print(io, "  ", sig.var.name, " >: ")
1✔
116
                    print_highlighted(io, "$(val.lb)", warn_color)
1✔
117
                else
118
                    print(io, "  ")
1✔
119
                    print_highlighted(io, "$(val.lb)", warn_color)
1✔
120
                    print(io, " <: ", sig.var.name, " <: ")
1✔
121
                    print_highlighted(io, "$(val.ub)", warn_color)
1✔
122
                end
123
            elseif val isa typeof(Vararg)
6✔
124
                print(io, "  ", name, "::")
1✔
125
                print_highlighted(io, "Int", warn_color)
1✔
126
            else
127
                print(io, "  ", sig.var.name, " = ")
5✔
128
                print_highlighted(io, "$(val)", :cyan) # show the "good" type
5✔
129
            end
130
            println(io)
9✔
131
            sig = sig.body
9✔
132
        end
9✔
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;
62✔
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)) &&
31✔
162
        error("code reflection cannot be used from generated functions")
163
    debuginfo = Base.IRShow.debuginfo(debuginfo)
31✔
164
    lineprinter = Base.IRShow.__debuginfo[debuginfo]
31✔
165
    nargs::Int = 0
31✔
166
    if arginfo.oc !== nothing
31✔
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; optimize, interp)[1]..., nargs;
3✔
170
                                lineprinter, label_dynamic_calls = optimize)
171
        return nothing
3✔
172
    end
173
    tt = arginfo.tt
28✔
174
    matches = findall(tt, Base.Compiler.method_table(interp))
28✔
175
    matches === nothing && Base.raise_match_failure(:code_warntype, tt)
28✔
176
    for match in matches.matches
28✔
177
        match = match::Core.MethodMatch
28✔
178
        src = Base.Compiler.typeinf_code(interp, match, optimize)
56✔
179
        mi = Base.Compiler.specialize_method(match)
28✔
180
        mi.def isa Method && (nargs = (mi.def::Method).nargs)
28✔
181
        print_warntype_mi(io, mi)
28✔
182
        if src isa Core.CodeInfo
28✔
183
            print_warntype_codeinfo(io, src, src.rettype, nargs;
28✔
184
                                    lineprinter, label_dynamic_calls = optimize)
185
        else
186
            println(io, "  inference not successful")
×
187
        end
188
    end
28✔
189
    nothing
28✔
190
end
191
code_warntype(io::IO, @nospecialize(f), @nospecialize(tt=Base.default_tt(f)); kwargs...) = code_warntype(io, ArgInfo(f, tt); kwargs...)
60✔
192
code_warntype(io::IO, @nospecialize(argtypes::Union{Tuple,Type{<:Tuple}}); kwargs...) = code_warntype(io, ArgInfo(argtypes); kwargs...)
4✔
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,
169✔
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")
169✔
212
    warning = ""
162✔
213
    # get the MethodInstance for the method match
214
    if arginfo.oc === nothing
162✔
215
        world = Base.get_world_counter()
158✔
216
        match = Base._which(arginfo.tt; world)
158✔
217
        mi = Base.specialize_method(match)
157✔
218
        # TODO: use jl_is_cacheable_sig instead of isdispatchtuple
219
        isdispatchtuple(mi.specTypes) || (warning = GENERIC_SIG_WARNING)
157✔
220
    else
221
        (; oc, tt) = arginfo
4✔
222
        world = UInt64(oc.world)
4✔
223
        if !isdefined(oc.source, :source)
4✔
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
4✔
227
            mi = oc.source.specializations::Core.MethodInstance
4✔
228
            Base.hasintersect(typeof(oc).parameters[1], tt) || (warning = OC_MISMATCH_WARNING)
4✔
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
161✔
236
        debuginfo = :source
113✔
237
    elseif debuginfo !== :source && debuginfo !== :none
48✔
238
        throw(ArgumentError("'debuginfo' must be either :source or :none"))
×
239
    end
240
    if native
161✔
241
        if syntax !== :att && syntax !== :intel
34✔
242
            throw(ArgumentError("'syntax' must be either :intel or :att"))
×
243
        end
244
        str = ""
34✔
245
        if !dump_module
34✔
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)
5✔
248
        end
249
        if isempty(str)
34✔
250
            # if that failed (or we want metadata), use LLVM to generate more accurate assembly output
251
            if arginfo.oc === nothing
29✔
252
                src = Base.Compiler.typeinf_code(Base.Compiler.NativeInterpreter(world), mi, true)
58✔
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")
29✔
257
            str = _dump_function_native_assembly(mi, src, wrapper, syntax, debuginfo, binary, raw, params)
29✔
258
        end
259
    else
260
        if arginfo.oc === nothing
127✔
261
            src = Base.Compiler.typeinf_code(Base.Compiler.NativeInterpreter(world), mi, true)
246✔
262
        else
263
            src, rt = Base.get_oc_code_rt(nothing, arginfo.oc, arginfo.tt, true)
4✔
264
        end
265
        src isa Core.CodeInfo || error("failed to infer source for $mi")
127✔
266
        str = _dump_function_llvm(mi, src, wrapper, !raw, dump_module, optimize, debuginfo, params)
127✔
267
    end
268
    str = warning * str
160✔
269
    return str
160✔
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,
5✔
276
                                    syntax::Ptr{UInt8}, debuginfo::Ptr{UInt8},
277
                                    binary::Bool)::Ref{String}
278
    return str
5✔
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,
29✔
287
                                        wrapper::Bool, syntax::Symbol, debuginfo::Symbol,
288
                                        binary::Bool, raw::Bool, params::CodegenParams)
289
    llvmf_dump = Ref{LLVMFDump}()
29✔
290
    @ccall jl_get_llvmf_defn(llvmf_dump::Ptr{LLVMFDump}, mi::Any, src::Any, wrapper::Bool,
29✔
291
                             true::Bool, params::CodegenParams)::Cvoid
292
    llvmf_dump[].f == C_NULL && error("could not compile the specified method")
29✔
293
    str = @ccall jl_dump_function_asm(llvmf_dump::Ptr{LLVMFDump}, false::Bool,
28✔
294
                                      syntax::Ptr{UInt8}, debuginfo::Ptr{UInt8},
295
                                      binary::Bool, raw::Bool)::Ref{String}
296
    return str
28✔
297
end
298

299
function _dump_function_llvm(
127✔
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}()
127✔
305
    @ccall jl_get_llvmf_defn(llvmf_dump::Ptr{LLVMFDump}, mi::Any, src::Any,
127✔
306
                             wrapper::Bool, optimize::Bool, params::CodegenParams)::Cvoid
307
    llvmf_dump[].f == C_NULL && error("could not compile the specified method")
127✔
308
    str = @ccall jl_dump_function_ir(llvmf_dump::Ptr{LLVMFDump}, strip_ir_metadata::Bool,
127✔
309
                                     dump_module::Bool, debuginfo::Ptr{UInt8})::Ref{String}
310
    return str
127✔
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;
102✔
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)
86✔
330
    if highlighting[:llvm] && get(io, :color, false)::Bool
83✔
331
        print_llvm(io, d)
2✔
332
    else
333
        print(io, d)
81✔
334
    end
335
end
336
code_llvm(io::IO, @nospecialize(argtypes::Union{Tuple,Type{<:Tuple}}); kwargs...) = code_llvm(io, ArgInfo(argtypes); kwargs...)
8✔
337
code_llvm(io::IO, @nospecialize(f), @nospecialize(types=Base.default_tt(f)); kwargs...) = code_llvm(io, ArgInfo(f, types); kwargs...)
168✔
338
code_llvm(args...; kwargs...) = (@nospecialize; code_llvm(stdout, args...; kwargs...))
12✔
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;
40✔
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)
36✔
359
    if highlighting[:native] && get(io, :color, false)::Bool
33✔
360
        print_native(io, d)
1✔
361
    else
362
        print(io, d)
32✔
363
    end
364
end
365
code_native(io::IO, @nospecialize(argtypes::Union{Tuple,Type{<:Tuple}}); kwargs...) = code_native(io, ArgInfo(argtypes); kwargs...)
10✔
366
code_native(io::IO, @nospecialize(f), @nospecialize(types=Base.default_tt(f)); kwargs...) = code_native(io, ArgInfo(f, types); kwargs...)
70✔
367
code_native(args...; kwargs...) = (@nospecialize; code_native(stdout, args...; kwargs...))
14✔
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)
37✔
374
    buf = IOBuffer(code)
37✔
375
    for line in eachline(buf)
74✔
376
        m = match(r"^(\s*)((?:[^;]|;\")*)(.*)$", line)
1,373✔
377
        m === nothing && continue
1,373✔
378
        indent, tokens, comment = m.captures
1,373✔
379
        print(io, indent)
2,746✔
380
        print_llvm_tokens(io, tokens)
2,746✔
381
        printstyled_ll(io, comment, :comment)
2,746✔
382
        println(io)
1,373✔
383
    end
1,373✔
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)
1,373✔
391
    m = match(r"^((?:[^\"\s:]+:|\"[^\"]*\":)?)(\s*)(.*)", tokens)
1,373✔
392
    if m !== nothing
1,373✔
393
        label, spaces, tokens = m.captures
1,373✔
394
        printstyled_ll(io, label, :label, spaces)
2,746✔
395
    end
396
    m = match(r"^(%[^\s=]+)(\s*)=(\s*)(.*)", tokens)
1,373✔
397
    if m !== nothing
1,373✔
398
        result, spaces, spaces2, tokens = m.captures
569✔
399
        printstyled_ll(io, result, :variable, spaces)
1,138✔
400
        printstyled_ll(io, '=', :default, spaces2)
1,138✔
401
    end
402
    m = match(r"^([a-z]\w*)(\s*)(.*)", tokens)
1,373✔
403
    if m !== nothing
1,373✔
404
        inst, spaces, tokens = m.captures
634✔
405
        iskeyword = occursin(r"^(?:define|declare|type)$", inst) || occursin("=", tokens)
1,263✔
406
        printstyled_ll(io, inst, iskeyword ? :keyword : :instruction, spaces)
1,268✔
407
    end
408

409
    print_llvm_operands(io, tokens)
1,373✔
410
end
411

412
function print_llvm_operands(io, tokens)
×
413
    while !isempty(tokens)
6,581✔
414
        tokens = print_llvm_operand(io, tokens)
2,801✔
415
    end
2,801✔
416
    return tokens
1,890✔
417
end
418

419
function print_llvm_operand(io, tokens)
2,801✔
420
    islabel = false
2,801✔
421
    while !isempty(tokens)
19,444✔
422
        m = match(r"^,(\s*)(.*)", tokens)
8,600✔
423
        if m !== nothing
8,600✔
424
            spaces, tokens = m.captures
1,162✔
425
            printstyled_ll(io, ',', :default, spaces)
2,324✔
426
            break
1,162✔
427
        end
428
        m = match(r"^(\*+|=)(\s*)(.*)", tokens)
7,438✔
429
        if m !== nothing
7,438✔
430
            sym, spaces, tokens = m.captures
16✔
431
            printstyled_ll(io, sym, :default, spaces)
32✔
432
            continue
16✔
433
        end
434
        m = match(r"^(\"[^\"]*\")(\s*)(.*)", tokens)
7,422✔
435
        if m !== nothing
7,422✔
436
            str, spaces, tokens = m.captures
3✔
437
            printstyled_ll(io, str, :variable, spaces)
6✔
438
            continue
3✔
439
        end
440
        m = match(r"^([({\[<])(\s*)(.*)", tokens)
7,419✔
441
        if m !== nothing
7,419✔
442
            bracket, spaces, tokens = m.captures
517✔
443
            printstyled_ll(io, bracket, :bracket, spaces)
1,034✔
444
            tokens = print_llvm_operands(io, tokens) # enter
1,034✔
445
            continue
517✔
446
        end
447
        m = match(r"^([)}\]>])(\s*)(.*)", tokens)
6,902✔
448
        if m !== nothing
6,902✔
449
            bracket, spaces, tokens = m.captures
517✔
450
            printstyled_ll(io, bracket, :bracket, spaces)
1,034✔
451
            break # leave
517✔
452
        end
453

454
        m = match(r"^([^\s,*=(){}\[\]<>]+)(\s*)(.*)", tokens)
6,385✔
455
        m === nothing && break
6,385✔
456
        token, spaces, tokens = m.captures
6,385✔
457
        if occursin(llvm_types, token)
6,385✔
458
            printstyled_ll(io, token, :type)
1,138✔
459
            islabel = token == "label"
1,138✔
460
        elseif occursin(llvm_cond, token) # condition code is instruction-level
5,247✔
461
            printstyled_ll(io, token, :instruction)
17✔
462
        elseif occursin(num_regex, token)
5,230✔
463
            printstyled_ll(io, token, :number)
1,494✔
464
        elseif occursin(r"^@.+$", token)
3,736✔
465
            printstyled_ll(io, token, :funcname)
18✔
466
        elseif occursin(r"^%.+$", token)
3,718✔
467
            islabel |= occursin(r"^%[^\d].*$", token) & occursin(r"^\]", tokens)
262✔
468
            printstyled_ll(io, token, islabel ? :label : :variable)
262✔
469
            islabel = false
262✔
470
        elseif occursin(r"^[a-z]\w+$", token)
3,456✔
471
            printstyled_ll(io, token, :keyword)
3,440✔
472
        else
473
            printstyled_ll(io, token, :default)
16✔
474
        end
475
        print(io, spaces)
6,385✔
476
    end
6,921✔
477
    return tokens
2,801✔
478
end
479

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

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

504
print_native_tokens(io, line, ::Val) = print(io, line)
1✔
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}})
98✔
512
    x86 = arch isa Val{:x86}
98✔
513
    m = match(r"^((?:[^\s:]+:|\"[^\"]+\":)?)(\s*)(.*)", tokens)
98✔
514
    if m !== nothing
98✔
515
        label, spaces, tokens = m.captures
98✔
516
        printstyled_ll(io, label, :label, spaces)
196✔
517
    end
518
    haslabel = false
98✔
519
    m = match(r"^([a-z][\w.]*)(\s*)(.*)", tokens)
98✔
520
    if m !== nothing
98✔
521
        instruction, spaces, tokens = m.captures
58✔
522
        printstyled_ll(io, instruction, :instruction, spaces)
116✔
523
        haslabel = occursin(r"^(?:bl?|bl?\.\w{2,5}|[ct]bn?z)?$", instruction)
58✔
524
    end
525

526
    isfuncname = false
98✔
527
    while !isempty(tokens)
942✔
528
        m = match(r"^([,:*])(\s*)(.*)", tokens)
373✔
529
        if m !== nothing
373✔
530
            sym, spaces, tokens = m.captures
91✔
531
            printstyled_ll(io, sym, :default, spaces)
182✔
532
            isfuncname = false
91✔
533
            continue
91✔
534
        end
535
        m = match(r"^([(){}\[\]])(\s*)(.*)", tokens)
282✔
536
        if m !== nothing
282✔
537
            bracket, spaces, tokens = m.captures
56✔
538
            printstyled_ll(io, bracket, :bracket, spaces)
112✔
539
            continue
56✔
540
        end
541
        m = match(r"^#([0-9a-fx.-]+)(\s*)(.*)", tokens)
226✔
542
        if !x86 && m !== nothing && occursin(num_regex, m.captures[1])
226✔
543
            num, spaces, tokens = m.captures
6✔
544
            printstyled_ll(io, "#" * num, :number, spaces)
12✔
545
            continue
6✔
546
        end
547

548
        m = match(r"^([^\s,:*(){}\[\]][^\s,:*/(){}\[\]]*)(\s*)(.*)", tokens)
220✔
549
        m === nothing && break
220✔
550
        token, spaces, tokens = m.captures
220✔
551
        if occursin(num_regex, token)
220✔
552
            printstyled_ll(io, token, :number)
23✔
553
        elseif x86 && occursin(x86_ptr, token) || occursin(avx512flags, token)
334✔
554
            printstyled_ll(io, token, :keyword)
22✔
555
            isfuncname = token == "offset"
22✔
556
        elseif !x86 && (occursin(arm_keywords, token) || occursin(arm_cond, token))
214✔
557
            printstyled_ll(io, token, :keyword)
7✔
558
        elseif occursin(r"^L.+$", token)
168✔
559
            printstyled_ll(io, token, :label)
5✔
560
        elseif occursin(r"^\$.+$", token)
163✔
561
            printstyled_ll(io, token, :funcname)
2✔
562
        elseif occursin(r"^%?(?:[a-z][\w.]+|\"[^\"]+\")$", token)
161✔
563
            islabel = haslabel & !occursin(',', tokens)
197✔
564
            printstyled_ll(io, token, islabel ? :label : isfuncname ? :funcname : :variable)
240✔
565
            isfuncname = false
120✔
566
        else
567
            printstyled_ll(io, token, :default)
41✔
568
        end
569
        print(io, spaces)
220✔
570
    end
373✔
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