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

JuliaLang / julia / #37613

06 Sep 2023 07:02PM UTC coverage: 85.586% (+1.3%) from 84.296%
#37613

push

local

web-flow
slot2ssa: Fix spurious φᶜ edges (#51199)

The unreachable here seems to be caused by the fact that (as of #50943)
we re-use a more narrow type from Inference that correctly ignores these
edges, but when inserting the `φᶜ` node in `slot2reg` we were including
extra edges that never get exercised at runtime.

I'm not sure _why_ this causes us to hit an unreachable, since the
narrow type from inference is technically still valid (the catch block
will never observe these spurious assignments at runtime), but this
seems to fix the issue and anyway improves the quality of the IRCode
generated by `slot2ssa`.

Resolves #51159

6 of 6 new or added lines in 2 files covered. (100.0%)

72827 of 85092 relevant lines covered (85.59%)

12467179.17 hits per line

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

28.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, _...)
166✔
31
    (show_type && used) || return nothing
116✔
32
    str = "::$type"
50✔
33
    if !highlighting[:warntype]
50✔
34
        print(io, str)
×
35
    elseif type isa Union && is_expected_union(type)
50✔
36
        Base.emphasize(io, str, Base.warn_color()) # more mild user notification
4✔
37
    elseif type isa Type && (!Base.isdispatchelem(type) || type == Core.Box)
90✔
38
        Base.emphasize(io, str)
×
39
    else
40
        Base.printstyled(io, str, color=:cyan) # show the "good" type
46✔
41
    end
42
    return nothing
50✔
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)
4✔
48
    Base.unionlen(u) < 4 || return false
4✔
49
    for x in Base.uniontypes(u)
4✔
50
        if !Base.isdispatchelem(x) || x == Core.Box
16✔
51
            return false
×
52
        end
53
    end
12✔
54
    return true
4✔
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));
4✔
76
                       debuginfo::Symbol=:default, optimize::Bool=false, kwargs...)
77
    debuginfo = Base.IRShow.debuginfo(debuginfo)
2✔
78
    lineprinter = Base.IRShow.__debuginfo[debuginfo]
2✔
79
    for (src, rettype) in code_typed(f, t; optimize, kwargs...)
2✔
80
        if !(src isa Core.CodeInfo)
2✔
81
            println(io, src)
×
82
            println(io, "  failed to infer")
×
83
            continue
×
84
        end
85
        lambda_io::IOContext = io
2✔
86
        p = src.parent
2✔
87
        nargs::Int = 0
2✔
88
        if p isa Core.MethodInstance
2✔
89
            println(io, p)
2✔
90
            print(io, "  from ")
2✔
91
            println(io, p.def)
2✔
92
            p.def isa Method && (nargs = p.def.nargs)
4✔
93
            if !isempty(p.sparam_vals)
2✔
94
                println(io, "Static Parameters")
×
95
                sig = p.def.sig
×
96
                warn_color = Base.warn_color() # more mild user notification
×
97
                for i = 1:length(p.sparam_vals)
×
98
                    sig = sig::UnionAll
×
99
                    name = sig.var.name
×
100
                    val = p.sparam_vals[i]
×
101
                    print_highlighted(io::IO, v::String, color::Symbol) =
×
102
                        if highlighting[:warntype]
103
                            Base.printstyled(io, v; color)
×
104
                        else
105
                            Base.print(io, v)
×
106
                        end
107
                    if val isa TypeVar
×
108
                        if val.lb === Union{}
×
109
                            print(io, "  ", name, " <: ")
×
110
                            print_highlighted(io, "$(val.ub)", warn_color)
×
111
                        elseif val.ub === Any
×
112
                            print(io, "  ", sig.var.name, " >: ")
×
113
                            print_highlighted(io, "$(val.lb)", warn_color)
×
114
                        else
115
                            print(io, "  ")
×
116
                            print_highlighted(io, "$(val.lb)", warn_color)
×
117
                            print(io, " <: ", sig.var.name, " <: ")
×
118
                            print_highlighted(io, "$(val.ub)", warn_color)
×
119
                        end
