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

JuliaLang / julia / #37433

pending completion
#37433

push

local

web-flow
Merge pull request #48513 from JuliaLang/jn/extend-once

ensure extension triggers are only run by the package that satified them

60 of 60 new or added lines in 1 file covered. (100.0%)

72324 of 82360 relevant lines covered (87.81%)

31376331.4 hits per line

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

95.36
/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="")
1,782✔
24
    printstyled(io, x, bold=llstyle[s][1], color=llstyle[s][2])
2,984✔
25
    print(io, trailing_spaces)
1,202✔
26
end
27

28
# displaying type warnings
29

30
function warntype_type_printer(io::IO; @nospecialize(type), used::Bool, show_type::Bool=true, _...)
572✔
31
    (show_type && used) || return nothing
375✔
32
    str = "::$type"
197✔
33
    if !highlighting[:warntype]
197✔
34
        print(io, str)
4✔
35
    elseif type isa Union && is_expected_union(type)
193✔
36
        Base.emphasize(io, str, Base.warn_color()) # more mild user notification
6✔
37
    elseif type isa Type && (!Base.isdispatchelem(type) || type == Core.Box)
325✔
38
        Base.emphasize(io, str)
7✔
39
    else
40
        Base.printstyled(io, str, color=:cyan) # show the "good" type
180✔
41
    end
42
    return nothing
197✔
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)
14✔
48
    Base.unionlen(u) < 4 || return false
14✔
49
    for x in Base.uniontypes(u)
14✔
50
        if !Base.isdispatchelem(x) || x == Core.Box
57✔
51
            return false
3✔
52
        end
53
    end
38✔
54
    return true
11✔
55
end
56

57
"""
58
    code_warntype([io::IO], f, types; debuginfo=:default)
59

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

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

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

73
See [`@code_warntype`](@ref man-code-warntype) for more information.
74
"""
75
function code_warntype(io::IO, @nospecialize(f), @nospecialize(t=Base.default_tt(f));
54✔
76
                       debuginfo::Symbol=:default, optimize::Bool=false, kwargs...)
77
    debuginfo = Base.IRShow.debuginfo(debuginfo)
26✔
78
    lineprinter = Base.IRShow.__debuginfo[debuginfo]
26✔
79
    for (src, rettype) in code_typed(f, t; optimize, kwargs...)
26✔
80
        if !(src isa Core.CodeInfo)
26✔
81
            println(io, src)
×
82
            println(io, "  failed to infer")
×
83
            continue
×
84
        end
85
        lambda_io::IOContext = io
26✔
86
        p = src.parent
26✔
87
        nargs::Int = 0
26✔
88
        if p isa Core.MethodInstance
26✔
89
            println(io, p)
26✔
90
            print(io, "  from ")
26✔
91
            println(io, p.def)
26✔
92
            p.def isa Method && (nargs = p.def.nargs)
26✔
93
            if !isempty(p.sparam_vals)
26✔
94
                println(io, "Static Parameters")
4✔
95
                sig = p.def.sig
4✔
96
                warn_color = Base.warn_color() # more mild user notification
4✔
97
                for i = 1:length(p.sparam_vals)
8✔
98
                    sig = sig::UnionAll
8✔
99
                    name = sig.var.name
8✔
100
                    val = p.sparam_vals[i]
8✔
101
                    print_highlighted(io::IO, v::String, color::Symbol) =
17✔
102
                        if highlighting[:warntype]
103
                            Base.printstyled(io, v; color)
9✔
104
                        else
105
                            Base.print(io, v)
×
106
                        end
107
                    if val isa TypeVar
8✔
108
                        if val.lb === Union{}
3✔
109
                            print(io, "  ", name, " <: ")
1✔
110
                            print_highlighted(io, "$(val.ub)", warn_color)
1✔
111
                        elseif val.ub === Any
2✔
112
                            print(io, "  ", sig.var.name, " >: ")
1✔
113
                            print_highlighted(io, "$(val.lb)", warn_color)
1✔
114
                        else
115
                            print(io, "  ")
1✔
116
                            print_highlighted(io, "$(val.lb)", warn_color)
1✔
117
                            print(io, " <: ", sig.var.name, " <: ")
1✔
118
                            print_highlighted(io, "$(val.ub)", warn_color)
