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

JuliaLang / julia / #37732

30 Mar 2024 04:18AM UTC coverage: 80.965% (+0.1%) from 80.848%
#37732

push

local

web-flow
no need to check whether mq_master is nil in the GC work-stealing loop (#53899)

This is not needed after https://github.com/JuliaLang/julia/pull/53355.

70159 of 86653 relevant lines covered (80.97%)

14962901.56 hits per line

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

32.35
/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, _...)
206✔
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,
71✔
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")
71✔
189
    if isa(f, Core.Builtin)
66✔
190
        throw(ArgumentError("argument is not a generic function"))
×
191
    end
192
    warning = ""
66✔
193
    # get the MethodInstance for the method match
194
    if !isa(f, Core.OpaqueClosure)
66✔
195
        world = Base.get_world_counter()
64✔
196
        match = Base._which(signature_type(f, t); world)
64✔
197
        mi = Core.Compiler.specialize_method(match)
64✔
198
        # TODO: use jl_is_cacheable_sig instead of isdispatchtuple
199
        isdispatchtuple(mi.specTypes) || (warning = GENERIC_SIG_WARNING)
64✔
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
66✔
217
        debuginfo = :source
25✔
218
    elseif debuginfo !== :source && debuginfo !== :none
41✔
219
        throw(ArgumentError("'debuginfo' must be either :source or :none"))
×
220
    end
221
    if native
66✔
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)
65✔
234
    end
235
    str = warning * str
66✔
236
    return str
66✔
237
end
238

239
function _dump_function_native_disassembly(mi::Core.MethodInstance, world::UInt,
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(
65✔
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}()
65✔
272
    @ccall jl_get_llvmf_defn(llvmf_dump::Ptr{LLVMFDump}, mi::Any, world::UInt,
65✔
273
                             wrapper::Bool, optimize::Bool, params::CodegenParams)::Cvoid
274
    llvmf_dump[].f == C_NULL && error("could not compile the specified method")
65✔
275
    str = @ccall jl_dump_function_ir(llvmf_dump::Ptr{LLVMFDump}, strip_ir_metadata::Bool,
65✔
276
                                     dump_module::Bool, debuginfo::Ptr{UInt8})::Ref{String}
277
    return str
65✔
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));
27✔
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)
27✔
297
    if highlighting[:llvm] && get(io, :color, false)::Bool
25✔
298
        print_llvm(io, d)
×
299
    else
300
        print(io, d)
25✔
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));
3✔
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