120
                    elseif val isa typeof(Vararg)
×
121
                        print(io, "  ", name, "::")
×
122
                        print_highlighted(io, "Int", warn_color)
×
123
                    else
124
                        print(io, "  ", sig.var.name, " = ")
×
125
                        print_highlighted(io, "$(val)", :cyan) # show the "good" type
×
126
                    end
127
                    println(io)
×
128
                    sig = sig.body
×
129
                end
×
130
            end
131
        end
132
        if src.slotnames !== nothing
2✔
133
            slotnames = Base.sourceinfo_slotnames(src)
2✔
134
            lambda_io = IOContext(lambda_io, :SOURCE_SLOTNAMES => slotnames)
2✔
135
            slottypes = src.slottypes
2✔
136
            nargs > 0 && println(io, "Arguments")
2✔
137
            for i = 1:length(slotnames)
4✔
138
                if i == nargs + 1
12✔
139
                    println(io, "Locals")
2✔
140
                end
141
                print(io, "  ", slotnames[i])
12✔
142
                if isa(slottypes, Vector{Any})
12✔
143
                    warntype_type_printer(io; type=slottypes[i], used=true)
12✔
144
                end
145
                println(io)
12✔
146
            end
22✔
147
        end
148
        print(io, "Body")
2✔
149
        warntype_type_printer(io; type=rettype, used=true)
2✔
150
        println(io)
2✔
151
        irshow_config = Base.IRShow.IRShowConfig(lineprinter(src), warntype_type_printer)
2✔
152
        Base.IRShow.show_ir(lambda_io, src, irshow_config)
2✔
153
        println(io)
2✔
154
    end
4✔
155
    nothing
2✔
156
end
157
code_warntype(args...; kwargs...) = (@nospecialize; code_warntype(stdout, args...; kwargs...))
×
158

159
using Base: CodegenParams
160

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

168
# Printing code representations in IR and assembly
169

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

225
function _dump_function_native_disassembly(mi::Core.MethodInstance, world::UInt,
×
226
                                           wrapper::Bool, syntax::Symbol,
227
                                           debuginfo::Symbol, binary::Bool)
228
    str = @ccall jl_dump_method_asm(mi::Any, world::UInt, false::Bool, wrapper::Bool,
×
229
                                    syntax::Ptr{UInt8}, debuginfo::Ptr{UInt8},
230
                                    binary::Bool)::Ref{String}
231
    return str
×
232
end
233

234
struct LLVMFDump
235
    tsm::Ptr{Cvoid} # opaque
236
    f::Ptr{Cvoid} # opaque
237
end
238

239
function _dump_function_native_assembly(mi::Core.MethodInstance, world::UInt,
×
240
                                        wrapper::Bool, syntax::Symbol, debuginfo::Symbol,
241
                                        binary::Bool, raw::Bool, params::CodegenParams)
242
    llvmf_dump = Ref{LLVMFDump}()
×
243
    @ccall jl_get_llvmf_defn(llvmf_dump::Ptr{LLVMFDump},mi::Any, world::UInt, wrapper::Bool,
×
244
                             true::Bool, params::CodegenParams)::Cvoid
245
    llvmf_dump[].f == C_NULL && error("could not compile the specified method")
×
246
    str = @ccall jl_dump_function_asm(llvmf_dump::Ptr{LLVMFDump}, false::Bool,
×
247
                                      syntax::Ptr{UInt8}, debuginfo::Ptr{UInt8},
248
                                      binary::Bool, raw::Bool)::Ref{String}
249
    return str
×
250
end
251

252
function _dump_function_llvm(
69✔
253
        mi::Core.MethodInstance, world::UInt, wrapper::Bool,
254
        strip_ir_metadata::Bool, dump_module::Bool,
255
        optimize::Bool, debuginfo::Symbol,
256
        params::CodegenParams)
257
    llvmf_dump = Ref{LLVMFDump}()
69✔
258
    @ccall jl_get_llvmf_defn(llvmf_dump::Ptr{LLVMFDump}, mi::Any, world::UInt,
69✔
259
                             wrapper::Bool, optimize::Bool, params::CodegenParams)::Cvoid