4✔
119
                        end
120
                    elseif val isa typeof(Vararg)
5✔
121
                        print(io, "  ", name, "::")
1✔
122
                        print_highlighted(io, "Int", warn_color)
1✔
123
                    else
124
                        print(io, "  ", sig.var.name, " = ")
4✔
125
                        print_highlighted(io, "$(val)", :cyan) # show the "good" type
4✔
126
                    end
127
                    println(io)
8✔
128
                    sig = sig.body
8✔
129
                end
12✔
130
            end
131
        end
132
        if src.slotnames !== nothing
26✔
133
            slotnames = Base.sourceinfo_slotnames(src)
26✔
134
            lambda_io = IOContext(lambda_io, :SOURCE_SLOTNAMES => slotnames)
26✔
135
            slottypes = src.slottypes
26✔
136
            nargs > 0 && println(io, "Arguments")
26✔
137
            for i = 1:length(slotnames)
52✔
138
                if i == nargs + 1
75✔
139
                    println(io, "Locals")
6✔
140
                end
141
                print(io, "  ", slotnames[i])
75✔
142
                if isa(slottypes, Vector{Any})
75✔
143
                    warntype_type_printer(io; type=slottypes[i], used=true)
75✔
144
                end
145
                println(io)
75✔
146
            end
124✔
147
        end
148
        print(io, "Body")
26✔
149
        warntype_type_printer(io; type=rettype, used=true)
26✔
150
        println(io)
26✔
151
        irshow_config = Base.IRShow.IRShowConfig(lineprinter(src), warntype_type_printer)
26✔
152
        Base.IRShow.show_ir(lambda_io, src, irshow_config)
26✔
153
        println(io)
26✔
154
    end
52✔
155
    nothing
26✔
156
end
157
code_warntype(@nospecialize(f), @nospecialize(t=Base.default_tt(f)); kwargs...) =
×
158
    code_warntype(stdout, f, t; kwargs...)
159

160
import Base.CodegenParams
161

162
const GENERIC_SIG_WARNING = "; WARNING: This code may not match what actually runs.\n"
163
const OC_MISMATCH_WARNING =
164
"""
165
; WARNING: The pre-inferred opaque closure is not callable with the given arguments
166
;          and will error on dispatch with this signature.
167
"""
168

169
# Printing code representations in IR and assembly
170
function _dump_function(@nospecialize(f), @nospecialize(t), native::Bool, wrapper::Bool,
209✔
171
                        strip_ir_metadata::Bool, dump_module::Bool, syntax::Symbol,
172
                        optimize::Bool, debuginfo::Symbol, binary::Bool,
173
                        params::CodegenParams=CodegenParams(debug_info_kind=Cint(0)))
174
    ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions")
209✔
175
    if isa(f, Core.Builtin)
120✔
176
        throw(ArgumentError("argument is not a generic function"))
2✔
177
    end
178
    warning = ""
118✔
179
    # get the MethodInstance for the method match
180
    if !isa(f, Core.OpaqueClosure)
118✔
181
        world = Base.get_world_counter()
116✔
182
        match = Base._which(signature_type(f, t); world)
116✔
183
        linfo = Core.Compiler.specialize_method(match)
113✔
184
        # TODO: use jl_is_cacheable_sig instead of isdispatchtuple
185
        isdispatchtuple(linfo.specTypes) || (warning = GENERIC_SIG_WARNING)
113✔
186
    else
187
        world = UInt64(f.world)
2✔
188
        if Core.Compiler.is_source_inferred(f.source.source)
2✔
189
            # OC was constructed from inferred source. There's only one
190
            # specialization and we can't infer anything more precise either.
191
            world = f.source.primary_world
2✔
192
            linfo = f.source.specializations[1]
2✔
193
            Core.Compiler.hasintersect(typeof(f).parameters[1], t) || (warning = OC_MISMATCH_WARNING)
2✔
194
        else
195
            linfo = Core.Compiler.specialize_method(f.source, Tuple{typeof(f.captures), t.parameters...}, Core.svec())
×
196
            actual = isdispatchtuple(linfo.specTypes)
×
197
            isdispatchtuple(linfo.specTypes) || (warning = GENERIC_SIG_WARNING)
×
198
        end
199
    end
