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

JuliaLang / julia / #38177

14 Aug 2025 11:50AM UTC coverage: 78.503% (+0.7%) from 77.785%
#38177

push

local

web-flow
Narrow drive letter matching for `splitdrive` on Windows to one character, fixup docstring for `joinpath` (#58951)

Fixes #58929

Only a single letter followed by a colon is a valid drive prefix for
this format, any other length of string prior to a colon isn't a valid
drive in Windows:
https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats

Also the docstring for `joinpath` rendered Windows paths as unescaped,
meaning they could not be copied and pasted on the REPL.

48594 of 61901 relevant lines covered (78.5%)

8669158.51 hits per line

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

96.95
/stdlib/InteractiveUtils/src/codeview.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
# highlighting settings
4
const highlighting = Dict{Symbol, Bool}(
5
    :warntype => true,
6
    :llvm => true,
7
    :native => true,
8
)
9

10
const llstyle = Dict{Symbol, Tuple{Bool, Union{Symbol, Int}}}(
11
    :default     => (false, :normal), # e.g. comma, equal sign, unknown token
12
    :comment     => (false, :light_black),
13
    :label       => (false, :light_red),
14
    :instruction => ( true, :light_cyan),
15
    :type        => (false, :cyan),
16
    :number      => (false, :yellow),
17
    :bracket     => (false, :yellow),
18
    :variable    => (false, :normal), # e.g. variable, register
19
    :keyword     => (false, :light_magenta),
20
    :funcname    => (false, :light_yellow),
21
)
22

23
struct ArgInfo
24
    oc::Union{Core.OpaqueClosure,Nothing}
25
    tt::Type{<:Tuple}
26

27
    # Construct from a function object + argtypes
28
    function ArgInfo(@nospecialize(f), @nospecialize(t))
1✔
29
        if isa(f, Core.Builtin)
188✔
30
            throw(ArgumentError("argument is not a generic function"))
2✔
31
        elseif f isa Core.OpaqueClosure
186✔
32
            return new(f, Base.to_tuple_type(t))
5✔
33
        else
34
            return new(nothing, signature_type(f, t))
181✔
35
        end
36
    end
37

38
    # Construct from argtypes (incl. arg0)
39
    function ArgInfo(@nospecialize(argtypes::Union{Tuple,Type{<:Tuple}}))
40
        tt = Base.to_tuple_type(argtypes)
8✔
41
        return new(nothing, tt)
8✔
42
    end
43
end
44

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

50
# displaying type warnings
51

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

67
# True if one can be pretty certain that the compiler handles this union well,
68
# i.e. must be small with concrete types.
69
function is_expected_union(u::Union)
23✔
70
    Base.unionlen(u) < 4 || return false
23✔
71
    for x in Base.uniontypes(u)
23✔
72
        if !Base.isdispatchelem(x) || x == Core.Box
93✔
73
            return false
3✔
74
        end
75
    end
45✔
76
    return true
20✔
77
end
78

79
function print_warntype_codeinfo(io::IO, src::Core.CodeInfo, @nospecialize(rettype), nargs::Int; lineprinter, label_dynamic_calls)
58✔
80
    if src.slotnames !== nothing
29✔
81
        slotnames = Base.sourceinfo_slotnames(src)
29✔
82
        io = IOContext(io, :SOURCE_SLOTNAMES => slotnames)
29✔
83
        slottypes = src.slottypes
29✔
84
        nargs > 0 && println(io, "Arguments")
29✔
85
        for i = 1:length(slotnames)
29✔
86
            if i == nargs + 1
89✔
87
                println(io, "Locals")
7✔
88
            end
89
            print(io, "  ", slotnames[i])
89✔
90
            if isa(slottypes, Vector{Any})
89✔
91
                warntype_type_printer(io; type=slottypes[i], used=true)
89✔
92
            end
93
            println(io)
89✔
94
        end
149✔
95
    end
96
    print(io, "Body")
29✔
97
    warntype_type_printer(io; type=rettype, used=true)
29✔
98
    println(io)
29✔
99
    irshow_config = Base.IRShow.IRShowConfig(lineprinter(src), warntype_type_printer; label_dynamic_calls)
29✔
100
    Base.IRShow.show_ir(io, src, irshow_config)
29✔
101
    println(io)
29✔
102
end
103

104
function print_warntype_mi(io::IO, mi::Core.MethodInstance)
28✔
105
    println(io, mi)
28✔
106
    print(io, "  from ")
28✔
107
    println(io, mi.def)
28✔
108
    if !isempty(mi.sparam_vals)
28✔
109
        println(io, "Static Parameters")
5✔
110
        sig = mi.def.sig
5✔
111
        warn_color = Base.warn_color() # more mild user notification
5✔
112
        for i = 1:length(mi.sparam_vals)
5✔
113
            sig = sig::UnionAll
9✔
114
            name = sig.var.name
9✔
115
            val = mi.sparam_vals[i]
9✔
116
            print_highlighted(io::IO, v::String, color::Symbol) =
19✔
117
                if highlighting[:warntype]
118
                    Base.printstyled(io, v; color)
10✔
119
                else
120
                    Base.print(io, v)
×
121
                end
122
            if val isa TypeVar
9✔
123
                if val.lb === Union{}
3✔
124
                    print(io, "  ", name, " <: ")
1✔
125
                    print_highlighted(io, "$(val.ub)", warn_color)
1✔
126
                elseif val.ub === Any
2✔
127
                    print(io, "  ", sig.var.name, " >: ")
1✔
128
                    print_highlighted(io, "$(val.lb)", warn_color)
1✔
129
                else
130
                    print(io, "  ")
1✔
131
                    print_highlighted(io, "$(val.lb)", warn_color)
1✔
132
                    print(io, " <: ", sig.var.name, " <: ")
1✔
133
                    print_highlighted(io, "$(val.ub)", warn_color)
1✔
134
                end
135
            elseif val isa typeof(Vararg)
6✔
136
                print(io, "  ", name, "::")
1✔
137
                print_highlighted(io, "Int", warn_color)
1✔
138
            else
139
                print(io, "  ", sig.var.name, " = ")
5✔
140
                print_highlighted(io, "$(val)", :cyan) # show the "good" type
5✔
141
            end
142
            println(io)
9✔
143
            sig = sig.body
9✔
144
        end
9✔
145
    end
146
end
147

148
"""
149
    code_warntype([io::IO], f, types; debuginfo=:default)
150

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

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

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

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

166
See also: [`@code_warntype`](@ref), [`code_typed`](@ref), [`code_lowered`](@ref), [`code_llvm`](@ref), [`code_native`](@ref).
167
"""
168
function code_warntype(io::IO, arginfo::ArgInfo;
58✔
169
                       world=Base.get_world_counter(),
170
                       interp::Base.Compiler.AbstractInterpreter=Base.Compiler.NativeInterpreter(world),
171
                       debuginfo::Symbol=:default, optimize::Bool=false, kwargs...)
172
    (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) &&
29✔
173
        error("code reflection cannot be used from generated functions")
174
    debuginfo = Base.IRShow.debuginfo(debuginfo)
29✔
175
    lineprinter = Base.IRShow.__debuginfo[debuginfo]
29✔
176
    nargs::Int = 0
29✔
177
    if arginfo.oc !== nothing
29✔
178
        (; oc, tt) = arginfo
1✔
179
        isa(oc.source, Method) && (nargs = oc.source.nargs)
2✔
180
        print_warntype_codeinfo(io, Base.code_typed_opaque_closure(oc, tt)[1]..., nargs;
1✔
181
                                lineprinter, label_dynamic_calls = optimize)
182
        return nothing
1✔
183
    end
184
    tt = arginfo.tt
28✔
185
    matches = findall(tt, Base.Compiler.method_table(interp))
28✔
186
    matches === nothing && Base.raise_match_failure(:code_warntype, tt)
28✔
187
    for match in matches.matches
28✔
188
        match = match::Core.MethodMatch
28✔
189
        src = Base.Compiler.typeinf_code(interp, match, optimize)
56✔
190
        mi = Base.Compiler.specialize_method(match)
28✔
191
        mi.def isa Method && (nargs = (mi.def::Method).nargs)
28✔
192
        print_warntype_mi(io, mi)
28✔
193
        if src isa Core.CodeInfo
28✔
194
            print_warntype_codeinfo(io, src, src.rettype, nargs;
28✔
195
                                    lineprinter, label_dynamic_calls = optimize)
196
        else
197
            println(io, "  inference not successful")
×
198
        end
199
    end
28✔
200
    nothing
28✔
201
end
202
code_warntype(io::IO, @nospecialize(f), @nospecialize(tt=Base.default_tt(f)); kwargs...) = code_warntype(io, ArgInfo(f, tt); kwargs...)
56✔
203
code_warntype(io::IO, @nospecialize(argtypes::Union{Tuple,Type{<:Tuple}}); kwargs...) = code_warntype(io, ArgInfo(argtypes); kwargs...)
4✔
204
code_warntype(args...; kwargs...) = (@nospecialize; code_warntype(stdout, args...; kwargs...))
×
205

206
using Base: CodegenParams
207

208
const GENERIC_SIG_WARNING = "; WARNING: This code may not match what actually runs.\n"
209
const OC_MISMATCH_WARNING =
210
"""
211
; WARNING: The pre-inferred opaque closure is not callable with the given arguments
212
;          and will error on dispatch with this signature.
213
"""
214

215
# Printing code representations in IR and assembly
216

217
function _dump_function(arginfo::ArgInfo, native::Bool, wrapper::Bool,
166✔
218
                        raw::Bool, dump_module::Bool, syntax::Symbol,
219
                        optimize::Bool, debuginfo::Symbol, binary::Bool,
220
                        params::CodegenParams=CodegenParams(debug_info_kind=Cint(0), debug_info_level=Cint(2), safepoint_on_entry=raw, gcstack_arg=raw))
221
    ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions")
166✔
222
    warning = ""
159✔
223
    # get the MethodInstance for the method match
224
    if arginfo.oc === nothing
159✔
225
        world = Base.get_world_counter()
155✔
226
        match = Base._which(arginfo.tt; world)
155✔
227
        mi = Base.specialize_method(match)
154✔
228
        # TODO: use jl_is_cacheable_sig instead of isdispatchtuple
229
        isdispatchtuple(mi.specTypes) || (warning = GENERIC_SIG_WARNING)
154✔
230
    else
231
        (; oc, tt) = arginfo
4✔
232
        world = UInt64(oc.world)
4✔
233
        if !isdefined(oc.source, :source)
4✔
234
            # OC was constructed from inferred source. There's only one
235
            # specialization and we can't infer anything more precise either.
236
            world = oc.source.primary_world
4✔
237
            mi = oc.source.specializations::Core.MethodInstance
4✔
238
            Base.hasintersect(typeof(oc).parameters[1], tt) || (warning = OC_MISMATCH_WARNING)
4✔
239
        else
240
            mi = Base.specialize_method(oc.source, Tuple{typeof(oc.captures), tt.parameters...}, Core.svec())
×
241
            isdispatchtuple(mi.specTypes) || (warning = GENERIC_SIG_WARNING)
×
242
        end
243
    end
244
    # get the code for it
245
    if debuginfo === :default
158✔
246
        debuginfo = :source
110✔
247
    elseif debuginfo !== :source && debuginfo !== :none
48✔
248
        throw(ArgumentError("'debuginfo' must be either :source or :none"))
×
249
    end
250
    if native
158✔
251
        if syntax !== :att && syntax !== :intel
34✔
252
            throw(ArgumentError("'syntax' must be either :intel or :att"))
×
253
        end
254
        str = ""
34✔
255
        if !dump_module
34✔
256
            # if we don't want the module metadata, attempt to disassemble what our JIT has
257
            str = _dump_function_native_disassembly(mi, world, wrapper, syntax, debuginfo, binary)
5✔
258
        end
259
        if isempty(str)
34✔
260
            # if that failed (or we want metadata), use LLVM to generate more accurate assembly output
261
            if arginfo.oc === nothing
29✔
262
                src = Base.Compiler.typeinf_code(Base.Compiler.NativeInterpreter(world), mi, true)
58✔
263
            else
264
                src, rt = Base.get_oc_code_rt(nothing, arginfo.oc, arginfo.tt, true)
×
265
            end
266
            src isa Core.CodeInfo || error("failed to infer source for $mi")
29✔
267
            str = _dump_function_native_assembly(mi, src, wrapper, syntax, debuginfo, binary, raw, params)
29✔
268
        end
269
    else
270
        if arginfo.oc === nothing
124✔
271
            src = Base.Compiler.typeinf_code(Base.Compiler.NativeInterpreter(world), mi, true)
240✔
272
        else
273
            src, rt = Base.get_oc_code_rt(nothing, arginfo.oc, arginfo.tt, true)
4✔
274
        end
275
        src isa Core.CodeInfo || error("failed to infer source for $mi")
124✔
276
        str = _dump_function_llvm(mi, src, wrapper, !raw, dump_module, optimize, debuginfo, params)
124✔
277
    end
278
    str = warning * str
157✔
279
    return str
157✔
280
end
281

282
function _dump_function_native_disassembly(mi::Core.MethodInstance, world::UInt,
283
                                           wrapper::Bool, syntax::Symbol,
284
                                           debuginfo::Symbol, binary::Bool)
285
    str = @ccall jl_dump_method_asm(mi::Any, world::UInt, false::Bool, wrapper::Bool,
5✔
286
                                    syntax::Ptr{UInt8}, debuginfo::Ptr{UInt8},
287
                                    binary::Bool)::Ref{String}
288
    return str
5✔
289
end
290

291
struct LLVMFDump
292
    tsm::Ptr{Cvoid} # opaque
293
    f::Ptr{Cvoid} # opaque
294
end
295

296
function _dump_function_native_assembly(mi::Core.MethodInstance, src::Core.CodeInfo,
29✔
297
                                        wrapper::Bool, syntax::Symbol, debuginfo::Symbol,
298
                                        binary::Bool, raw::Bool, params::CodegenParams)
299
    llvmf_dump = Ref{LLVMFDump}()
29✔
300
    @ccall jl_get_llvmf_defn(llvmf_dump::Ptr{LLVMFDump}, mi::Any, src::Any, wrapper::Bool,
29✔
301
                             true::Bool, params::CodegenParams)::Cvoid
302
    llvmf_dump[].f == C_NULL && error("could not compile the specified method")
29✔
303
    str = @ccall jl_dump_function_asm(llvmf_dump::Ptr{LLVMFDump}, false::Bool,
28✔
304
                                      syntax::Ptr{UInt8}, debuginfo::Ptr{UInt8},
305
                                      binary::Bool, raw::Bool)::Ref{String}
306
    return str
28✔
307
end
308

309
function _dump_function_llvm(
124✔
310
        mi::Core.MethodInstance, src::Core.CodeInfo, wrapper::Bool,
311
        strip_ir_metadata::Bool, dump_module::Bool,
312
        optimize::Bool, debuginfo::Symbol,
313
        params::CodegenParams)
314
    llvmf_dump = Ref{LLVMFDump}()
124✔
315
    @ccall jl_get_llvmf_defn(llvmf_dump::Ptr{LLVMFDump}, mi::Any, src::Any,
124✔
316
                             wrapper::Bool, optimize::Bool, params::CodegenParams)::Cvoid
317
    llvmf_dump[].f == C_NULL && error("could not compile the specified method")
124✔
318
    str = @ccall jl_dump_function_ir(llvmf_dump::Ptr{LLVMFDump}, strip_ir_metadata::Bool,
124✔
319
                                     dump_module::Bool, debuginfo::Ptr{UInt8})::Ref{String}
320
    return str
124✔
321
end
322

323
"""
324
    code_llvm([io=stdout,], f, types; raw=false, dump_module=false, optimize=true, debuginfo=:default)
325

326
Prints the LLVM bitcodes generated for running the method matching the given generic
327
function and type signature to `io`.
328

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

334
See also: [`@code_llvm`](@ref), [`code_warntype`](@ref), [`code_typed`](@ref), [`code_lowered`](@ref), [`code_native`](@ref).
335
"""
336
function code_llvm(io::IO, arginfo::ArgInfo;
96✔
337
                   raw::Bool=false, dump_module::Bool=false, optimize::Bool=true, debuginfo::Symbol=:default,
338
                   params::CodegenParams=CodegenParams(debug_info_kind=Cint(0), debug_info_level=Cint(2), safepoint_on_entry=raw, gcstack_arg=raw))
339
    d = _dump_function(arginfo, false, false, raw, dump_module, :intel, optimize, debuginfo, false, params)
83✔
340
    if highlighting[:llvm] && get(io, :color, false)::Bool
80✔
341
        print_llvm(io, d)
1✔
342
    else
343
        print(io, d)
79✔
344
    end
345
end
346
code_llvm(io::IO, @nospecialize(argtypes::Union{Tuple,Type{<:Tuple}}); kwargs...) = code_llvm(io, ArgInfo(argtypes); kwargs...)
6✔
347
code_llvm(io::IO, @nospecialize(f), @nospecialize(types=Base.default_tt(f)); kwargs...) = code_llvm(io, ArgInfo(f, types); kwargs...)
164✔
348
code_llvm(args...; kwargs...) = (@nospecialize; code_llvm(stdout, args...; kwargs...))
12✔
349

350
"""
351
    code_native([io=stdout,], f, types; syntax=:intel, debuginfo=:default, binary=false, dump_module=true)
352

353
Prints the native assembly instructions generated for running the method matching the given
354
generic function and type signature to `io`.
355

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

362
See also: [`@code_native`](@ref), [`code_warntype`](@ref), [`code_typed`](@ref), [`code_lowered`](@ref), [`code_llvm`](@ref).
363
"""
364
function code_native(io::IO, arginfo::ArgInfo;
38✔
365
                     dump_module::Bool=true, syntax::Symbol=:intel, raw::Bool=false,
366
                     debuginfo::Symbol=:default, binary::Bool=false,
367
                     params::CodegenParams=CodegenParams(debug_info_kind=Cint(0), debug_info_level=Cint(2), safepoint_on_entry=raw, gcstack_arg=raw))
368
    d = _dump_function(arginfo, true, false, raw, dump_module, syntax, true, debuginfo, binary, params)
36✔
369
    if highlighting[:native] && get(io, :color, false)::Bool
33✔
370
        print_native(io, d)
1✔
371
    else
372
        print(io, d)
32✔
373
    end
374
end
375
code_native(io::IO, @nospecialize(argtypes::Union{Tuple,Type{<:Tuple}}); kwargs...) = code_native(io, ArgInfo(argtypes); kwargs...)
6✔
376
code_native(io::IO, @nospecialize(f), @nospecialize(types=Base.default_tt(f)); kwargs...) = code_native(io, ArgInfo(f, types); kwargs...)
74✔
377
code_native(args...; kwargs...) = (@nospecialize; code_native(stdout, args...; kwargs...))
14✔
378

379
## colorized IR and assembly printing
380

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

383
function print_llvm(io::IO, code::String)
36✔
384
    buf = IOBuffer(code)
36✔
385
    for line in eachline(buf)
72✔
386
        m = match(r"^(\s*)((?:[^;]|;\")*)(.*)$", line)
45✔
387
        m === nothing && continue
45✔
388
        indent, tokens, comment = m.captures
45✔
389
        print(io, indent)
90✔
390
        print_llvm_tokens(io, tokens)
90✔
391
        printstyled_ll(io, comment, :comment)
90✔
392
        println(io)
45✔
393
    end
45✔
394
end
395

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

400
function print_llvm_tokens(io, tokens)
45✔
401
    m = match(r"^((?:[^\"\s:]+:|\"[^\"]*\":)?)(\s*)(.*)", tokens)
45✔
402
    if m !== nothing
45✔
403
        label, spaces, tokens = m.captures
45✔
404
        printstyled_ll(io, label, :label, spaces)
90✔
405
    end
406
    m = match(r"^(%[^\s=]+)(\s*)=(\s*)(.*)", tokens)
45✔
407
    if m !== nothing
45✔
408
        result, spaces, spaces2, tokens = m.captures
22✔
409
        printstyled_ll(io, result, :variable, spaces)
44✔
410
        printstyled_ll(io, '=', :default, spaces2)
44✔
411
    end
412
    m = match(r"^([a-z]\w*)(\s*)(.*)", tokens)
45✔
413
    if m !== nothing
45✔
414
        inst, spaces, tokens = m.captures
35✔
415
        iskeyword = occursin(r"^(?:define|declare|type)$", inst) || occursin("=", tokens)
66✔
416
        printstyled_ll(io, inst, iskeyword ? :keyword : :instruction, spaces)
70✔
417
    end
418

419
    print_llvm_operands(io, tokens)
45✔
420
end
421

422
function print_llvm_operands(io, tokens)
×
423
    while !isempty(tokens)
246✔
424
        tokens = print_llvm_operand(io, tokens)
108✔
425
    end
108✔
426
    return tokens
69✔
427
end
428

429
function print_llvm_operand(io, tokens)
108✔
430
    islabel = false
108✔
431
    while !isempty(tokens)
644✔
432
        m = match(r"^,(\s*)(.*)", tokens)
270✔
433
        if m !== nothing
270✔
434
            spaces, tokens = m.captures
32✔
435
            printstyled_ll(io, ',', :default, spaces)
64✔
436
            break
32✔
437
        end
438
        m = match(r"^(\*+|=)(\s*)(.*)", tokens)
238✔
439
        if m !== nothing
238✔
440
            sym, spaces, tokens = m.captures
16✔
441
            printstyled_ll(io, sym, :default, spaces)
32✔
442
            continue
16✔
443
        end
444
        m = match(r"^(\"[^\"]*\")(\s*)(.*)", tokens)
222✔
445
        if m !== nothing
222✔
446
            str, spaces, tokens = m.captures
3✔
447
            printstyled_ll(io, str, :variable, spaces)
6✔
448
            continue
3✔
449
        end
450
        m = match(r"^([({\[<])(\s*)(.*)", tokens)
219✔
451
        if m !== nothing
219✔
452
            bracket, spaces, tokens = m.captures
24✔
453
            printstyled_ll(io, bracket, :bracket, spaces)
48✔
454
            tokens = print_llvm_operands(io, tokens) # enter
48✔
455
            continue
24✔
456
        end
457
        m = match(r"^([)}\]>])(\s*)(.*)", tokens)
195✔
458
        if m !== nothing
195✔
459
            bracket, spaces, tokens = m.captures
24✔
460
            printstyled_ll(io, bracket, :bracket, spaces)
48✔
461
            break # leave
24✔
462
        end
463

464
        m = match(r"^([^\s,*=(){}\[\]<>]+)(\s*)(.*)", tokens)
171✔
465
        m === nothing && break
171✔
466
        token, spaces, tokens = m.captures
171✔
467
        if occursin(llvm_types, token)
171✔
468
            printstyled_ll(io, token, :type)
52✔
469
            islabel = token == "label"
52✔
470
        elseif occursin(llvm_cond, token) # condition code is instruction-level
119✔
471
            printstyled_ll(io, token, :instruction)
1✔
472
        elseif occursin(num_regex, token)
118✔
473
            printstyled_ll(io, token, :number)
31✔
474
        elseif occursin(r"^@.+$", token)
87✔
475
            printstyled_ll(io, token, :funcname)
5✔
476
        elseif occursin(r"^%.+$", token)
82✔
477
            islabel |= occursin(r"^%[^\d].*$", token) & occursin(r"^\]", tokens)
38✔
478
            printstyled_ll(io, token, islabel ? :label : :variable)
38✔
479
            islabel = false
38✔
480
        elseif occursin(r"^[a-z]\w+$", token)
44✔
481
            printstyled_ll(io, token, :keyword)
39✔
482
        else
483
            printstyled_ll(io, token, :default)
5✔
484
        end
485
        print(io, spaces)
171✔
486
    end
214✔
487
    return tokens
108✔
488
end
489

490
function print_native(io::IO, code::String, arch::Symbol=sys_arch_category())
58✔
491
    archv = Val(arch)
59✔
492
    buf = IOBuffer(code)
58✔
493
    for line in eachline(buf)
116✔
494
        m = match(r"^(\s*)((?:[^;#/]|#\S|;\"|/[^/])*)(.*)$", line)
92✔
495
        m === nothing && continue
92✔
496
        indent, tokens, comment = m.captures
92✔
497
        print(io, indent)
184✔
498
        print_native_tokens(io, tokens, archv)
92✔
499
        printstyled_ll(io, comment, :comment)
184✔
500
        println(io)
92✔
501
    end
92✔
502
end
503

504
function sys_arch_category()
505
    if Sys.ARCH === :x86_64 || Sys.ARCH === :i686
1✔
506
        :x86
507
    elseif Sys.ARCH === :aarch64 || startswith(string(Sys.ARCH), "arm")
×
508
        :arm
509
    else
510
        :unsupported
511
    end
512
end
513

514
print_native_tokens(io, line, ::Val) = print(io, line)
1✔
515

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

521
function print_native_tokens(io, tokens, arch::Union{Val{:x86}, Val{:arm}})
91✔
522
    x86 = arch isa Val{:x86}
91✔
523
    m = match(r"^((?:[^\s:]+:|\"[^\"]+\":)?)(\s*)(.*)", tokens)
91✔
524
    if m !== nothing
91✔
525
        label, spaces, tokens = m.captures
91✔
526
        printstyled_ll(io, label, :label, spaces)
182✔
527
    end
528
    haslabel = false
91✔
529
    m = match(r"^([a-z][\w.]*)(\s*)(.*)", tokens)
91✔
530
    if m !== nothing
91✔
531
        instruction, spaces, tokens = m.captures
57✔
532
        printstyled_ll(io, instruction, :instruction, spaces)
114✔
533
        haslabel = occursin(r"^(?:bl?|bl?\.\w{2,5}|[ct]bn?z)?$", instruction)
57✔
534
    end
535

536
    isfuncname = false
91✔
537
    while !isempty(tokens)
902✔
538
        m = match(r"^([,:*])(\s*)(.*)", tokens)
360✔
539
        if m !== nothing
360✔
540
            sym, spaces, tokens = m.captures
89✔
541
            printstyled_ll(io, sym, :default, spaces)
178✔
542
            isfuncname = false
89✔
543
            continue
89✔
544
        end
545
        m = match(r"^([(){}\[\]])(\s*)(.*)", tokens)
271✔
546
        if m !== nothing
271✔
547
            bracket, spaces, tokens = m.captures
58✔
548
            printstyled_ll(io, bracket, :bracket, spaces)
116✔
549
            continue
58✔
550
        end
551
        m = match(r"^#([0-9a-fx.-]+)(\s*)(.*)", tokens)
213✔
552
        if !x86 && m !== nothing && occursin(num_regex, m.captures[1])
213✔
553
            num, spaces, tokens = m.captures
6✔
554
            printstyled_ll(io, "#" * num, :number, spaces)
12✔
555
            continue
6✔
556
        end
557

558
        m = match(r"^([^\s,:*(){}\[\]][^\s,:*/(){}\[\]]*)(\s*)(.*)", tokens)
207✔
559
        m === nothing && break
207✔
560
        token, spaces, tokens = m.captures
207✔
561
        if occursin(num_regex, token)
207✔
562
            printstyled_ll(io, token, :number)
20✔
563
        elseif x86 && occursin(x86_ptr, token) || occursin(avx512flags, token)
314✔
564
            printstyled_ll(io, token, :keyword)
23✔
565
            isfuncname = token == "offset"
23✔
566
        elseif !x86 && (occursin(arm_keywords, token) || occursin(arm_cond, token))
203✔
567
            printstyled_ll(io, token, :keyword)
7✔
568
        elseif occursin(r"^L.+$", token)
157✔
569
            printstyled_ll(io, token, :label)
5✔
570
        elseif occursin(r"^\$.+$", token)
152✔
571
            printstyled_ll(io, token, :funcname)
2✔
572
        elseif occursin(r"^%?(?:[a-z][\w.]+|\"[^\"]+\")$", token)
150✔
573
            islabel = haslabel & !occursin(',', tokens)
190✔
574
            printstyled_ll(io, token, islabel ? :label : isfuncname ? :funcname : :variable)
230✔
575
            isfuncname = false
115✔
576
        else
577
            printstyled_ll(io, token, :default)
35✔
578
        end
579
        print(io, spaces)
207✔
580
    end
360✔
581
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