260
    llvmf_dump[].f == C_NULL && error("could not compile the specified method")
69✔
261
    str = @ccall jl_dump_function_ir(llvmf_dump::Ptr{LLVMFDump}, strip_ir_metadata::Bool,
69✔
262
                                     dump_module::Bool, debuginfo::Ptr{UInt8})::Ref{String}
263
    return str
69✔
264
end
265

266
"""
267
    code_llvm([io=stdout,], f, types; raw=false, dump_module=false, optimize=true, debuginfo=:default)
268

269
Prints the LLVM bitcodes generated for running the method matching the given generic
270
function and type signature to `io`.
271

272
If the `optimize` keyword is unset, the code will be shown before LLVM optimizations.
273
All metadata and dbg.* calls are removed from the printed bitcode. For the full IR, set the `raw` keyword to true.
274
To dump the entire module that encapsulates the function (with declarations), set the `dump_module` keyword to true.
275
Keyword argument `debuginfo` may be one of source (default) or none, to specify the verbosity of code comments.
276
"""
277
function code_llvm(io::IO, @nospecialize(f), @nospecialize(types=Base.default_tt(f));
66✔
278
                   raw::Bool=false, dump_module::Bool=false, optimize::Bool=true, debuginfo::Symbol=:default,
279
                   params::CodegenParams=CodegenParams(debug_info_kind=Cint(0), safepoint_on_entry=raw, gcstack_arg=raw))
280
    d = _dump_function(f, types, false, false, raw, dump_module, :intel, optimize, debuginfo, false, params)
33✔
281
    if highlighting[:llvm] && get(io, :color, false)::Bool
31✔
282
        print_llvm(io, d)
×
283
    else
284
        print(io, d)
31✔
285
    end
286
end
287
code_llvm(args...; kwargs...) = (@nospecialize; code_llvm(stdout, args...; kwargs...))
6✔
288

289
"""
290
    code_native([io=stdout,], f, types; syntax=:intel, debuginfo=:default, binary=false, dump_module=true)
291

292
Prints the native assembly instructions generated for running the method matching the given
293
generic function and type signature to `io`.
294

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

301
See also: [`@code_native`](@ref), [`code_llvm`](@ref), [`code_typed`](@ref) and [`code_lowered`](@ref)
302
"""
303
function code_native(io::IO, @nospecialize(f), @nospecialize(types=Base.default_tt(f));
4✔
304
                     dump_module::Bool=true, syntax::Symbol=:intel, raw::Bool=false,
305
                     debuginfo::Symbol=:default, binary::Bool=false,
306
                     params::CodegenParams=CodegenParams(debug_info_kind=Cint(0), safepoint_on_entry=raw, gcstack_arg=raw))
307
    d = _dump_function(f, types, true, false, raw, dump_module, syntax, true, debuginfo, binary, params)
2✔
308
    if highlighting[:native] && get(io, :color, false)::Bool
×
309
        print_native(io, d)
×
310
    else
311
        print(io, d)
×
312
    end
313
end
314
code_native(args...; kwargs...) = (@nospecialize; code_native(stdout, args...; kwargs...))
4✔
315

316
## colorized IR and assembly printing
317

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

320
function print_llvm(io::IO, code::String)
×
321
    buf = IOBuffer(code)
×
322
    for line in eachline(buf)
×
323
        m = match(r"^(\s*)((?:[^;]|;\")*)(.*)$", line)
×
324
        m === nothing && continue
×
325
        indent, tokens, comment = m.captures
×
326
        print(io, indent)
×
327
        print_llvm_tokens(io, tokens)
×
328
        printstyled_ll(io, comment, :comment)
×
329
        println(io)
×
330
    end
×
331
end
332

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

337
function print_llvm_tokens(io, tokens)
×
338
    m = match(r"^((?:[^\s:]+:)?)(\s*)(.*)", tokens)
×
339
    if m !== nothing
×
340
        label, spaces, tokens = m.captures
×
341
        printstyled_ll(io, label, :label, spaces)
×
342
    end