200
    # get the code for it
201
    if debuginfo === :default
115✔
202
        debuginfo = :source
74✔
203
    elseif debuginfo !== :source && debuginfo !== :none
41✔
204
        throw(ArgumentError("'debuginfo' must be either :source or :none"))
×
205
    end
206
    if native
115✔
207
        if syntax !== :att && syntax !== :intel
28✔
208
            throw(ArgumentError("'syntax' must be either :intel or :att"))
×
209
        end
210
        if dump_module
28✔
211
            str = _dump_function_linfo_native(linfo, world, wrapper, syntax, debuginfo, binary, params)
25✔
212
        else
213
            str = _dump_function_linfo_native(linfo, world, wrapper, syntax, debuginfo, binary)
31✔
214
        end
215
    else
216
        str = _dump_function_linfo_llvm(linfo, world, wrapper, strip_ir_metadata, dump_module, optimize, debuginfo, params)
87✔
217
    end
218
    str = warning * str
115✔
219
    return str
115✔
220
end
221

222
function _dump_function_linfo_native(linfo::Core.MethodInstance, world::UInt, wrapper::Bool, syntax::Symbol, debuginfo::Symbol, binary::Bool)
3✔
223
    str = ccall(:jl_dump_method_asm, Ref{String},
3✔
224
                (Any, UInt, Bool, Bool, Ptr{UInt8}, Ptr{UInt8}, Bool),
225
                linfo, world, false, wrapper, syntax, debuginfo, binary)
226
    return str
3✔
227
end
228

229
struct LLVMFDump
230
    tsm::Ptr{Cvoid} # opaque
231
    f::Ptr{Cvoid} # opaque
232
end
233

234
function _dump_function_linfo_native(linfo::Core.MethodInstance, world::UInt, wrapper::Bool, syntax::Symbol, debuginfo::Symbol, binary::Bool, params::CodegenParams)
25✔
235
    llvmf_dump = Ref{LLVMFDump}()
25✔
236
    ccall(:jl_get_llvmf_defn, Cvoid, (Ptr{LLVMFDump}, Any, UInt, Bool, Bool, CodegenParams), llvmf_dump, linfo, world, wrapper, true, params)
25✔
237
    llvmf_dump[].f == C_NULL && error("could not compile the specified method")
25✔
238
    str = ccall(:jl_dump_function_asm, Ref{String},
25✔
239
                (Ptr{LLVMFDump}, Bool, Ptr{UInt8}, Ptr{UInt8}, Bool),
240
                llvmf_dump, false, syntax, debuginfo, binary)
241
    return str
25✔
242
end
243

244
function _dump_function_linfo_llvm(
87✔
245
        linfo::Core.MethodInstance, world::UInt, wrapper::Bool,
246
        strip_ir_metadata::Bool, dump_module::Bool,
247
        optimize::Bool, debuginfo::Symbol,
248
        params::CodegenParams)
249
    llvmf_dump = Ref{LLVMFDump}()
87✔
250
    ccall(:jl_get_llvmf_defn, Cvoid, (Ptr{LLVMFDump}, Any, UInt, Bool, Bool, CodegenParams), llvmf_dump, linfo, world, wrapper, optimize, params)
87✔
251
    llvmf_dump[].f == C_NULL && error("could not compile the specified method")
87✔
252
    str = ccall(:jl_dump_function_ir, Ref{String},
87✔
253
                (Ptr{LLVMFDump}, Bool, Bool, Ptr{UInt8}),
254
                llvmf_dump, strip_ir_metadata, dump_module, debuginfo)
255
    return str
87✔
256
end
257

258
"""
259
    code_llvm([io=stdout,], f, types; raw=false, dump_module=false, optimize=true, debuginfo=:default)
260

261
Prints the LLVM bitcodes generated for running the method matching the given generic
262
function and type signature to `io`.
263

264
If the `optimize` keyword is unset, the code will be shown before LLVM optimizations.
265
All metadata and dbg.* calls are removed from the printed bitcode. For the full IR, set the `raw` keyword to true.
266
To dump the entire module that encapsulates the function (with declarations), set the `dump_module` keyword to true.
267
Keyword argument `debuginfo` may be one of source (default) or none, to specify the verbosity of code comments.
268
"""
269
function code_llvm(io::IO, @nospecialize(f), @nospecialize(types), raw::Bool,
52✔
270
                   dump_module::Bool=false, optimize::Bool=true, debuginfo::Symbol=:default)
