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

JuliaLang / julia / #37710

08 Mar 2024 09:44PM UTC coverage: 85.122% (-1.6%) from 86.673%
#37710

push

local

web-flow
optimize remset marking (#52476)

Tag the lowest bit of a pointer to indicate it's in the remset and
enqueue objects in the remset for later processing when GC threads have
woken up, instead of sequentially marking them all at once.

In principle, this should allow for more parallelism in the mark phase,
though I didn't benchmark it yet.

74862 of 87947 relevant lines covered (85.12%)

14553233.03 hits per line

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

32.57
/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
function printstyled_ll(io::IO, x, s::Symbol, trailing_spaces="")
×
24
    printstyled(io, x, bold=llstyle[s][1], color=llstyle[s][2])
×
25
    print(io, trailing_spaces)
×
26
end
27

28
# displaying type warnings
29

30
function warntype_type_printer(io::IO; @nospecialize(type), used::Bool, show_type::Bool=true, _...)
216✔
31
    (show_type && used) || return nothing
141✔
32
    str = "::$type"
75✔
33
    if !highlighting[:warntype]
75✔
34
        print(io, str)
×
35
    elseif type isa Union && is_expected_union(type)
75✔
36
        Base.emphasize(io, str, Base.warn_color()) # more mild user notification
12✔
37
    elseif type isa Type && (!Base.isdispatchelem(type) || type == Core.Box)
116✔
38
        Base.emphasize(io, str)
×
39
    else
40
        Base.printstyled(io, str, color=:cyan) # show the "good" type
63✔
41
    end
42
    return nothing
75✔
43
end
44

45
# True if one can be pretty certain that the compiler handles this union well,
46
# i.e. must be small with concrete types.
47
function is_expected_union(u::Union)
12✔
48
    Base.unionlen(u) < 4 || return false
12✔
49
    for x in Base.uniontypes(u)
12✔
50
        if !Base.isdispatchelem(x) || x == Core.Box
48✔
51
            return false
×
52
        end
53
    end
24✔
54
    return true
12✔
55
end
56

57
function print_warntype_codeinfo(io::IO, src::Core.CodeInfo, @nospecialize(rettype), nargs::Int; lineprinter)
4✔
58
    if src.slotnames !== nothing
2✔
59
        slotnames = Base.sourceinfo_slotnames(src)
2✔
60
        io = IOContext(io, :SOURCE_SLOTNAMES => slotnames)
2✔
61
        slottypes = src.slottypes
2✔
62
        nargs > 0 && println(io, "Arguments")
2✔
63
        for i = 1:length(slotnames)
2✔
64
            if i == nargs + 1
12✔
65
                println(io, "Locals")
2✔
66
            end
67
            print(io, "  ", slotnames[i])
12✔
68
            if isa(slottypes, Vector{Any})
12✔
69
                warntype_type_printer(io; type=slottypes[i], used=true)
12✔
70
            end
71
            println(io)
24✔
72
        end
22✔
73
    end
74
    print(io, "Body")
2✔
75
    warntype_type_printer(io; type=rettype, used=true)
2✔
76
    println(io)
4✔
77
    irshow_config = Base.IRShow.IRShowConfig(lineprinter(src), warntype_type_printer)
2✔
78
    Base.IRShow.show_ir(io, src, irshow_config)
2✔
79
    println(io)
2✔
80
end
81

82
function print_warntype_mi(io::IO, mi::Core.MethodInstance)
2✔
83
    println(io, mi)
2✔
84
    print(io, "  from ")
2✔
85
    println(io, mi.def)
2✔
86
    if !isempty(mi.sparam_vals)
2✔
87
        println(io, "Static Parameters")
×
88
        sig = mi.def.sig
×
89
        warn_color = Base.warn_color() # more mild user notification
×
90
        for i = 1:length(mi.sparam_vals)
×
91
            sig = sig::UnionAll
×
92
            name = sig.var.name
×
93
            val = mi.sparam_vals[i]
×
94
            print_highlighted(io::IO, v::String, color::Symbol) =
×
95
                if highlighting[:warntype]
96
                    Base.printstyled(io, v; color)
×
97
                else
98
                    Base.print(io, v)
×
99
                end
100
            if val isa TypeVar
×
101
                if val.lb === Union{}
×
102
                    print(io, "  ", name, " <: ")
×
103
                    print_highlighted(io, "$(val.ub)", warn_color)
×
104
                elseif val.ub === Any
×
105
                    print(io, "  ", sig.var.name, " >: ")
×
106
                    print_highlighted(io, "$(val.lb)", warn_color)
×
107
                else
108
                    print(io, "  ")
×
109
                    print_highlighted(io, "$(val.lb)", warn_color)
×
110
                    print(io, " <: ", sig.var.name, " <: ")
×
111
                    print_highlighted(io, "$(val.ub)", warn_color)
×
112
                end
113
            elseif val isa typeof(Vararg)
×
114
                print(io, "  ", name, "::")
×
115
                print_highlighted(io, "Int", warn_color)
×
116
            else
117
                print(io, "  ", sig.var.name, " = ")
×
118
                print_highlighted(io, "$(val)", :cyan) # show the "good" type
×
119
            end
120
            println(io)
×
121
            sig = sig.body
×
122
        end
×
123
    end
124
end
125

126
"""
127
    code_warntype([io::IO], f, types; debuginfo=:default)
128

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

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

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

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

144
See also: [`@code_warntype`](@ref), [`code_typed`](@ref), [`code_lowered`](@ref), [`code_llvm`](@ref), [`code_native`](@ref).
145
"""
146
function code_warntype(io::IO, @nospecialize(f), @nospecialize(t=Base.default_tt(f));
4✔
147
                       world=Base.get_world_counter(),
148
                       interp::Core.Compiler.AbstractInterpreter=Core.Compiler.NativeInterpreter(world),
149
                       debuginfo::Symbol=:default, optimize::Bool=false, kwargs...)
150
    (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) &&
2✔
151
        error("code reflection cannot be used from generated functions")
152
    debuginfo = Base.IRShow.debuginfo(debuginfo)
2✔
153
    lineprinter = Base.IRShow.__debuginfo[debuginfo]
2✔
154
    nargs::Int = 0
2✔
155
    if isa(f, Core.OpaqueClosure)
2✔
156
        isa(f.source, Method) && (nargs = f.nargs)
×
157
        print_warntype_codeinfo(io, Base.code_typed_opaque_closure(f)[1]..., nargs; lineprinter)
×
158
        return nothing
×
159
    end
160
    matches = Base._methods_by_ftype(Base.signature_type(f, t), #=lim=#-1, world)::Vector
2✔
161
    for match in matches
2✔
162
        match = match::Core.MethodMatch
2✔
163
        (src, rettype) = Core.Compiler.typeinf_code(interp, match, optimize)
2✔
164
        mi = Core.Compiler.specialize_method(match)
2✔
165
        mi.def isa Method && (nargs = (mi.def::Method).nargs)
2✔
166
        print_warntype_mi(io, mi)
2✔
167
        print_warntype_codeinfo(io, src, rettype, nargs; lineprinter)
2✔
168
    end
2✔
169
    nothing
2✔
170
end
171
code_warntype(args...; kwargs...) = (@nospecialize; code_warntype(stdout, args...; kwargs...))
×
172

173
using Base: CodegenParams
174

175
const GENERIC_SIG_WARNING = "; WARNING: This code may not match what actually runs.\n"
176
const OC_MISMATCH_WARNING =
177
"""
178
; WARNING: The pre-inferred opaque closure is not callable with the given arguments
179
;          and will error on dispatch with this signature.
180
"""
181

182
# Printing code representations in IR and assembly
183

184
function _dump_function(@nospecialize(f), @nospecialize(t), native::Bool, wrapper::Bool,
73✔
185
                        raw::Bool, dump_module::Bool, syntax::Symbol,
186
                        optimize::Bool, debuginfo::Symbol, binary::Bool,
187
                        params::CodegenParams=CodegenParams(debug_info_kind=Cint(0), debug_info_level=Cint(2), safepoint_on_entry=raw, gcstack_arg=raw))
188
    ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions")
73✔
189
    if isa(f, Core.Builtin)
68✔
190
        throw(ArgumentError("argument is not a generic function"))
×
191
    end
192
    warning = ""
68✔
193
    # get the MethodInstance for the method match
194
    if !isa(f, Core.OpaqueClosure)
68✔
195
        world = Base.get_world_counter()
66✔
196
        match = Base._which(signature_type(f, t); world)
66✔
197
        mi = Core.Compiler.specialize_method(match)
66✔
198
        # TODO: use jl_is_cacheable_sig instead of isdispatchtuple
199
        isdispatchtuple(mi.specTypes) || (warning = GENERIC_SIG_WARNING)
66✔
200
    else
201
        world = UInt64(f.world)
2✔
202
        tt = Base.to_tuple_type(t)
2✔
203
        if !isdefined(f.source, :source)
2✔
204
            # OC was constructed from inferred source. There's only one
205
            # specialization and we can't infer anything more precise either.
206
            world = f.source.primary_world
2✔
207
            mi = f.source.specializations::Core.MethodInstance
2✔
208
            Core.Compiler.hasintersect(typeof(f).parameters[1], tt) || (warning = OC_MISMATCH_WARNING)
2✔
209
        else
210
            mi = Core.Compiler.specialize_method(f.source, Tuple{typeof(f.captures), tt.parameters...}, Core.svec())
×
211
            actual = isdispatchtuple(mi.specTypes)
×
212
            isdispatchtuple(mi.specTypes) || (warning = GENERIC_SIG_WARNING)
×
213
        end
214
    end
215
    # get the code for it
216
    if debuginfo === :default
68✔
217
        debuginfo = :source
27✔
218
    elseif debuginfo !== :source && debuginfo !== :none
41✔
219
        throw(ArgumentError("'debuginfo' must be either :source or :none"))
×
220
    end
221
    if native
68✔
222
        if syntax !== :att && syntax !== :intel
1✔
223
            throw(ArgumentError("'syntax' must be either :intel or :att"))
×
224
        end
225
        if dump_module
1✔
226
            # we want module metadata, so use LLVM to generate assembly output
227
            str = _dump_function_native_assembly(mi, world, wrapper, syntax, debuginfo, binary, raw, params)
×
228
        else
229
            # if we don't want the module metadata, just disassemble what our JIT has
230
            str = _dump_function_native_disassembly(mi, world, wrapper, syntax, debuginfo, binary)
1✔
231
        end
232
    else
233
        str = _dump_function_llvm(mi, world, wrapper, !raw, dump_module, optimize, debuginfo, params)
67✔
234
    end
235
    str = warning * str
68✔
236
    return str
68✔
237
end
238

239
function _dump_function_native_disassembly(mi::Core.MethodInstance, world::UInt,
1✔
240
                                           wrapper::Bool, syntax::Symbol,
241
                                           debuginfo::Symbol, binary::Bool)
242
    str = @ccall jl_dump_method_asm(mi::Any, world::UInt, false::Bool, wrapper::Bool,
1✔
243
                                    syntax::Ptr{UInt8}, debuginfo::Ptr{UInt8},
244
                                    binary::Bool)::Ref{String}
245
    return str
1✔
246
end
247

248
struct LLVMFDump
249
    tsm::Ptr{Cvoid} # opaque
250
    f::Ptr{Cvoid} # opaque
251
end
252

253
function _dump_function_native_assembly(mi::Core.MethodInstance, world::UInt,
×
254
                                        wrapper::Bool, syntax::Symbol, debuginfo::Symbol,
255
                                        binary::Bool, raw::Bool, params::CodegenParams)
256
    llvmf_dump = Ref{LLVMFDump}()
×
257
    @ccall jl_get_llvmf_defn(llvmf_dump::Ptr{LLVMFDump},mi::Any, world::UInt, wrapper::Bool,
×
258
                             true::Bool, params::CodegenParams)::Cvoid
259
    llvmf_dump[].f == C_NULL && error("could not compile the specified method")
×
260
    str = @ccall jl_dump_function_asm(llvmf_dump::Ptr{LLVMFDump}, false::Bool,
×
261
                                      syntax::Ptr{UInt8}, debuginfo::Ptr{UInt8},
262
                                      binary::Bool, raw::Bool)::Ref{String}
263
    return str
×
264
end
265

266
function _dump_function_llvm(
67✔
267
        mi::Core.MethodInstance, world::UInt, wrapper::Bool,
268
        strip_ir_metadata::Bool, dump_module::Bool,
269
        optimize::Bool, debuginfo::Symbol,
270
        params::CodegenParams)
271
    llvmf_dump = Ref{LLVMFDump}()
67✔
272
    @ccall jl_get_llvmf_defn(llvmf_dump::Ptr{LLVMFDump}, mi::Any, world::UInt,
67✔
273
                             wrapper::Bool, optimize::Bool, params::CodegenParams)::Cvoid
274
    llvmf_dump[].f == C_NULL && error("could not compile the specified method")
67✔
275
    str = @ccall jl_dump_function_ir(llvmf_dump::Ptr{LLVMFDump}, strip_ir_metadata::Bool,
67✔
276
                                     dump_module::Bool, debuginfo::Ptr{UInt8})::Ref{String}
277
    return str
67✔
278
end
279

280
"""
281
    code_llvm([io=stdout,], f, types; raw=false, dump_module=false, optimize=true, debuginfo=:default)
282

283
Prints the LLVM bitcodes generated for running the method matching the given generic
284
function and type signature to `io`.
285

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

291
See also: [`@code_llvm`](@ref), [`code_warntype`](@ref), [`code_typed`](@ref), [`code_lowered`](@ref), [`code_native`](@ref).
292
"""
293
function code_llvm(io::IO, @nospecialize(f), @nospecialize(types=Base.default_tt(f));
58✔
294
                   raw::Bool=false, dump_module::Bool=false, optimize::Bool=true, debuginfo::Symbol=:default,
295
                   params::CodegenParams=CodegenParams(debug_info_kind=Cint(0), debug_info_level=Cint(2), safepoint_on_entry=raw, gcstack_arg=raw))
296
    d = _dump_function(f, types, false, false, raw, dump_module, :intel, optimize, debuginfo, false, params)
29✔
297
    if highlighting[:llvm] && get(io, :color, false)::Bool
27✔
298
        print_llvm(io, d)
×
299
    else
300
        print(io, d)
27✔
301
    end
302
end
303
code_llvm(args...; kwargs...) = (@nospecialize; code_llvm(stdout, args...; kwargs...))
6✔
304

305
"""
306
    code_native([io=stdout,], f, types; syntax=:intel, debuginfo=:default, binary=false, dump_module=true)
307

308
Prints the native assembly instructions generated for running the method matching the given
309
generic function and type signature to `io`.
310

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

317
See also: [`@code_native`](@ref), [`code_warntype`](@ref), [`code_typed`](@ref), [`code_lowered`](@ref), [`code_llvm`](@ref).
318
"""
319
function code_native(io::IO, @nospecialize(f), @nospecialize(types=Base.default_tt(f));
6✔
320
                     dump_module::Bool=true, syntax::Symbol=:intel, raw::Bool=false,
321
                     debuginfo::Symbol=:default, binary::Bool=false,
322
                     params::CodegenParams=CodegenParams(debug_info_kind=Cint(0), debug_info_level=Cint(2), safepoint_on_entry=raw, gcstack_arg=raw))
323
    d = _dump_function(f, types, true, false, raw, dump_module, syntax, true, debuginfo, binary, params)
3✔
324
    if highlighting[:native] && get(io, :color, false)::Bool
1✔
325
        print_native(io, d)
×
326
    else
327
        print(io, d)
1✔
328
    end
329
end
330
code_native(args...; kwargs...) = (@nospecialize; code_native(stdout, args...; kwargs...))
4✔
331

332
## colorized IR and assembly printing
333

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

336
function print_llvm(io::IO, code::String)
×
337
    buf = IOBuffer(code)
×
338
    for line in eachline(buf)
×
339
        m = match(r"^(\s*)((?:[^;]|;\")*)(.*)$", line)
×
340
        m === nothing && continue
×
341
        indent, tokens, comment = m.captures
×
342
        print(io, indent)
×
343
        print_llvm_tokens(io, tokens)
×
344
        printstyled_ll(io, comment, :comment)
×
345
        println(io)
×
346
    end
×
347
end
348

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

353
function print_llvm_tokens(io, tokens)
×
354
    m = match(r"^((?:[^\s:]+:)?)(\s*)(.*)", tokens)
×
355
    if m !== nothing
×
356
        label, spaces, tokens = m.captures
×
357
        printstyled_ll(io, label, :label, spaces)
×
358
    end
359
    m = match(r"^(%[^\s=]+)(\s*)=(\s*)(.*)", tokens)
×
360
    if m !== nothing
×
361
        result, spaces, spaces2, tokens = m.captures
×
362
        printstyled_ll(io, result, :variable, spaces)
×
363
        printstyled_ll(io, '=', :default, spaces2)
×
364
    end
365
    m = match(r"^([a-z]\w*)(\s*)(.*)", tokens)
×
366
    if m !== nothing
×
367
        inst, spaces, tokens = m.captures
×
368
        iskeyword = occursin(r"^(?:define|declare|type)$", inst) || occursin("=", tokens)
×
369
        printstyled_ll(io, inst, iskeyword ? :keyword : :instruction, spaces)
×
370
    end
371

372
    print_llvm_operands(io, tokens)
×
373
end
374

375
function print_llvm_operands(io, tokens)
×
376
    while !isempty(tokens)
×
377
        tokens = print_llvm_operand(io, tokens)
×
378
    end
×
379
    return tokens
×
380
end
381

382
function print_llvm_operand(io, tokens)
×
383
    islabel = false
×
384
    while !isempty(tokens)
×
385
        m = match(r"^,(\s*)(.*)", tokens)
×
386
        if m !== nothing
×
387
            spaces, tokens = m.captures
×
388
            printstyled_ll(io, ',', :default, spaces)
×
389
            break
×
390
        end
391
        m = match(r"^(\*+|=)(\s*)(.*)", tokens)
×
392
        if m !== nothing
×
393
            sym, spaces, tokens = m.captures
×
394
            printstyled_ll(io, sym, :default, spaces)
×
395
            continue
×
396
        end
397
        m = match(r"^(\"[^\"]*\")(\s*)(.*)", tokens)
×
398
        if m !== nothing
×
399
            str, spaces, tokens = m.captures
×
400
            printstyled_ll(io, str, :variable, spaces)
×
401
            continue
×
402
        end
403
        m = match(r"^([({\[<])(\s*)(.*)", tokens)
×
404
        if m !== nothing
×
405
            bracket, spaces, tokens = m.captures
×
406
            printstyled_ll(io, bracket, :bracket, spaces)
×
407
            tokens = print_llvm_operands(io, tokens) # enter
×
408
            continue
×
409
        end
410
        m = match(r"^([)}\]>])(\s*)(.*)", tokens)
×
411
        if m !== nothing
×
412
            bracket, spaces, tokens = m.captures
×
413
            printstyled_ll(io, bracket, :bracket, spaces)
×
414
            break # leave
×
415
        end
416

417
        m = match(r"^([^\s,*=(){}\[\]<>]+)(\s*)(.*)", tokens)
×
418
        m === nothing && break
×
419
        token, spaces, tokens = m.captures
×
420
        if occursin(llvm_types, token)
×
421
            printstyled_ll(io, token, :type)
×
422
            islabel = token == "label"
×
423
        elseif occursin(llvm_cond, token) # condition code is instruction-level
×
424
            printstyled_ll(io, token, :instruction)
×
425
        elseif occursin(num_regex, token)
×
426
            printstyled_ll(io, token, :number)
×
427
        elseif occursin(r"^@.+$", token)
×
428
            printstyled_ll(io, token, :funcname)
×
429
        elseif occursin(r"^%.+$", token)
×
430
            islabel |= occursin(r"^%[^\d].*$", token) & occursin(r"^\]", tokens)
×
431
            printstyled_ll(io, token, islabel ? :label : :variable)
×
432
            islabel = false
×
433
        elseif occursin(r"^[a-z]\w+$", token)
×
434
            printstyled_ll(io, token, :keyword)
×
435
        else
436
            printstyled_ll(io, token, :default)
×
437
        end
438
        print(io, spaces)
×
439
    end
×
440
    return tokens
×
441
end
442

443
function print_native(io::IO, code::String, arch::Symbol=sys_arch_category())
×
444
    archv = Val(arch)
×
445
    buf = IOBuffer(code)
×
446
    for line in eachline(buf)
×
447
        m = match(r"^(\s*)((?:[^;#/]|#\S|;\"|/[^/])*)(.*)$", line)
×
448
        m === nothing && continue
×
449
        indent, tokens, comment = m.captures
×
450
        print(io, indent)
×
451
        print_native_tokens(io, tokens, archv)
×
452
        printstyled_ll(io, comment, :comment)
×
453
        println(io)
×
454
    end
×
455
end
456

457
function sys_arch_category()
×
458
    if Sys.ARCH === :x86_64 || Sys.ARCH === :i686
×
459
        :x86
×
460
    elseif Sys.ARCH === :aarch64 || startswith(string(Sys.ARCH), "arm")
×
461
        :arm
×
462
    else
463
        :unsupported
×
464
    end
465
end
466

467
print_native_tokens(io, line, ::Val) = print(io, line)
×
468

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

474
function print_native_tokens(io, tokens, arch::Union{Val{:x86}, Val{:arm}})
×
475
    x86 = arch isa Val{:x86}
×
476
    m = match(r"^((?:[^\s:]+:|\"[^\"]+\":)?)(\s*)(.*)", tokens)
×
477
    if m !== nothing
×
478
        label, spaces, tokens = m.captures
×
479
        printstyled_ll(io, label, :label, spaces)
×
480
    end
481
    haslabel = false
×
482
    m = match(r"^([a-z][\w.]*)(\s*)(.*)", tokens)
×
483
    if m !== nothing
×
484
        instruction, spaces, tokens = m.captures
×
485
        printstyled_ll(io, instruction, :instruction, spaces)
×
486
        haslabel = occursin(r"^(?:bl?|bl?\.\w{2,5}|[ct]bn?z)?$", instruction)
×
487
    end
488

489
    isfuncname = false
×
490
    while !isempty(tokens)
×
491
        m = match(r"^([,:*])(\s*)(.*)", tokens)
×
492
        if m !== nothing
×
493
            sym, spaces, tokens = m.captures
×
494
            printstyled_ll(io, sym, :default, spaces)
×
495
            isfuncname = false
×
496
            continue
×
497
        end
498
        m = match(r"^([(){}\[\]])(\s*)(.*)", tokens)
×
499
        if m !== nothing
×
500
            bracket, spaces, tokens = m.captures
×
501
            printstyled_ll(io, bracket, :bracket, spaces)
×
502
            continue
×
503
        end
504
        m = match(r"^#([0-9a-fx.-]+)(\s*)(.*)", tokens)
×
505
        if !x86 && m !== nothing && occursin(num_regex, m.captures[1])
×
506
            num, spaces, tokens = m.captures
×
507
            printstyled_ll(io, "#" * num, :number, spaces)
×
508
            continue
×
509
        end
510

511
        m = match(r"^([^\s,:*(){}\[\]][^\s,:*/(){}\[\]]*)(\s*)(.*)", tokens)
×
512
        m === nothing && break
×
513
        token, spaces, tokens = m.captures
×
514
        if occursin(num_regex, token)
×
515
            printstyled_ll(io, token, :number)
×
516
        elseif x86 && occursin(x86_ptr, token) || occursin(avx512flags, token)
×
517
            printstyled_ll(io, token, :keyword)
×
518
            isfuncname = token == "offset"
×
519
        elseif !x86 && (occursin(arm_keywords, token) || occursin(arm_cond, token))
×
520
            printstyled_ll(io, token, :keyword)
×
521
        elseif occursin(r"^L.+$", token)
×
522
            printstyled_ll(io, token, :label)
×
523
        elseif occursin(r"^\$.+$", token)
×
524
            printstyled_ll(io, token, :funcname)
×
525
        elseif occursin(r"^%?(?:[a-z][\w.]+|\"[^\"]+\")$", token)
×
526
            islabel = haslabel & !occursin(',', tokens)
×
527
            printstyled_ll(io, token, islabel ? :label : isfuncname ? :funcname : :variable)
×
528
            isfuncname = false
×
529
        else
530
            printstyled_ll(io, token, :default)
×
531
        end
532
        print(io, spaces)
×
533
    end
×
534
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