343
    m = match(r"^(%[^\s=]+)(\s*)=(\s*)(.*)", tokens)
×
344
    if m !== nothing
×
345
        result, spaces, spaces2, tokens = m.captures
×
346
        printstyled_ll(io, result, :variable, spaces)
×
347
        printstyled_ll(io, '=', :default, spaces2)
×
348
    end
349
    m = match(r"^([a-z]\w*)(\s*)(.*)", tokens)
×
350
    if m !== nothing
×
351
        inst, spaces, tokens = m.captures
×
352
        iskeyword = occursin(r"^(?:define|declare|type)$", inst) || occursin("=", tokens)
×
353
        printstyled_ll(io, inst, iskeyword ? :keyword : :instruction, spaces)
×
354
    end
355

356
    print_llvm_operands(io, tokens)
×
357
end
358

359
function print_llvm_operands(io, tokens)
×
360
    while !isempty(tokens)
×
361
        tokens = print_llvm_operand(io, tokens)
×
362
    end
×
363
    return tokens
×
364
end
365

366
function print_llvm_operand(io, tokens)
×
367
    islabel = false
×
368
    while !isempty(tokens)
×
369
        m = match(r"^,(\s*)(.*)", tokens)
×
370
        if m !== nothing
×
371
            spaces, tokens = m.captures
×
372
            printstyled_ll(io, ',', :default, spaces)
×
373
            break
×
374
        end
375
        m = match(r"^(\*+|=)(\s*)(.*)", tokens)
×
376
        if m !== nothing
×
377
            sym, spaces, tokens = m.captures
×
378
            printstyled_ll(io, sym, :default, spaces)
×
379
            continue
×
380
        end
381
        m = match(r"^(\"[^\"]*\")(\s*)(.*)", tokens)
×
382
        if m !== nothing
×
383
            str, spaces, tokens = m.captures
×
384
            printstyled_ll(io, str, :variable, spaces)
×
385
            continue
×
386
        end
387
        m = match(r"^([({\[<])(\s*)(.*)", tokens)
×
388
        if m !== nothing
×
389
            bracket, spaces, tokens = m.captures
×
390
            printstyled_ll(io, bracket, :bracket, spaces)
×
391
            tokens = print_llvm_operands(io, tokens) # enter
×
392
            continue
×
393
        end
394
        m = match(r"^([)}\]>])(\s*)(.*)", tokens)
×
395
        if m !== nothing
×
396
            bracket, spaces, tokens = m.captures
×
397
            printstyled_ll(io, bracket, :bracket, spaces)
×
398
            break # leave
×
399
        end
400

401
        m = match(r"^([^\s,*=(){}\[\]<>]+)(\s*)(.*)", tokens)
×
402
        m === nothing && break
×
403
        token, spaces, tokens = m.captures
×
404
        if occursin(llvm_types, token)
×
405
            printstyled_ll(io, token, :type)
×
406
            islabel = token == "label"
×
407
        elseif occursin(llvm_cond, token) # condition code is instruction-level
×
408
            printstyled_ll(io, token, :instruction)
×
409
        elseif occursin(num_regex, token)
×
410
            printstyled_ll(io, token, :number)
×
411
        elseif occursin(r"^@.+$", token)
×
412
            printstyled_ll(io, token, :funcname)
×
413
        elseif occursin(r"^%.+$", token)
×
414
            islabel |= occursin(r"^%[^\d].*$", token) & occursin(r"^\]", tokens)
×
415
            printstyled_ll(io, token, islabel ? :label : :variable)
×
416
            islabel = false
×
417
        elseif occursin(r"^[a-z]\w+$", token)
×
418
            printstyled_ll(io, token, :keyword)
×
419
        else
420
            printstyled_ll(io, token, :default)
×
421
        end
422
        print(io, spaces)
×
423
    end
×
424
    return tokens
×
425
end
426

427
function print_native(io::IO, code::String, arch::Symbol=sys_arch_category())
×
428
    archv = Val(arch)
×
429
    buf = IOBuffer(code)
×
430
    for line in eachline(buf)