271
    d = _dump_function(f, types, false, false, !raw, dump_module, :intel, optimize, debuginfo, false)
52✔
272
    if highlighting[:llvm] && get(io, :color, false)::Bool
48✔
273
        print_llvm(io, d)
1✔
274
    else
275
        print(io, d)
47✔
276
    end
277
end
278
code_llvm(io::IO, @nospecialize(f), @nospecialize(types=Base.default_tt(f)); raw::Bool=false, dump_module::Bool=false, optimize::Bool=true, debuginfo::Symbol=:default) =
106✔
279
    code_llvm(io, f, types, raw, dump_module, optimize, debuginfo)
280
code_llvm(@nospecialize(f), @nospecialize(types=Base.default_tt(f)); raw=false, dump_module=false, optimize=true, debuginfo::Symbol=:default) =
12✔
281
    code_llvm(stdout, f, types; raw, dump_module, optimize, debuginfo)
282

283
"""
284
    code_native([io=stdout,], f, types; syntax=:intel, debuginfo=:default, binary=false, dump_module=true)
285

286
Prints the native assembly instructions generated for running the method matching the given
287
generic function and type signature to `io`.
288

289
* Set assembly syntax by setting `syntax` to `:intel` (default) for intel syntax or `:att` for AT&T syntax.
290
* Specify verbosity of code comments by setting `debuginfo` to `:source` (default) or `:none`.
291
* If `binary` is `true`, also print the binary machine code for each instruction precedented by an abbreviated address.
292
* If `dump_module` is `false`, do not print metadata such as rodata or directives.
293

294
See also: [`@code_native`](@ref), [`code_llvm`](@ref), [`code_typed`](@ref) and [`code_lowered`](@ref)
295
"""
296
function code_native(io::IO, @nospecialize(f), @nospecialize(types=Base.default_tt(f));
68✔
297
                     dump_module::Bool=true, syntax::Symbol=:intel, debuginfo::Symbol=:default, binary::Bool=false)
298
    d = _dump_function(f, types, true, false, false, dump_module, syntax, true, debuginfo, binary)
33✔
299
    if highlighting[:native] && get(io, :color, false)::Bool
28✔
300
        print_native(io, d)
1✔
301
    else
302
        print(io, d)
27✔
303
    end
304
end
305
code_native(@nospecialize(f), @nospecialize(types=Base.default_tt(f)); dump_module::Bool=true, syntax::Symbol=:intel, debuginfo::Symbol=:default, binary::Bool=false) =
14✔
306
    code_native(stdout, f, types; dump_module, syntax, debuginfo, binary)
307
code_native(::IO, ::Any, ::Symbol) = error("invalid code_native call") # resolve ambiguous call
×
308

309
## colorized IR and assembly printing
310

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

313
function print_llvm(io::IO, code::String)
34✔
314
    buf = IOBuffer(code)
34✔
315
    for line in eachline(buf)
68✔
316
        m = match(r"^(\s*)((?:[^;]|;\")*)(.*)$", line)
54✔
317
        m === nothing && continue
54✔
318
        indent, tokens, comment = m.captures
54✔
319
        print(io, indent)
108✔
320
        print_llvm_tokens(io, tokens)
108✔
321
        printstyled_ll(io, comment, :comment)
108✔
322
        println(io)
54✔
323
    end
54✔
324
end
325

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

330
function print_llvm_tokens(io, tokens)
54✔
331
    m = match(r"^((?:[^\s:]+:)?)(\s*)(.*)", tokens)
54✔
332
    if m !== nothing
54✔
333
        label, spaces, tokens = m.captures
54✔
334
        printstyled_ll(io, label, :label, spaces)
108✔
335
    end
336
    m = match(r"^(%[^\s=]+)(\s*)=(\s*)(.*)", tokens)
54✔
337
    if m !== nothing
54✔
338
        result, spaces, spaces2, tokens = m.captures
31✔
339
        printstyled_ll(io, result, :variable, spaces)
62✔
340
        printstyled_ll(io, '=', :default, spaces2)
62✔
341
    end
342
    m = match(r"^([a-z]\w*)(\s*)(.*)", tokens)
54✔
343
    if m !== nothing
54✔
344
        inst, spaces, tokens = m.captures
46✔
345
        iskeyword = occursin(r"^(?:define|declare|type)$", inst) || occursin("=", tokens)
88✔
346
        printstyled_ll(io, inst, iskeyword ? :keyword : :instruction, spaces)
92✔
347
    end
348

349
    print_llvm_operands(io, tokens)
54✔
350
end
351

352
function print_llvm_operands(io, tokens)
87✔
353
    while !isempty(tokens)
325✔
354
        tokens = print_llvm_operand(io, tokens)
302✔
355
    end
151✔
356
    return tokens
87✔
357
end
358

359
function print_llvm_operand(io, tokens)
151✔
360
    islabel = false
151✔
361
    while !isempty(tokens)
884✔
362
        m = match(r"^,(\s*)(.*)", tokens)
370✔
363
        if m !== nothing
370✔
364
            spaces, tokens = m.captures
46✔
365
            printstyled_ll(io, ',', :default, spaces)
92✔
366
            break
46✔
367
        end
368
        m = match(r"^(\*+|=)(\s*)(.*)", tokens)
324✔
369
        if m !== nothing
324✔
370
            sym, spaces, tokens = m.captures
37✔
371
            printstyled_ll(io, sym, :default, spaces)
74✔
372
            continue
37✔
373
        end
374
        m = match(r"^(\"[^\"]*\")(\s*)(.*)", tokens)
287✔
375
        if m !== nothing
287✔
376
            str, spaces, tokens = m.captures
7✔
377
            printstyled_ll(io, str, :variable, spaces)
14✔
378
            continue
7✔
379
        end
380
        m = match(r"^([({\[<])(\s*)(.*)", tokens)
280✔
381
        if m !== nothing
280✔
382
            bracket, spaces, tokens = m.captures
33✔
383
            printstyled_ll(io, bracket, :bracket, spaces)
66✔
384
            tokens = print_llvm_operands(io, tokens) # enter
66✔
385
            continue
33✔
386
        end
387
        m = match(r"^([)}\]>])(\s*)(.*)", tokens)
247✔
388
        if m !== nothing
247✔
389
            bracket, spaces, tokens = m.captures
33✔
390
            printstyled_ll(io, bracket, :bracket, spaces)
66✔
391
            break # leave
33✔
392
        end
393

394
        m = match(r"^([^\s,*=(){}\[\]<>]+)(\s*)(.*)", tokens)
214✔
395
        m === nothing && break
214✔
396
        token, spaces, tokens = m.captures
214✔
397
        if occursin(llvm_types, token)
214✔
398
            printstyled_ll(io, token, :type)
71✔
399
            islabel = token == "label"
138✔
400
        elseif occursin(llvm_cond, token) # condition code is instruction-level
143✔
401
            printstyled_ll(io, token, :instruction)
1✔
402
        elseif occursin(num_regex, token)
142✔
403
            printstyled_ll(io, token, :number)
36✔
404
        elseif occursin(r"^@.+$", token)
106✔
405
            printstyled_ll(io, token, :funcname)
5✔
406
        elseif occursin(r"^%.+$", token)
101✔
407
            islabel |= occursin(r"^%[^\d].*$", token) & occursin(r"^\]", tokens)
47✔
408
            printstyled_ll(io, token, islabel ? :label : :variable)
47✔
409
            islabel = false
47✔
410
        elseif occursin(r"^[a-z]\w+$", token)
54✔
411
            printstyled_ll(io, token, :keyword)
48✔
412
        else
413
            printstyled_ll(io, token, :default)
6✔
414
        end
415
        print(io, spaces)
214✔
416
    end
291✔
417
    return tokens
151✔
418
end
419

420
function print_native(io::IO, code::String, arch::Symbol=sys_arch_category())
59✔
421
    archv = Val(arch)
59✔
422
    buf = IOBuffer(code)
58✔
423
    for line in eachline(buf)
116✔
424
        m = match(r"^(\s*)((?:[^;#/]|#\S|;\"|/[^/])*)(.*)$", line)
91✔
425
        m === nothing && continue
91✔
426
        indent, tokens, comment = m.captures