×
431
        m = match(r"^(\s*)((?:[^;#/]|#\S|;\"|/[^/])*)(.*)$", line)
×
432
        m === nothing && continue
×
433
        indent, tokens, comment = m.captures
×
434
        print(io, indent)
×
435
        print_native_tokens(io, tokens, archv)
×
436
        printstyled_ll(io, comment, :comment)
×
437
        println(io)
×
438
    end
×
439
end
440

441
function sys_arch_category()
×
442
    if Sys.ARCH === :x86_64 || Sys.ARCH === :i686
×
443
        :x86
×
444
    elseif Sys.ARCH === :aarch64 || startswith(string(Sys.ARCH), "arm")
×
445
        :arm
×
446
    else
447
        :unsupported
×
448
    end
449
end
450

451
print_native_tokens(io, line, ::Val) = print(io, line)
×
452

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

458
function print_native_tokens(io, tokens, arch::Union{Val{:x86}, Val{:arm}})
×
459
    x86 = arch isa Val{:x86}
×
460
    m = match(r"^((?:[^\s:]+:|\"[^\"]+\":)?)(\s*)(.*)", tokens)
×
461
    if m !== nothing
×
462
        label, spaces, tokens = m.captures
×
463
        printstyled_ll(io, label, :label, spaces)
×
464
    end
465
    haslabel = false
×
466
    m = match(r"^([a-z][\w.]*)(\s*)(.*)", tokens)
×
467
    if m !== nothing
×
468
        instruction, spaces, tokens = m.captures
×
469
        printstyled_ll(io, instruction, :instruction, spaces)
×
470
        haslabel = occursin(r"^(?:bl?|bl?\.\w{2,5}|[ct]bn?z)?$", instruction)
×
471
    end
472

473
    isfuncname = false
×
474
    while !isempty(tokens)
×
475
        m = match(r"^([,:*])(\s*)(.*)", tokens)
×
476
        if m !== nothing
×
477
            sym, spaces, tokens = m.captures
×
478
            printstyled_ll(io, sym, :default, spaces)
×
479
            isfuncname = false
×
480
            continue
×
481
        end
482
        m = match(r"^([(){}\[\]])(\s*)(.*)", tokens)
×
483
        if m !== nothing
×
484
            bracket, spaces, tokens = m.captures
×
485
            printstyled_ll(io, bracket, :bracket, spaces)
×
486
            continue
×
487
        end
488
        m = match(r"^#([0-9a-fx.-]+)(\s*)(.*)", tokens)
×
489
        if !x86 && m !== nothing && occursin(num_regex, m.captures[1])
×
490
            num, spaces, tokens = m.captures
×
491
            printstyled_ll(io, "#" * num, :number, spaces)
×
492
            continue
×
493
        end
494

495
        m = match(r"^([^\s,:*(){}\[\]][^\s,:*/(){}\[\]]*)(\s*)(.*)", tokens)
×
496
        m === nothing && break
×
497
        token, spaces, tokens = m.captures
×
498
        if occursin(num_regex, token)
×
499
            printstyled_ll(io, token, :number)
×
500
        elseif x86 && occursin(x86_ptr, token) || occursin(avx512flags, token)
×
501
            printstyled_ll(io, token, :keyword)
×
502
            isfuncname = token == "offset"
×
503
        elseif !x86 && (occursin(arm_keywords, token) || occursin(arm_cond, token))
×
504
            printstyled_ll(io, token, :keyword)
×
505
        elseif occursin(r"^L.+$", token)
×
506
            printstyled_ll(io, token, :label)
×
507
        elseif occursin(r"^\$.+$", token)
×
508
            printstyled_ll(io, token, :funcname)
×
509
        elseif occursin(r"^%?(?:[a-z][\w.]+|\"[^\"]+\")$", token)
×
510
            islabel = haslabel & !occursin(',', tokens)
×
511
            printstyled_ll(io, token, islabel ? :label : isfuncname ? :funcname : :variable)
×
512
            isfuncname = false
×
513
        else
514
            printstyled_ll(io, token, :default)
×
515
        end
516
        print(io, spaces)
×
517
    end
×
518
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