91✔
427
        print(io, indent)
182✔
428
        print_native_tokens(io, tokens, archv)
91✔
429
        printstyled_ll(io, comment, :comment)
182✔
430
        println(io)
91✔
431
    end
91✔
432
end
433

434
function sys_arch_category()
1✔
435
    if Sys.ARCH === :x86_64 || Sys.ARCH === :i686
1✔
436
        :x86
1✔
437
    elseif Sys.ARCH === :aarch64 || startswith(string(Sys.ARCH), "arm")
×
438
        :arm
×
439
    else
440
        :unsupported
×
441
    end
442
end
443

444
print_native_tokens(io, line, ::Val) = print(io, line)
1✔
445

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

451
function print_native_tokens(io, tokens, arch::Union{Val{:x86}, Val{:arm}})
90✔
452
    x86 = arch isa Val{:x86}
90✔
453
    m = match(r"^((?:[^\s:]+:|\"[^\"]+\":)?)(\s*)(.*)", tokens)
90✔
454
    if m !== nothing
90✔
455
        label, spaces, tokens = m.captures
90✔
456
        printstyled_ll(io, label, :label, spaces)
180✔
457
    end
458
    haslabel = false
90✔
459
    m = match(r"^([a-z][\w.]*)(\s*)(.*)", tokens)
90✔
460
    if m !== nothing
90✔
461
        instruction, spaces, tokens = m.captures
58✔
462
        printstyled_ll(io, instruction, :instruction, spaces)
116✔
463
        haslabel = occursin(r"^(?:bl?|bl?\.\w{2,5}|[ct]bn?z)?$", instruction)
58✔
464
    end
465

466
    isfuncname = false
90✔
467
    while !isempty(tokens)
914✔
468
        m = match(r"^([,:*])(\s*)(.*)", tokens)
367✔
469
        if m !== nothing
367✔
470
            sym, spaces, tokens = m.captures
84✔
471
            printstyled_ll(io, sym, :default, spaces)
168✔
472
            isfuncname = false
84✔
473
            continue
84✔
474
        end
475
        m = match(r"^([(){}\[\]])(\s*)(.*)", tokens)
283✔
476
        if m !== nothing
283✔
477
            bracket, spaces, tokens = m.captures
66✔
478
            printstyled_ll(io, bracket, :bracket, spaces)
132✔
479
            continue
66✔
480
        end
481
        m = match(r"^#([0-9a-fx.-]+)(\s*)(.*)", tokens)
217✔
482
        if !x86 && m !== nothing && occursin(num_regex, m.captures[1])
217✔
483
            num, spaces, tokens = m.captures
6✔
484
            printstyled_ll(io, "#" * num, :number, spaces)
12✔
485
            continue
6✔
486
        end
487

488
        m = match(r"^([^\s,:*(){}\[\]][^\s,:*/(){}\[\]]*)(\s*)(.*)", tokens)
211✔
489
        m === nothing && break
211✔
490
        token, spaces, tokens = m.captures
211✔
491
        if occursin(num_regex, token)
211✔
492
            printstyled_ll(io, token, :number)
20✔
493
        elseif x86 && occursin(x86_ptr, token) || occursin(avx512flags, token)
312✔
494
            printstyled_ll(io, token, :keyword)
32✔
495
            isfuncname = token == "offset"
32✔
496
        elseif !x86 && (occursin(arm_keywords, token) || occursin(arm_cond, token))
198✔
497
            printstyled_ll(io, token, :keyword)
7✔
498
        elseif occursin(r"^L.+$", token)
152✔
499
            printstyled_ll(io, token, :label)
5✔
500
        elseif occursin(r"^\$.+$", token)
147✔
501
            printstyled_ll(io, token, :funcname)
1✔
502
        elseif occursin(r"^%?(?:[a-z][\w.]+|\"[^\"]+\")$", token)
146✔
503
            islabel = haslabel & !occursin(',', tokens)
187✔
504
            printstyled_ll(io, token, islabel ? :label : isfuncname ? :funcname : :variable)
228✔
505
            isfuncname = false
114✔
506
        else
507
            printstyled_ll(io, token, :default)
32✔
508
        end
509
        print(io, spaces)
211✔
510
    end
457✔
511
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

© 2025 Coveralls, Inc