From 32d84f8c1eb4982eb580f3248a94699cc72258f0 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 13:14:37 +0200 Subject: [PATCH 01/27] Add DIBuilder wrapper with lifecycle management. Introduces the `DIBuilder` type wrapping `LLVMDIBuilderRef`, with `allow_unresolved` kwarg, do-block form, `dispose`, `finalize!`, and `finalize_subprogram!`. Follows the existing `IRBuilder` pattern. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/src/lib/metadata.md | 10 ++++++ src/debuginfo.jl | 73 ++++++++++++++++++++++++++++++++++++++++ test/debuginfo.jl | 21 ++++++++++++ 3 files changed, 104 insertions(+) diff --git a/docs/src/lib/metadata.md b/docs/src/lib/metadata.md index 36aba9d7..b2905d89 100644 --- a/docs/src/lib/metadata.md +++ b/docs/src/lib/metadata.md @@ -37,6 +37,16 @@ push!(::NamedMDNode, ::MDNode) DINode ``` +### Builder + +```@docs +DIBuilder +DIBuilder(::LLVM.Module) +dispose(::DIBuilder) +LLVM.finalize!(::DIBuilder) +LLVM.finalize_subprogram! +``` + ### Location information ```@docs diff --git a/src/debuginfo.jl b/src/debuginfo.jl index e6f7483d..62b3ac4d 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -1,3 +1,66 @@ +## debug info builder + +export DIBuilder + +""" + DIBuilder + +A builder for constructing debug information metadata. + +This object needs to be disposed of using [`dispose`](@ref), after first having +called [`finalize!`](@ref). +""" +@checked struct DIBuilder + ref::API.LLVMDIBuilderRef +end + +Base.unsafe_convert(::Type{API.LLVMDIBuilderRef}, builder::DIBuilder) = + mark_use(builder).ref + +""" + DIBuilder(mod::Module; allow_unresolved::Bool=true) + +Create a new debug info builder that emits metadata into `mod`. + +When `allow_unresolved` is `true` (the default), the builder collects unresolved +metadata nodes attached to the module so that cycles can be resolved during a +call to [`finalize!`](@ref). When `false`, the builder errors on unresolved +nodes instead. +""" +function DIBuilder(mod::Module; allow_unresolved::Bool=true) + ref = allow_unresolved ? API.LLVMCreateDIBuilder(mod) : + API.LLVMCreateDIBuilderDisallowUnresolved(mod) + mark_alloc(DIBuilder(ref)) +end + +""" + dispose(builder::DIBuilder) + +Dispose of a debug info builder. [`finalize!`](@ref) must be called first. +""" +dispose(builder::DIBuilder) = mark_dispose(API.LLVMDisposeDIBuilder, builder) + +function DIBuilder(f::Core.Function, args...; kwargs...) + builder = DIBuilder(args...; kwargs...) + try + f(builder) + finally + dispose(builder) + end +end + +Base.show(io::IO, builder::DIBuilder) = @printf(io, "DIBuilder(%p)", builder.ref) + +""" + finalize!(builder::DIBuilder) + +Resolve any unresolved metadata nodes and mark all compile units finalized. +Must be called before [`dispose`](@ref) and before using the emitted debug +information. +""" +finalize!(builder::DIBuilder) = API.LLVMDIBuilderFinalize(builder) + + ## location information export DILocation, line, column, scope, inlined_at @@ -286,6 +349,7 @@ flags(typ::DIType) = API.LLVMDITypeGetFlags(typ) ## subprogram export DISubProgram, line +@public finalize_subprogram! """ DISubProgram @@ -304,6 +368,15 @@ Get the line number of the given subprogram. """ line(subprogram::DISubProgram) = Int(API.LLVMDISubprogramGetLine(subprogram)) +""" + finalize_subprogram!(builder::DIBuilder, sp::DISubProgram) + +Finalize the given subprogram, allowing later changes to be disallowed. Must be +called before [`finalize!`](@ref). +""" +finalize_subprogram!(builder::DIBuilder, sp::DISubProgram) = + API.LLVMDIBuilderFinalizeSubprogram(builder, sp) + ## compile unit diff --git a/test/debuginfo.jl b/test/debuginfo.jl index aeb79ed8..1188cde3 100644 --- a/test/debuginfo.jl +++ b/test/debuginfo.jl @@ -2,6 +2,27 @@ DEBUG_METADATA_VERSION() +@testset "DIBuilder lifecycle" begin + @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin + dib = DIBuilder(mod) + LLVM.finalize!(dib) + dispose(dib) + end + + @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin + dib = DIBuilder(mod; allow_unresolved=false) + LLVM.finalize!(dib) + dispose(dib) + end + + # do-block form + @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin + DIBuilder(mod) do dib + LLVM.finalize!(dib) + end + end +end + @dispose ctx=Context() begin mod = parse(LLVM.Module, """ define void @foo() !dbg !15 { From f03148f209ac859b491a681d22a7906617057bf6 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 13:17:16 +0200 Subject: [PATCH 02/27] Add file!, compileunit!, dimodule!, and namespace! factories. Introduces the first DIBuilder factory helpers, along with the DIModule and DINamespace metadata types (both register as scopes). The compileunit! signature mirrors PR #152's compilationunit! with the producer/split-name/emission-kind/sysroot/sdk knobs exposed as kwargs. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/src/lib/metadata.md | 16 ++++++ src/debuginfo.jl | 120 +++++++++++++++++++++++++++++++++++++++ test/debuginfo.jl | 30 ++++++++++ 3 files changed, 166 insertions(+) diff --git a/docs/src/lib/metadata.md b/docs/src/lib/metadata.md index b2905d89..75eb3068 100644 --- a/docs/src/lib/metadata.md +++ b/docs/src/lib/metadata.md @@ -83,6 +83,21 @@ DIFile directory filename source +LLVM.file! +``` + +### Module + +```@docs +LLVM.DIModule +LLVM.dimodule! +``` + +### Namespace + +```@docs +LLVM.DINamespace +LLVM.namespace! ``` ### Type @@ -107,6 +122,7 @@ line(::DISubProgram) ```@docs DICompileUnit +LLVM.compileunit! ``` ### Other diff --git a/src/debuginfo.jl b/src/debuginfo.jl index 62b3ac4d..edc4cd4c 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -235,6 +235,7 @@ abstract type DILocalScope <: DIScope end ## file export DIFile, directory, filename, source +@public file! """ DIFile @@ -246,6 +247,17 @@ A file in the source code. end register(DIFile, API.LLVMDIFileMetadataKind) +""" + file!(builder::DIBuilder, filename::AbstractString, directory::AbstractString) -> DIFile + +Create a new [`DIFile`](@ref) describing the given source file. +""" +function file!(builder::DIBuilder, filename::AbstractString, directory::AbstractString) + DIFile(API.LLVMDIBuilderCreateFile(builder, + filename, Csize_t(length(filename)), + directory, Csize_t(length(directory)))) +end + """ directory(file::DIFile) @@ -381,6 +393,7 @@ finalize_subprogram!(builder::DIBuilder, sp::DISubProgram) = ## compile unit export DICompileUnit +@public compileunit! """ DICompileUnit @@ -392,6 +405,113 @@ A compilation unit in the source code. end register(DICompileUnit, API.LLVMDICompileUnitMetadataKind) +""" + compileunit!(builder::DIBuilder, lang, file::DIFile, producer::AbstractString; + optimized::Bool=true, flags::AbstractString="", + runtime_version::Integer=0, + split_name::Union{AbstractString,Nothing}=nothing, + emission_kind=API.LLVMDWARFEmissionFull, + dwo_id::Integer=0, + split_debug_inlining::Bool=true, + debug_info_for_profiling::Bool=false, + sysroot::AbstractString="", sdk::AbstractString="") -> DICompileUnit + +Create a new [`DICompileUnit`](@ref). `lang` is a `LLVMDWARFSourceLanguage` +value (e.g. `LLVM.API.LLVMDWARFSourceLanguageJulia`). +""" +function compileunit!(builder::DIBuilder, lang, file::DIFile, producer::AbstractString; + optimized::Bool=true, + flags::AbstractString="", + runtime_version::Integer=0, + split_name::Union{AbstractString,Nothing}=nothing, + emission_kind=API.LLVMDWARFEmissionFull, + dwo_id::Integer=0, + split_debug_inlining::Bool=true, + debug_info_for_profiling::Bool=false, + sysroot::AbstractString="", + sdk::AbstractString="") + split_name_ptr = split_name === nothing ? C_NULL : split_name + split_name_len = split_name === nothing ? Csize_t(0) : Csize_t(length(split_name)) + DICompileUnit(API.LLVMDIBuilderCreateCompileUnit( + builder, lang, file, + producer, Csize_t(length(producer)), + optimized, + flags, Csize_t(length(flags)), + Cuint(runtime_version), + split_name_ptr, split_name_len, + emission_kind, + Cuint(dwo_id), + split_debug_inlining, + debug_info_for_profiling, + sysroot, Csize_t(length(sysroot)), + sdk, Csize_t(length(sdk)))) +end + + +## module + +export DIModule +@public dimodule! + +""" + DIModule + +A module in the source code (Clang modules / Fortran modules / Swift modules). +""" +@checked struct DIModule <: DIScope + ref::API.LLVMMetadataRef +end +register(DIModule, API.LLVMDIModuleMetadataKind) + +""" + dimodule!(builder::DIBuilder, parent_scope::DIScope, name::AbstractString; + config_macros::AbstractString="", include_path::AbstractString="", + api_notes_file::AbstractString="") -> DIModule + +Create a new [`DIModule`](@ref) describing a module in the source code. +""" +function dimodule!(builder::DIBuilder, parent_scope::DIScope, name::AbstractString; + config_macros::AbstractString="", + include_path::AbstractString="", + api_notes_file::AbstractString="") + DIModule(API.LLVMDIBuilderCreateModule( + builder, parent_scope, + name, Csize_t(length(name)), + config_macros, Csize_t(length(config_macros)), + include_path, Csize_t(length(include_path)), + api_notes_file, Csize_t(length(api_notes_file)))) +end + + +## namespace + +export DINamespace +@public namespace! + +""" + DINamespace + +A namespace in the source code. +""" +@checked struct DINamespace <: DIScope + ref::API.LLVMMetadataRef +end +register(DINamespace, API.LLVMDINamespaceMetadataKind) + +""" + namespace!(builder::DIBuilder, parent_scope::DIScope, name::AbstractString; + export_symbols::Bool=false) -> DINamespace + +Create a new [`DINamespace`](@ref) describing a namespace in the source code. +""" +function namespace!(builder::DIBuilder, parent_scope::DIScope, name::AbstractString; + export_symbols::Bool=false) + DINamespace(API.LLVMDIBuilderCreateNameSpace( + builder, parent_scope, + name, Csize_t(length(name)), + export_symbols)) +end + ## other diff --git a/test/debuginfo.jl b/test/debuginfo.jl index 1188cde3..2b55f07b 100644 --- a/test/debuginfo.jl +++ b/test/debuginfo.jl @@ -23,6 +23,36 @@ DEBUG_METADATA_VERSION() end end +@testset "DIBuilder: file/compile-unit/module/namespace" begin + @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin + DIBuilder(mod) do dib + file = LLVM.file!(dib, "test.jl", "/tmp") + @test file isa DIFile + @test LLVM.filename(file) == "test.jl" + @test LLVM.directory(file) == "/tmp" + + cu = LLVM.compileunit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, + file, "LLVM.jl Tests") + @test cu isa DICompileUnit + + ns = LLVM.namespace!(dib, cu, "MyNamespace") + @test ns isa DINamespace + @test LLVM.name(ns) == "MyNamespace" + + dm = LLVM.dimodule!(dib, cu, "MyModule") + @test dm isa DIModule + @test LLVM.name(dm) == "MyModule" + + LLVM.finalize!(dib) + end + + # emitted DWARF should round-trip as text IR (compile unit is retained) + ir = string(mod) + @test occursin("DICompileUnit", ir) + @test occursin("DIFile", ir) + end +end + @dispose ctx=Context() begin mod = parse(LLVM.Module, """ define void @foo() !dbg !15 { From 4f664a92bb9246347196158721ccb19a90a1bbf1 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 13:18:36 +0200 Subject: [PATCH 03/27] Add DILexicalBlock, DILexicalBlockFile, and their factories. Adds two new DILocalScope subtypes and the matching lexicalblock!/ lexicalblockfile! builder methods. Tested alongside DILocation for nested scopes and inlined_at chains. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/src/lib/metadata.md | 9 +++++++ src/debuginfo.jl | 51 ++++++++++++++++++++++++++++++++++++++++ test/debuginfo.jl | 29 +++++++++++++++++++++++ 3 files changed, 89 insertions(+) diff --git a/docs/src/lib/metadata.md b/docs/src/lib/metadata.md index 75eb3068..bb779c92 100644 --- a/docs/src/lib/metadata.md +++ b/docs/src/lib/metadata.md @@ -86,6 +86,15 @@ source LLVM.file! ``` +### Lexical Block + +```@docs +LLVM.DILexicalBlock +LLVM.DILexicalBlockFile +LLVM.lexicalblock! +LLVM.lexicalblockfile! +``` + ### Module ```@docs diff --git a/src/debuginfo.jl b/src/debuginfo.jl index edc4cd4c..d6850de5 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -483,6 +483,57 @@ function dimodule!(builder::DIBuilder, parent_scope::DIScope, name::AbstractStri end +## lexical block + +export DILexicalBlock, DILexicalBlockFile +@public lexicalblock!, lexicalblockfile! + +""" + DILexicalBlock + +A lexical block (a nested scope, typically a compound statement) in the source code. +""" +@checked struct DILexicalBlock <: DILocalScope + ref::API.LLVMMetadataRef +end +register(DILexicalBlock, API.LLVMDILexicalBlockMetadataKind) + +""" + DILexicalBlockFile + +A lexical block that changes the current source file, e.g. due to an `#include`. +""" +@checked struct DILexicalBlockFile <: DILocalScope + ref::API.LLVMMetadataRef +end +register(DILexicalBlockFile, API.LLVMDILexicalBlockFileMetadataKind) + +""" + lexicalblock!(builder::DIBuilder, scope::DIScope, file::DIFile, + line::Integer, column::Integer) -> DILexicalBlock + +Create a new [`DILexicalBlock`](@ref) describing a nested source scope. +""" +function lexicalblock!(builder::DIBuilder, scope::DIScope, file::DIFile, + line::Integer, column::Integer) + DILexicalBlock(API.LLVMDIBuilderCreateLexicalBlock( + builder, scope, file, Cuint(line), Cuint(column))) +end + +""" + lexicalblockfile!(builder::DIBuilder, scope::DIScope, file::DIFile, + discriminator::Integer=0) -> DILexicalBlockFile + +Create a new [`DILexicalBlockFile`](@ref) for tracking source-file changes +within a lexical scope. +""" +function lexicalblockfile!(builder::DIBuilder, scope::DIScope, file::DIFile, + discriminator::Integer=0) + DILexicalBlockFile(API.LLVMDIBuilderCreateLexicalBlockFile( + builder, scope, file, Cuint(discriminator))) +end + + ## namespace export DINamespace diff --git a/test/debuginfo.jl b/test/debuginfo.jl index 2b55f07b..01d058b8 100644 --- a/test/debuginfo.jl +++ b/test/debuginfo.jl @@ -53,6 +53,35 @@ end end end +@testset "DIBuilder: lexical blocks" begin + @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin + DIBuilder(mod) do dib + file = LLVM.file!(dib, "test.jl", "/tmp") + cu = LLVM.compileunit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, + file, "LLVM.jl Tests") + + lb = LLVM.lexicalblock!(dib, cu, file, 3, 5) + @test lb isa DILexicalBlock + + lbf = LLVM.lexicalblockfile!(dib, lb, file, 0) + @test lbf isa DILexicalBlockFile + + # DILocation with lexical block scope + loc = DILocation(10, 20, lb) + @test LLVM.line(loc) == 10 + @test LLVM.column(loc) == 20 + @test LLVM.scope(loc) == lb + + # inlined_at chain + outer = DILocation(5, 1, cu) + inner = DILocation(10, 20, lb, outer) + @test LLVM.inlined_at(inner) == outer + + LLVM.finalize!(dib) + end + end +end + @dispose ctx=Context() begin mod = parse(LLVM.Module, """ define void @foo() !dbg !15 { From 59c4a79f4aba00a33cb658c89b26618fe756a46c Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 13:27:59 +0200 Subject: [PATCH 04/27] Add DIBuilder type-creation factories. Adds the full set of type constructors exposed by llvm-c/DebugInfo.h: basic, derived (pointer/typedef/qualified/artificial/member/bitfield/ static/member-pointer/inheritance/reference), composite (struct/union/ class/array/vector/enumeration/forward-decl/replaceable), and subroutine. Also adds getorcreatesubrange! and registers DIEnumerator and DISubrange as metadata types. align(::DIType) and tag(::DINode) accessors round out the read API. LLVM-21-only additions (settype!, subrangetype!, dynamicarraytype!, enumeratorarb!) are gated behind @static if version() >= v"21". Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/src/lib/metadata.md | 48 ++++ src/debuginfo.jl | 563 ++++++++++++++++++++++++++++++++++++++- test/debuginfo.jl | 91 +++++++ 3 files changed, 701 insertions(+), 1 deletion(-) diff --git a/docs/src/lib/metadata.md b/docs/src/lib/metadata.md index bb779c92..b7cc2d08 100644 --- a/docs/src/lib/metadata.md +++ b/docs/src/lib/metadata.md @@ -118,6 +118,54 @@ Base.sizeof(::DIType) offset(::DIType) line(::DIType) flags(::DIType) +LLVM.align +LLVM.DIEnumerator +LLVM.DISubrange +``` + +Built-in factories for primitive types: + +```@docs +LLVM.basictype! +LLVM.unspecifiedtype! +LLVM.nullptrtype! +``` + +Derived types (pointers, qualifiers, members, inheritance, ...): + +```@docs +LLVM.pointertype! +LLVM.referencetype! +LLVM.typedeftype! +LLVM.qualifiedtype! +LLVM.artificialtype! +LLVM.objectpointertype! +LLVM.membertype! +LLVM.bitfieldmembertype! +LLVM.staticmembertype! +LLVM.memberpointertype! +LLVM.inheritance! +``` + +Composite types: + +```@docs +LLVM.structtype! +LLVM.uniontype! +LLVM.classtype! +LLVM.arraytype! +LLVM.vectortype! +LLVM.enumerator! +LLVM.enumerationtype! +LLVM.forwarddecl! +LLVM.replaceablecompositetype! +LLVM.getorcreatesubrange! +``` + +Subroutine types: + +```@docs +LLVM.subroutinetype! ``` ### Subprogram diff --git a/src/debuginfo.jl b/src/debuginfo.jl index d6850de5..618d4bf1 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -297,7 +297,14 @@ end ## type -export DIType, name, offset, line, flags +export DIType, DIEnumerator, DISubrange, name, offset, line, flags +@public align, + basictype!, unspecifiedtype!, pointertype!, referencetype!, nullptrtype!, + typedeftype!, qualifiedtype!, artificialtype!, objectpointertype!, + inheritance!, membertype!, bitfieldmembertype!, staticmembertype!, + memberpointertype!, structtype!, uniontype!, classtype!, arraytype!, + vectortype!, enumerationtype!, enumerator!, forwarddecl!, + replaceablecompositetype!, subroutinetype!, getorcreatesubrange! """ DIType @@ -317,6 +324,26 @@ for typ in (:Basic, :Derived, :Composite, :Subroutine) end end +""" + DIEnumerator + +A single enumerator value in an enumeration type. +""" +@checked struct DIEnumerator <: DINode + ref::API.LLVMMetadataRef +end +register(DIEnumerator, API.LLVMDIEnumeratorMetadataKind) + +""" + DISubrange + +A subrange describing one dimension of an array or vector type. +""" +@checked struct DISubrange <: DINode + ref::API.LLVMMetadataRef +end +register(DISubrange, API.LLVMDISubrangeMetadataKind) + """ name(typ::DIType) @@ -357,6 +384,540 @@ Get the flags of the given type. """ flags(typ::DIType) = API.LLVMDITypeGetFlags(typ) +""" + align(typ::DIType) + +Get the alignment in bits of the given type. +""" +align(typ::DIType) = Int(API.LLVMDITypeGetAlignInBits(typ)) + +""" + tag(node::DINode) + +Get the DWARF tag of the given node, or `0` if none. +""" +tag(node::DINode) = Int(API.LLVMGetDINodeTag(node)) + + +# basic types + +""" + basictype!(builder::DIBuilder, name::AbstractString, size_in_bits::Integer, + encoding::Integer; flags=API.LLVMDIFlagZero) -> DIBasicType + +Create a new [`DIBasicType`](@ref), such as an integer or floating-point type. +`encoding` is a `DW_ATE_*` value (see the DWARF standard). +""" +function basictype!(builder::DIBuilder, name::AbstractString, size_in_bits::Integer, + encoding::Integer; flags=API.LLVMDIFlagZero) + DIBasicType(API.LLVMDIBuilderCreateBasicType( + builder, name, Csize_t(length(name)), + UInt64(size_in_bits), Cuint(encoding), flags)) +end + +""" + unspecifiedtype!(builder::DIBuilder, name::AbstractString) -> DIBasicType + +Create a new unspecified type (`DW_TAG_unspecified_type`), e.g. a C++ `decltype(nullptr)`. +""" +function unspecifiedtype!(builder::DIBuilder, name::AbstractString) + DIBasicType(API.LLVMDIBuilderCreateUnspecifiedType( + builder, name, Csize_t(length(name)))) +end + + +# derived types + +""" + pointertype!(builder::DIBuilder, pointee_type::DIType, size_in_bits::Integer; + align_in_bits::Integer=0, address_space::Integer=0, + name::AbstractString="") -> DIDerivedType + +Create a new pointer type. +""" +function pointertype!(builder::DIBuilder, pointee_type::DIType, size_in_bits::Integer; + align_in_bits::Integer=0, address_space::Integer=0, + name::AbstractString="") + DIDerivedType(API.LLVMDIBuilderCreatePointerType( + builder, pointee_type, + UInt64(size_in_bits), UInt32(align_in_bits), Cuint(address_space), + name, Csize_t(length(name)))) +end + +""" + referencetype!(builder::DIBuilder, tag::Integer, type::DIType) -> DIDerivedType + +Create a new reference type (C++ `T&` / `T&&`), with the given DWARF `tag` +(e.g. `DW_TAG_reference_type` or `DW_TAG_rvalue_reference_type`). +""" +function referencetype!(builder::DIBuilder, tag::Integer, type::DIType) + DIDerivedType(API.LLVMDIBuilderCreateReferenceType(builder, Cuint(tag), type)) +end + +""" + nullptrtype!(builder::DIBuilder) -> DIBasicType + +Create a new type representing a null pointer. +""" +nullptrtype!(builder::DIBuilder) = + DIBasicType(API.LLVMDIBuilderCreateNullPtrType(builder)) + +""" + typedeftype!(builder::DIBuilder, type::DIType, name::AbstractString, + file::DIFile, line::Integer, scope::DIScope; + align_in_bits::Integer=0) -> DIDerivedType + +Create a new typedef type. +""" +function typedeftype!(builder::DIBuilder, type::DIType, name::AbstractString, + file::DIFile, line::Integer, scope::DIScope; + align_in_bits::Integer=0) + DIDerivedType(API.LLVMDIBuilderCreateTypedef( + builder, type, name, Csize_t(length(name)), + file, Cuint(line), scope, UInt32(align_in_bits))) +end + +""" + qualifiedtype!(builder::DIBuilder, tag::Integer, type::DIType) -> DIDerivedType + +Create a new qualified type, such as `const T` (`DW_TAG_const_type`) or +`volatile T` (`DW_TAG_volatile_type`). +""" +function qualifiedtype!(builder::DIBuilder, tag::Integer, type::DIType) + DIDerivedType(API.LLVMDIBuilderCreateQualifiedType(builder, Cuint(tag), type)) +end + +""" + artificialtype!(builder::DIBuilder, type::DIType) -> DIDerivedType + +Create a new artificial type (`DI_FLAG_ARTIFICIAL`), e.g. an implicit `this`. +""" +artificialtype!(builder::DIBuilder, type::DIType) = + DIDerivedType(API.LLVMDIBuilderCreateArtificialType(builder, type)) + +""" + objectpointertype!(builder::DIBuilder, type::DIType) -> DIDerivedType + +Create a new type identifying an object pointer (`DI_FLAG_OBJECT_POINTER`). +""" +objectpointertype!(builder::DIBuilder, type::DIType) = + DIDerivedType(API.LLVMDIBuilderCreateObjectPointerType(builder, type)) + +""" + inheritance!(builder::DIBuilder, derived::DIType, base::DIType, + base_offset::Integer, vbptr_offset::Integer=0; + flags=API.LLVMDIFlagZero) -> DIDerivedType + +Create a new inheritance relationship from `derived` to `base`. +""" +function inheritance!(builder::DIBuilder, derived::DIType, base::DIType, + base_offset::Integer, vbptr_offset::Integer=0; + flags=API.LLVMDIFlagZero) + DIDerivedType(API.LLVMDIBuilderCreateInheritance( + builder, derived, base, UInt64(base_offset), UInt32(vbptr_offset), flags)) +end + +""" + membertype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + file::DIFile, line::Integer, size_in_bits::Integer, + align_in_bits::Integer, offset_in_bits::Integer, + type::DIType; flags=API.LLVMDIFlagZero) -> DIDerivedType + +Create a new member (field) of a composite type. +""" +function membertype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + file::DIFile, line::Integer, size_in_bits::Integer, + align_in_bits::Integer, offset_in_bits::Integer, + type::DIType; flags=API.LLVMDIFlagZero) + DIDerivedType(API.LLVMDIBuilderCreateMemberType( + builder, scope, name, Csize_t(length(name)), + file, Cuint(line), + UInt64(size_in_bits), UInt32(align_in_bits), UInt64(offset_in_bits), + flags, type)) +end + +""" + bitfieldmembertype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + file::DIFile, line::Integer, size_in_bits::Integer, + offset_in_bits::Integer, storage_offset_in_bits::Integer, + type::DIType; flags=API.LLVMDIFlagZero) -> DIDerivedType + +Create a new bit-field member of a composite type. +""" +function bitfieldmembertype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + file::DIFile, line::Integer, size_in_bits::Integer, + offset_in_bits::Integer, storage_offset_in_bits::Integer, + type::DIType; flags=API.LLVMDIFlagZero) + DIDerivedType(API.LLVMDIBuilderCreateBitFieldMemberType( + builder, scope, name, Csize_t(length(name)), + file, Cuint(line), + UInt64(size_in_bits), UInt64(offset_in_bits), UInt64(storage_offset_in_bits), + flags, type)) +end + +""" + staticmembertype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + file::DIFile, line::Integer, type::DIType; + flags=API.LLVMDIFlagZero, + constant_val=nothing, + align_in_bits::Integer=0) -> DIDerivedType + +Create a new static member of a composite type. +""" +function staticmembertype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + file::DIFile, line::Integer, type::DIType; + flags=API.LLVMDIFlagZero, + constant_val=nothing, + align_in_bits::Integer=0) + DIDerivedType(API.LLVMDIBuilderCreateStaticMemberType( + builder, scope, name, Csize_t(length(name)), + file, Cuint(line), type, flags, + something(constant_val, C_NULL), UInt32(align_in_bits))) +end + +""" + memberpointertype!(builder::DIBuilder, pointee_type::DIType, class_type::DIType, + size_in_bits::Integer; + align_in_bits::Integer=0, + flags=API.LLVMDIFlagZero) -> DIDerivedType + +Create a new pointer-to-member type for C++. +""" +function memberpointertype!(builder::DIBuilder, pointee_type::DIType, class_type::DIType, + size_in_bits::Integer; + align_in_bits::Integer=0, + flags=API.LLVMDIFlagZero) + DIDerivedType(API.LLVMDIBuilderCreateMemberPointerType( + builder, pointee_type, class_type, + UInt64(size_in_bits), UInt32(align_in_bits), flags)) +end + + +# composite types + +""" + structtype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + file::DIFile, line::Integer, size_in_bits::Integer, + align_in_bits::Integer, elements::Vector{<:Metadata}; + flags=API.LLVMDIFlagZero, derived_from=nothing, + runtime_lang::Integer=0, vtable_holder=nothing, + unique_id::AbstractString="") -> DICompositeType + +Create a new struct type. +""" +function structtype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + file::DIFile, line::Integer, size_in_bits::Integer, + align_in_bits::Integer, elements::Vector{<:Metadata}; + flags=API.LLVMDIFlagZero, derived_from=nothing, + runtime_lang::Integer=0, vtable_holder=nothing, + unique_id::AbstractString="") + elts = convert(Vector{Metadata}, elements) + DICompositeType(API.LLVMDIBuilderCreateStructType( + builder, scope, name, Csize_t(length(name)), + file, Cuint(line), + UInt64(size_in_bits), UInt32(align_in_bits), flags, + something(derived_from, C_NULL), + elts, Cuint(length(elts)), + Cuint(runtime_lang), + something(vtable_holder, C_NULL), + unique_id, Csize_t(length(unique_id)))) +end + +""" + uniontype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + file::DIFile, line::Integer, size_in_bits::Integer, + align_in_bits::Integer, elements::Vector{<:Metadata}; + flags=API.LLVMDIFlagZero, runtime_lang::Integer=0, + unique_id::AbstractString="") -> DICompositeType + +Create a new union type. +""" +function uniontype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + file::DIFile, line::Integer, size_in_bits::Integer, + align_in_bits::Integer, elements::Vector{<:Metadata}; + flags=API.LLVMDIFlagZero, runtime_lang::Integer=0, + unique_id::AbstractString="") + elts = convert(Vector{Metadata}, elements) + DICompositeType(API.LLVMDIBuilderCreateUnionType( + builder, scope, name, Csize_t(length(name)), + file, Cuint(line), + UInt64(size_in_bits), UInt32(align_in_bits), flags, + elts, Cuint(length(elts)), + Cuint(runtime_lang), + unique_id, Csize_t(length(unique_id)))) +end + +""" + classtype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + file::DIFile, line::Integer, size_in_bits::Integer, + align_in_bits::Integer, offset_in_bits::Integer, + elements::Vector{<:Metadata}; + flags=API.LLVMDIFlagZero, derived_from=nothing, + vtable_holder=nothing, template_params=nothing, + unique_id::AbstractString="") -> DICompositeType + +Create a new C++ class type. +""" +function classtype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + file::DIFile, line::Integer, size_in_bits::Integer, + align_in_bits::Integer, offset_in_bits::Integer, + elements::Vector{<:Metadata}; + flags=API.LLVMDIFlagZero, derived_from=nothing, + vtable_holder=nothing, template_params=nothing, + unique_id::AbstractString="") + elts = convert(Vector{Metadata}, elements) + DICompositeType(API.LLVMDIBuilderCreateClassType( + builder, scope, name, Csize_t(length(name)), + file, Cuint(line), + UInt64(size_in_bits), UInt32(align_in_bits), UInt64(offset_in_bits), + flags, + something(derived_from, C_NULL), + elts, Cuint(length(elts)), + something(vtable_holder, C_NULL), + something(template_params, C_NULL), + unique_id, Csize_t(length(unique_id)))) +end + +""" + arraytype!(builder::DIBuilder, size::Integer, align_in_bits::Integer, + element_type::DIType, subscripts::Vector{<:Metadata}) -> DICompositeType + +Create a new array type. Subscripts are typically built with +[`getorcreatesubrange!`](@ref). +""" +function arraytype!(builder::DIBuilder, size::Integer, align_in_bits::Integer, + element_type::DIType, subscripts::Vector{<:Metadata}) + subs = convert(Vector{Metadata}, subscripts) + DICompositeType(API.LLVMDIBuilderCreateArrayType( + builder, UInt64(size), UInt32(align_in_bits), + element_type, subs, Cuint(length(subs)))) +end + +""" + vectortype!(builder::DIBuilder, size::Integer, align_in_bits::Integer, + element_type::DIType, subscripts::Vector{<:Metadata}) -> DICompositeType + +Create a new vector type. Subscripts are typically built with +[`getorcreatesubrange!`](@ref). +""" +function vectortype!(builder::DIBuilder, size::Integer, align_in_bits::Integer, + element_type::DIType, subscripts::Vector{<:Metadata}) + subs = convert(Vector{Metadata}, subscripts) + DICompositeType(API.LLVMDIBuilderCreateVectorType( + builder, UInt64(size), UInt32(align_in_bits), + element_type, subs, Cuint(length(subs)))) +end + +""" + enumerator!(builder::DIBuilder, name::AbstractString, value::Integer; + unsigned::Bool=false) -> DIEnumerator + +Create a new enumerator for use inside an enumeration type. +""" +function enumerator!(builder::DIBuilder, name::AbstractString, value::Integer; + unsigned::Bool=false) + DIEnumerator(API.LLVMDIBuilderCreateEnumerator( + builder, name, Csize_t(length(name)), Int64(value), unsigned)) +end + +""" + enumerationtype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + file::DIFile, line::Integer, size_in_bits::Integer, + align_in_bits::Integer, elements::Vector{<:Metadata}; + class_ty=nothing) -> DICompositeType + +Create a new enumeration type. `elements` should be a vector of +[`DIEnumerator`](@ref) metadata nodes. +""" +function enumerationtype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + file::DIFile, line::Integer, size_in_bits::Integer, + align_in_bits::Integer, elements::Vector{<:Metadata}; + class_ty=nothing) + elts = convert(Vector{Metadata}, elements) + DICompositeType(API.LLVMDIBuilderCreateEnumerationType( + builder, scope, name, Csize_t(length(name)), + file, Cuint(line), + UInt64(size_in_bits), UInt32(align_in_bits), + elts, Cuint(length(elts)), + something(class_ty, C_NULL))) +end + +""" + forwarddecl!(builder::DIBuilder, tag::Integer, name::AbstractString, + scope::DIScope, file::DIFile, line::Integer; + runtime_lang::Integer=0, size_in_bits::Integer=0, + align_in_bits::Integer=0, + unique_id::AbstractString="") -> DICompositeType + +Create a new forward declaration to a composite type. +""" +function forwarddecl!(builder::DIBuilder, tag::Integer, name::AbstractString, + scope::DIScope, file::DIFile, line::Integer; + runtime_lang::Integer=0, size_in_bits::Integer=0, + align_in_bits::Integer=0, + unique_id::AbstractString="") + DICompositeType(API.LLVMDIBuilderCreateForwardDecl( + builder, Cuint(tag), name, Csize_t(length(name)), + scope, file, Cuint(line), Cuint(runtime_lang), + UInt64(size_in_bits), UInt32(align_in_bits), + unique_id, Csize_t(length(unique_id)))) +end + +""" + replaceablecompositetype!(builder::DIBuilder, tag::Integer, + name::AbstractString, scope::DIScope, + file::DIFile, line::Integer; + runtime_lang::Integer=0, size_in_bits::Integer=0, + align_in_bits::Integer=0, + flags=API.LLVMDIFlagZero, + unique_id::AbstractString="") -> DICompositeType + +Create a new replaceable composite type forward declaration. +""" +function replaceablecompositetype!(builder::DIBuilder, tag::Integer, + name::AbstractString, scope::DIScope, + file::DIFile, line::Integer; + runtime_lang::Integer=0, size_in_bits::Integer=0, + align_in_bits::Integer=0, + flags=API.LLVMDIFlagZero, + unique_id::AbstractString="") + DICompositeType(API.LLVMDIBuilderCreateReplaceableCompositeType( + builder, Cuint(tag), name, Csize_t(length(name)), + scope, file, Cuint(line), Cuint(runtime_lang), + UInt64(size_in_bits), UInt32(align_in_bits), flags, + unique_id, Csize_t(length(unique_id)))) +end + + +# subroutine types + +""" + subroutinetype!(builder::DIBuilder, file::DIFile, + parameter_types::Vector{<:Metadata}; + flags=API.LLVMDIFlagZero) -> DISubroutineType + +Create a new subroutine type. The first entry of `parameter_types` is the +return type; the rest are the parameter types. +""" +function subroutinetype!(builder::DIBuilder, file::DIFile, + parameter_types::Vector{<:Metadata}; + flags=API.LLVMDIFlagZero) + params = convert(Vector{Metadata}, parameter_types) + DISubroutineType(API.LLVMDIBuilderCreateSubroutineType( + builder, file, params, Cuint(length(params)), flags)) +end + + +# subrange helpers + +""" + getorcreatesubrange!(builder::DIBuilder, lower_bound::Integer, count::Integer) + +Get or create a subrange metadata node, describing one dimension of an array +or vector type. +""" +getorcreatesubrange!(builder::DIBuilder, lower_bound::Integer, count::Integer) = + DISubrange(API.LLVMDIBuilderGetOrCreateSubrange( + builder, Int64(lower_bound), Int64(count))) + + +# LLVM 21+ additions + +@static if version() >= v"21" + +@public settype!, subrangetype!, dynamicarraytype!, enumeratorarb! + +""" + settype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + file::DIFile, line::Integer, size_in_bits::Integer, + align_in_bits::Integer, base_type::DIType) -> DIDerivedType + +Create a new set type (`DW_TAG_set_type`). Requires LLVM 21+. +""" +function settype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + file::DIFile, line::Integer, size_in_bits::Integer, + align_in_bits::Integer, base_type::DIType) + DIDerivedType(API.LLVMDIBuilderCreateSetType( + builder, scope, name, Csize_t(length(name)), + file, Cuint(line), + UInt64(size_in_bits), UInt32(align_in_bits), base_type)) +end + +""" + subrangetype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + line::Integer, file::DIFile, size_in_bits::Integer, + align_in_bits::Integer, base_type::DIType; + flags=API.LLVMDIFlagZero, + lower_bound=nothing, upper_bound=nothing, + stride=nothing, bias=nothing) + +Create a new subrange type. Requires LLVM 21+. +""" +function subrangetype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + line::Integer, file::DIFile, size_in_bits::Integer, + align_in_bits::Integer, base_type::DIType; + flags=API.LLVMDIFlagZero, + lower_bound=nothing, upper_bound=nothing, + stride=nothing, bias=nothing) + Metadata(API.LLVMDIBuilderCreateSubrangeType( + builder, scope, name, Csize_t(length(name)), + Cuint(line), file, + UInt64(size_in_bits), UInt32(align_in_bits), flags, base_type, + something(lower_bound, C_NULL), + something(upper_bound, C_NULL), + something(stride, C_NULL), + something(bias, C_NULL))) +end + +""" + dynamicarraytype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + line::Integer, file::DIFile, size::Integer, + align_in_bits::Integer, element_type::DIType, + subscripts::Vector{<:Metadata}; + data_location=nothing, associated=nothing, + allocated=nothing, rank=nothing, + bit_stride=nothing) -> DICompositeType + +Create a new dynamic array type (Fortran assumed-shape/deferred-shape arrays). +Requires LLVM 21+. +""" +function dynamicarraytype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + line::Integer, file::DIFile, size::Integer, + align_in_bits::Integer, element_type::DIType, + subscripts::Vector{<:Metadata}; + data_location=nothing, associated=nothing, + allocated=nothing, rank=nothing, + bit_stride=nothing) + subs = convert(Vector{Metadata}, subscripts) + DICompositeType(API.LLVMDIBuilderCreateDynamicArrayType( + builder, scope, name, Csize_t(length(name)), + Cuint(line), file, + UInt64(size), UInt32(align_in_bits), element_type, + subs, Cuint(length(subs)), + something(data_location, C_NULL), + something(associated, C_NULL), + something(allocated, C_NULL), + something(rank, C_NULL), + something(bit_stride, C_NULL))) +end + +""" + enumeratorarb!(builder::DIBuilder, name::AbstractString, + size_in_bits::Integer, words::Vector{UInt64}; + unsigned::Bool=false) -> DIEnumerator + +Create a new arbitrary-precision enumerator. Requires LLVM 21+. +""" +function enumeratorarb!(builder::DIBuilder, name::AbstractString, + size_in_bits::Integer, words::Vector{UInt64}; + unsigned::Bool=false) + DIEnumerator(API.LLVMDIBuilderCreateEnumeratorOfArbitraryPrecision( + builder, name, Csize_t(length(name)), + UInt64(size_in_bits), words, unsigned)) +end + +end # @static if version() >= v"21" + ## subprogram diff --git a/test/debuginfo.jl b/test/debuginfo.jl index 01d058b8..5d0648ec 100644 --- a/test/debuginfo.jl +++ b/test/debuginfo.jl @@ -82,6 +82,97 @@ end end end +@testset "DIBuilder: type constructors" begin + DW_ATE_signed = 0x05 + DW_TAG_structure_type = 0x13 + DW_TAG_const_type = 0x26 + DW_TAG_reference_type = 0x10 + + @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin + DIBuilder(mod) do dib + file = LLVM.file!(dib, "test.jl", "/tmp") + cu = LLVM.compileunit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, + file, "LLVM.jl Tests") + + # basic types + i64 = LLVM.basictype!(dib, "Int64", 64, DW_ATE_signed) + @test i64 isa LLVM.DIBasicType + @test LLVM.name(i64) == "Int64" + @test LLVM.align(i64) == 0 + @test LLVM.tag(i64) != 0 + + @test LLVM.unspecifiedtype!(dib, "unspec") isa LLVM.DIBasicType + @test LLVM.nullptrtype!(dib) isa LLVM.DIBasicType + + # derived types + ptr = LLVM.pointertype!(dib, i64, 64; name="i64_ptr") + @test ptr isa LLVM.DIDerivedType + + td = LLVM.typedeftype!(dib, i64, "MyInt", file, 1, cu) + @test td isa LLVM.DIDerivedType + + cq = LLVM.qualifiedtype!(dib, DW_TAG_const_type, i64) + @test cq isa LLVM.DIDerivedType + + at2 = LLVM.artificialtype!(dib, i64) + @test at2 isa LLVM.DIDerivedType + + op = LLVM.objectpointertype!(dib, i64) + @test op isa LLVM.DIDerivedType + + ref = LLVM.referencetype!(dib, DW_TAG_reference_type, i64) + @test ref isa LLVM.DIDerivedType + + # composite types + mem = LLVM.membertype!(dib, cu, "x", file, 2, 64, 64, 0, i64) + @test mem isa LLVM.DIDerivedType + + st = LLVM.structtype!(dib, cu, "Point", file, 1, 64, 64, LLVM.Metadata[mem]) + @test st isa LLVM.DICompositeType + @test LLVM.name(st) == "Point" + + un = LLVM.uniontype!(dib, cu, "U", file, 1, 64, 64, LLVM.Metadata[mem]) + @test un isa LLVM.DICompositeType + + ct = LLVM.classtype!(dib, cu, "C", file, 1, 64, 64, 0, LLVM.Metadata[mem]) + @test ct isa LLVM.DICompositeType + + # arrays/vectors via subrange + sr = LLVM.getorcreatesubrange!(dib, 0, 10) + @test sr isa LLVM.DISubrange + aty = LLVM.arraytype!(dib, 640, 64, i64, [sr]) + @test aty isa LLVM.DICompositeType + + vty = LLVM.vectortype!(dib, 256, 64, i64, [sr]) + @test vty isa LLVM.DICompositeType + + # enumerations + e1 = LLVM.enumerator!(dib, "A", 0) + @test e1 isa LLVM.DIEnumerator + et = LLVM.enumerationtype!(dib, cu, "Color", file, 1, 32, 32, LLVM.Metadata[e1]) + @test et isa LLVM.DICompositeType + + # bitfield + static + member-pointer + inheritance + @test LLVM.bitfieldmembertype!(dib, cu, "b", file, 1, 3, 0, 0, i64) isa LLVM.DIDerivedType + @test LLVM.staticmembertype!(dib, cu, "s", file, 1, i64) isa LLVM.DIDerivedType + @test LLVM.memberpointertype!(dib, i64, st, 64) isa LLVM.DIDerivedType + + base = LLVM.classtype!(dib, cu, "Base", file, 1, 64, 64, 0, LLVM.Metadata[]) + @test LLVM.inheritance!(dib, ct, base, 0) isa LLVM.DIDerivedType + + # subroutine + sroute = LLVM.subroutinetype!(dib, file, LLVM.Metadata[i64, i64]) + @test sroute isa LLVM.DISubroutineType + + # forward decl / replaceable composite + @test LLVM.forwarddecl!(dib, DW_TAG_structure_type, "Fwd", cu, file, 1) isa LLVM.DICompositeType + @test LLVM.replaceablecompositetype!(dib, DW_TAG_structure_type, "Rep", cu, file, 1) isa LLVM.DICompositeType + + LLVM.finalize!(dib) + end + end +end + @dispose ctx=Context() begin mod = parse(LLVM.Module, """ define void @foo() !dbg !15 { From f9b1ec84ebb1d69560b154ba1e5380e3ea7f9205 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 13:32:56 +0200 Subject: [PATCH 05/27] Add subprogram!, variable, and expression factories. Adds DIBuilder methods for creating subprograms, local/parameter variables, DIExpression and DIGlobalVariableExpression metadata, and the global-variable forward declaration. Registers DIExpression and DIGlobalVariableExpression as new metadata types and exposes the variable/expression accessors on DIGlobalVariableExpression. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/src/lib/metadata.md | 23 +++++ src/debuginfo.jl | 186 +++++++++++++++++++++++++++++++++++++++ test/debuginfo.jl | 54 ++++++++++++ 3 files changed, 263 insertions(+) diff --git a/docs/src/lib/metadata.md b/docs/src/lib/metadata.md index b7cc2d08..14a69e4c 100644 --- a/docs/src/lib/metadata.md +++ b/docs/src/lib/metadata.md @@ -173,6 +173,29 @@ LLVM.subroutinetype! ```@docs DISubProgram line(::DISubProgram) +LLVM.subprogram!(::DIBuilder, ::DIScope, ::AbstractString, ::AbstractString, ::DIFile, ::Integer, ::LLVM.DISubroutineType, ::Integer) +``` + +### Variables + +Factories for local variables and parameters: + +```@docs +LLVM.autovariable! +LLVM.parametervariable! +``` + +### Expressions + +```@docs +LLVM.DIExpression +LLVM.DIGlobalVariableExpression +LLVM.expression! +LLVM.constantvalueexpression! +LLVM.variable +LLVM.expression +LLVM.globalvariableexpression! +LLVM.tempglobalvariablefwddecl! ``` ### Compile Unit diff --git a/src/debuginfo.jl b/src/debuginfo.jl index 618d4bf1..30104648 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -941,6 +941,30 @@ Get the line number of the given subprogram. """ line(subprogram::DISubProgram) = Int(API.LLVMDISubprogramGetLine(subprogram)) +""" + subprogram!(builder::DIBuilder, scope::DIScope, name::AbstractString, + linkage_name::AbstractString, file::DIFile, line::Integer, + type::DISubroutineType, scope_line::Integer; + is_local_to_unit::Bool=false, is_definition::Bool=true, + flags=API.LLVMDIFlagZero, + is_optimized::Bool=false) -> DISubProgram + +Create a new [`DISubProgram`](@ref) describing a function. +""" +function subprogram!(builder::DIBuilder, scope::DIScope, name::AbstractString, + linkage_name::AbstractString, file::DIFile, line::Integer, + type::DISubroutineType, scope_line::Integer; + is_local_to_unit::Bool=false, is_definition::Bool=true, + flags=API.LLVMDIFlagZero, + is_optimized::Bool=false) + DISubProgram(API.LLVMDIBuilderCreateFunction( + builder, scope, name, Csize_t(length(name)), + linkage_name, Csize_t(length(linkage_name)), + file, Cuint(line), type, + is_local_to_unit, is_definition, Cuint(scope_line), + flags, is_optimized)) +end + """ finalize_subprogram!(builder::DIBuilder, sp::DISubProgram) @@ -1044,6 +1068,168 @@ function dimodule!(builder::DIBuilder, parent_scope::DIScope, name::AbstractStri end +## variable factories + +@public autovariable!, parametervariable! + +""" + autovariable!(builder::DIBuilder, scope::DIScope, name::AbstractString, + file::DIFile, line::Integer, type::DIType; + always_preserve::Bool=false, flags=API.LLVMDIFlagZero, + align_in_bits::Integer=0) -> DILocalVariable + +Create a new local variable descriptor (for a compiler-introduced automatic +variable). +""" +function autovariable!(builder::DIBuilder, scope::DIScope, name::AbstractString, + file::DIFile, line::Integer, type::DIType; + always_preserve::Bool=false, flags=API.LLVMDIFlagZero, + align_in_bits::Integer=0) + DILocalVariable(API.LLVMDIBuilderCreateAutoVariable( + builder, scope, name, Csize_t(length(name)), + file, Cuint(line), type, + always_preserve, flags, UInt32(align_in_bits))) +end + +""" + parametervariable!(builder::DIBuilder, scope::DIScope, name::AbstractString, + arg_no::Integer, file::DIFile, line::Integer, type::DIType; + always_preserve::Bool=false, + flags=API.LLVMDIFlagZero) -> DILocalVariable + +Create a new descriptor for a function parameter variable. `arg_no` is +the 1-based parameter index. +""" +function parametervariable!(builder::DIBuilder, scope::DIScope, name::AbstractString, + arg_no::Integer, file::DIFile, line::Integer, type::DIType; + always_preserve::Bool=false, + flags=API.LLVMDIFlagZero) + DILocalVariable(API.LLVMDIBuilderCreateParameterVariable( + builder, scope, name, Csize_t(length(name)), Cuint(arg_no), + file, Cuint(line), type, + always_preserve, flags)) +end + + +## expression + +export DIExpression, DIGlobalVariableExpression, variable, expression +@public expression!, constantvalueexpression! + +""" + DIExpression + +A DWARF expression that modifies how a variable's value is expressed at runtime. +""" +@checked struct DIExpression <: MDNode + ref::API.LLVMMetadataRef +end +register(DIExpression, API.LLVMDIExpressionMetadataKind) + +""" + DIGlobalVariableExpression + +A pairing of a [`DIGlobalVariable`](@ref) and its associated [`DIExpression`](@ref). +""" +@checked struct DIGlobalVariableExpression <: MDNode + ref::API.LLVMMetadataRef +end +register(DIGlobalVariableExpression, API.LLVMDIGlobalVariableExpressionMetadataKind) + +""" + expression!(builder::DIBuilder, + addr::Vector{UInt64}=UInt64[]) -> DIExpression + +Create a new [`DIExpression`](@ref) from the given array of opcodes (encoding +a DWARF expression such as `DW_OP_plus_uconst`). +""" +function expression!(builder::DIBuilder, addr::Vector{UInt64}=UInt64[]) + DIExpression(API.LLVMDIBuilderCreateExpression( + builder, addr, Csize_t(length(addr)))) +end + +""" + constantvalueexpression!(builder::DIBuilder, value::Integer) -> DIExpression + +Create a new [`DIExpression`](@ref) representing a single constant value. +""" +function constantvalueexpression!(builder::DIBuilder, value::Integer) + DIExpression(API.LLVMDIBuilderCreateConstantValueExpression( + builder, UInt64(value))) +end + +""" + variable(gve::DIGlobalVariableExpression) + +Get the debug info global variable associated with the given expression. +""" +function variable(gve::DIGlobalVariableExpression) + ref = API.LLVMDIGlobalVariableExpressionGetVariable(gve) + ref == C_NULL ? nothing : Metadata(ref)::DIGlobalVariable +end + +""" + expression(gve::DIGlobalVariableExpression) + +Get the debug info expression associated with the given global-variable pair. +""" +function expression(gve::DIGlobalVariableExpression) + ref = API.LLVMDIGlobalVariableExpressionGetExpression(gve) + ref == C_NULL ? nothing : Metadata(ref)::DIExpression +end + + +## global variable + +@public globalvariableexpression!, tempglobalvariablefwddecl! + +""" + globalvariableexpression!(builder::DIBuilder, scope::DIScope, + name::AbstractString, linkage::AbstractString, + file::DIFile, line::Integer, type::DIType, + local_to_unit::Bool, expression::DIExpression; + declaration=nothing, + align_in_bits::Integer=0) -> DIGlobalVariableExpression + +Create a new global variable descriptor paired with a DWARF expression. +""" +function globalvariableexpression!(builder::DIBuilder, scope::DIScope, + name::AbstractString, linkage::AbstractString, + file::DIFile, line::Integer, type::DIType, + local_to_unit::Bool, expression::DIExpression; + declaration=nothing, + align_in_bits::Integer=0) + DIGlobalVariableExpression(API.LLVMDIBuilderCreateGlobalVariableExpression( + builder, scope, name, Csize_t(length(name)), + linkage, Csize_t(length(linkage)), + file, Cuint(line), type, local_to_unit, expression, + something(declaration, C_NULL), UInt32(align_in_bits))) +end + +""" + tempglobalvariablefwddecl!(builder::DIBuilder, scope::DIScope, + name::AbstractString, linkage::AbstractString, + file::DIFile, line::Integer, type::DIType, + local_to_unit::Bool; + declaration=nothing, + align_in_bits::Integer=0) -> DIGlobalVariable + +Create a new temporary forward declaration for a global variable. +""" +function tempglobalvariablefwddecl!(builder::DIBuilder, scope::DIScope, + name::AbstractString, linkage::AbstractString, + file::DIFile, line::Integer, type::DIType, + local_to_unit::Bool; + declaration=nothing, + align_in_bits::Integer=0) + DIGlobalVariable(API.LLVMDIBuilderCreateTempGlobalVariableFwdDecl( + builder, scope, name, Csize_t(length(name)), + linkage, Csize_t(length(linkage)), + file, Cuint(line), type, local_to_unit, + something(declaration, C_NULL), UInt32(align_in_bits))) +end + + ## lexical block export DILexicalBlock, DILexicalBlockFile diff --git a/test/debuginfo.jl b/test/debuginfo.jl index 5d0648ec..56b3062a 100644 --- a/test/debuginfo.jl +++ b/test/debuginfo.jl @@ -173,6 +173,60 @@ end end end +@testset "DIBuilder: subprograms, variables, expressions" begin + DW_ATE_signed = 0x05 + + @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin + DIBuilder(mod) do dib + file = LLVM.file!(dib, "test.jl", "/tmp") + cu = LLVM.compileunit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, + file, "LLVM.jl Tests") + + i64 = LLVM.basictype!(dib, "Int64", 64, DW_ATE_signed) + stype = LLVM.subroutinetype!(dib, file, LLVM.Metadata[i64, i64, i64]) + + # subprogram + sp = LLVM.subprogram!(dib, file, "add", "add", file, 1, stype, 1) + @test sp isa DISubProgram + @test LLVM.line(sp) == 1 + + # variables + v = LLVM.autovariable!(dib, sp, "x", file, 2, i64) + @test v isa LLVM.DILocalVariable + @test LLVM.line(v) == 2 + @test LLVM.file(v) == file + @test LLVM.scope(v) == sp + + p = LLVM.parametervariable!(dib, sp, "a", 1, file, 1, i64) + @test p isa LLVM.DILocalVariable + + # expressions + e = LLVM.expression!(dib) + @test e isa LLVM.DIExpression + + ce = LLVM.constantvalueexpression!(dib, 42) + @test ce isa LLVM.DIExpression + + # global variable expression + accessors + gve = LLVM.globalvariableexpression!(dib, cu, "g", "g", + file, 1, i64, false, e) + @test gve isa LLVM.DIGlobalVariableExpression + gv = LLVM.variable(gve) + @test gv isa LLVM.DIGlobalVariable + @test LLVM.line(gv) == 1 + @test LLVM.expression(gve) isa LLVM.DIExpression + + # temp global forward decl + tgv = LLVM.tempglobalvariablefwddecl!(dib, cu, "tg", "tg", + file, 2, i64, false) + @test tgv isa LLVM.DIGlobalVariable + + LLVM.finalize_subprogram!(dib, sp) + LLVM.finalize!(dib) + end + end +end + @dispose ctx=Context() begin mod = parse(LLVM.Module, """ define void @foo() !dbg !15 { From 4ba3c7eaa797c1074bcc76c59320eb172ec8234f Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 13:35:37 +0200 Subject: [PATCH 06/27] Add dbg.declare/dbg.value insertion and Instruction debuglocation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds declare_before!, declare_at_end!, value_before!, value_at_end! with LLVM-19-aware dispatch: intrinsic-based on LLVM ≤ 18 (returning Instruction) and record-based on LLVM ≥ 19 (returning a new DbgRecord wrapper). LLVM 20+ adds DILabel, label!, label_before!, label_at_end!. debuglocation / debuglocation! on Instruction wraps LLVMInstructionGet/SetDebugLoc and mirrors the existing IRBuilder helpers. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/src/lib/metadata.md | 15 +++ src/debuginfo.jl | 210 +++++++++++++++++++++++++++++++++++++++ test/debuginfo.jl | 67 +++++++++++++ 3 files changed, 292 insertions(+) diff --git a/docs/src/lib/metadata.md b/docs/src/lib/metadata.md index 14a69e4c..4d51a95c 100644 --- a/docs/src/lib/metadata.md +++ b/docs/src/lib/metadata.md @@ -198,6 +198,21 @@ LLVM.globalvariableexpression! LLVM.tempglobalvariablefwddecl! ``` +### Instruction-level insertion + +The `declare_*!` / `value_*!` methods return an `Instruction` on LLVM ≤ 18 +(legacy `llvm.dbg.*` intrinsics) and a `LLVM.DbgRecord` on LLVM ≥ 19 +(the new `#dbg_*` record format). + +```@docs +LLVM.declare_before! +LLVM.declare_at_end! +LLVM.value_before! +LLVM.value_at_end! +LLVM.debuglocation(::Instruction) +LLVM.debuglocation!(::Instruction, ::DILocation) +``` + ### Compile Unit ```@docs diff --git a/src/debuginfo.jl b/src/debuginfo.jl index 30104648..b8f281c2 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -1311,6 +1311,216 @@ function namespace!(builder::DIBuilder, parent_scope::DIScope, name::AbstractStr end +## instruction insertion + +@public declare_before!, declare_at_end!, value_before!, value_at_end! + +@static if version() >= v"19" + +export DbgRecord + +""" + DbgRecord + +A non-instruction debug record attached to a basic block, replacing the +legacy `llvm.dbg.*` intrinsics in LLVM ≥ 19. +""" +@checked struct DbgRecord + ref::API.LLVMDbgRecordRef +end + +Base.unsafe_convert(::Type{API.LLVMDbgRecordRef}, record::DbgRecord) = record.ref + +function Base.show(io::IO, record::DbgRecord) + str_ptr = API.LLVMPrintDbgRecordToString(record) + str = unsafe_string(str_ptr) + print(io, rstrip(str)) + # LLVMPrintDbgRecordToString-returned memory is freed by LLVMDisposeMessage + API.LLVMDisposeMessage(str_ptr) +end + +""" + declare_before!(builder::DIBuilder, storage::Value, var::DILocalVariable, + expr::DIExpression, debugloc::DILocation, + instr::Instruction) -> DbgRecord + +Insert a new `#dbg_declare` record describing `storage` as the runtime +location of `var`, immediately before `instr`. +""" +declare_before!(builder::DIBuilder, storage::Value, var::DIVariable, + expr::DIExpression, debugloc::DILocation, instr::Instruction) = + DbgRecord(API.LLVMDIBuilderInsertDeclareRecordBefore( + builder, storage, var, expr, debugloc, instr)) + +""" + declare_at_end!(builder::DIBuilder, storage::Value, var::DILocalVariable, + expr::DIExpression, debugloc::DILocation, + block::BasicBlock) -> DbgRecord + +Insert a new `#dbg_declare` record at the end of `block`. +""" +declare_at_end!(builder::DIBuilder, storage::Value, var::DIVariable, + expr::DIExpression, debugloc::DILocation, block::BasicBlock) = + DbgRecord(API.LLVMDIBuilderInsertDeclareRecordAtEnd( + builder, storage, var, expr, debugloc, block)) + +""" + value_before!(builder::DIBuilder, val::Value, var::DILocalVariable, + expr::DIExpression, debugloc::DILocation, + instr::Instruction) -> DbgRecord + +Insert a new `#dbg_value` record describing `val` as the value of `var`, +immediately before `instr`. +""" +value_before!(builder::DIBuilder, val::Value, var::DIVariable, + expr::DIExpression, debugloc::DILocation, instr::Instruction) = + DbgRecord(API.LLVMDIBuilderInsertDbgValueRecordBefore( + builder, val, var, expr, debugloc, instr)) + +""" + value_at_end!(builder::DIBuilder, val::Value, var::DILocalVariable, + expr::DIExpression, debugloc::DILocation, + block::BasicBlock) -> DbgRecord + +Insert a new `#dbg_value` record at the end of `block`. +""" +value_at_end!(builder::DIBuilder, val::Value, var::DIVariable, + expr::DIExpression, debugloc::DILocation, block::BasicBlock) = + DbgRecord(API.LLVMDIBuilderInsertDbgValueRecordAtEnd( + builder, val, var, expr, debugloc, block)) + +else # LLVM < 19: legacy intrinsic-based insertion + +""" + declare_before!(builder::DIBuilder, storage::Value, var::DILocalVariable, + expr::DIExpression, debugloc::DILocation, + instr::Instruction) -> Instruction + +Insert a new `llvm.dbg.declare` intrinsic call immediately before `instr`. +""" +declare_before!(builder::DIBuilder, storage::Value, var::DIVariable, + expr::DIExpression, debugloc::DILocation, instr::Instruction) = + Instruction(API.LLVMDIBuilderInsertDeclareBefore( + builder, storage, var, expr, debugloc, instr)) + +""" + declare_at_end!(builder::DIBuilder, storage::Value, var::DILocalVariable, + expr::DIExpression, debugloc::DILocation, + block::BasicBlock) -> Instruction + +Insert a new `llvm.dbg.declare` intrinsic call at the end of `block`. +""" +declare_at_end!(builder::DIBuilder, storage::Value, var::DIVariable, + expr::DIExpression, debugloc::DILocation, block::BasicBlock) = + Instruction(API.LLVMDIBuilderInsertDeclareAtEnd( + builder, storage, var, expr, debugloc, block)) + +""" + value_before!(builder::DIBuilder, val::Value, var::DILocalVariable, + expr::DIExpression, debugloc::DILocation, + instr::Instruction) -> Instruction + +Insert a new `llvm.dbg.value` intrinsic call immediately before `instr`. +""" +value_before!(builder::DIBuilder, val::Value, var::DIVariable, + expr::DIExpression, debugloc::DILocation, instr::Instruction) = + Instruction(API.LLVMDIBuilderInsertDbgValueBefore( + builder, val, var, expr, debugloc, instr)) + +""" + value_at_end!(builder::DIBuilder, val::Value, var::DILocalVariable, + expr::DIExpression, debugloc::DILocation, + block::BasicBlock) -> Instruction + +Insert a new `llvm.dbg.value` intrinsic call at the end of `block`. +""" +value_at_end!(builder::DIBuilder, val::Value, var::DIVariable, + expr::DIExpression, debugloc::DILocation, block::BasicBlock) = + Instruction(API.LLVMDIBuilderInsertDbgValueAtEnd( + builder, val, var, expr, debugloc, block)) + +end # @static version check + + +## label (LLVM 20+) + +@static if version() >= v"20" + +export DILabel +@public label!, label_before!, label_at_end! + +""" + DILabel + +A debug-info label, describing a source-level code location by name. +Requires LLVM 20+. +""" +@checked struct DILabel <: DINode + ref::API.LLVMMetadataRef +end +register(DILabel, API.LLVMDILabelMetadataKind) + +""" + label!(builder::DIBuilder, scope::DIScope, name::AbstractString, + file::DIFile, line::Integer; + always_preserve::Bool=false) -> DILabel + +Create a new [`DILabel`](@ref). Requires LLVM 20+. +""" +function label!(builder::DIBuilder, scope::DIScope, name::AbstractString, + file::DIFile, line::Integer; + always_preserve::Bool=false) + DILabel(API.LLVMDIBuilderCreateLabel( + builder, scope, name, Csize_t(length(name)), + file, Cuint(line), always_preserve)) +end + +""" + label_before!(builder::DIBuilder, label::DILabel, + location::DILocation, instr::Instruction) -> DbgRecord + +Insert a new label record immediately before `instr`. Requires LLVM 20+. +""" +label_before!(builder::DIBuilder, label::DILabel, + location::DILocation, instr::Instruction) = + DbgRecord(API.LLVMDIBuilderInsertLabelBefore(builder, label, location, instr)) + +""" + label_at_end!(builder::DIBuilder, label::DILabel, + location::DILocation, block::BasicBlock) -> DbgRecord + +Insert a new label record at the end of `block`. Requires LLVM 20+. +""" +label_at_end!(builder::DIBuilder, label::DILabel, + location::DILocation, block::BasicBlock) = + DbgRecord(API.LLVMDIBuilderInsertLabelAtEnd(builder, label, location, block)) + +end # @static version check + + +## instruction debug location + +# re-uses the existing `debuglocation` / `debuglocation!` exports on IRBuilder. + +""" + debuglocation(inst::Instruction) -> Union{DILocation,Nothing} + +Get the debug location attached to the given instruction, or `nothing`. +""" +function debuglocation(inst::Instruction) + ref = API.LLVMInstructionGetDebugLoc(inst) + ref == C_NULL ? nothing : Metadata(ref)::DILocation +end + +""" + debuglocation!(inst::Instruction, loc::DILocation) + +Set the debug location of the given instruction. +""" +debuglocation!(inst::Instruction, loc::DILocation) = + API.LLVMInstructionSetDebugLoc(inst, loc) + + ## other export DEBUG_METADATA_VERSION, strip_debuginfo!, subprogram, subprogram! diff --git a/test/debuginfo.jl b/test/debuginfo.jl index 56b3062a..9c9f13f3 100644 --- a/test/debuginfo.jl +++ b/test/debuginfo.jl @@ -227,6 +227,73 @@ end end end +@testset "DIBuilder: instruction-level insertion" begin + DW_ATE_signed = 0x05 + + @dispose ctx=Context() mod=LLVM.Module("SomeModule") builder=IRBuilder() begin + DIBuilder(mod) do dib + file = LLVM.file!(dib, "test.jl", "/tmp") + cu = LLVM.compileunit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, + file, "LLVM.jl Tests") + i64 = LLVM.basictype!(dib, "Int64", 64, DW_ATE_signed) + stype = LLVM.subroutinetype!(dib, file, LLVM.Metadata[i64, i64, i64]) + sp = LLVM.subprogram!(dib, file, "add", "add", file, 1, stype, 1) + + ft = LLVM.FunctionType(LLVM.Int64Type(), [LLVM.Int64Type(), LLVM.Int64Type()]) + fn = LLVM.Function(mod, "add", ft) + LLVM.subprogram!(fn, sp) + + bb = BasicBlock(fn, "entry") + position!(builder, bb) + x_alloca = alloca!(builder, LLVM.Int64Type(), "x.addr") + + var = LLVM.autovariable!(dib, sp, "x", file, 2, i64) + expr = LLVM.expression!(dib) + loc = DILocation(2, 1, sp) + + # declare_before! + declare_result = LLVM.declare_before!(dib, x_alloca, var, expr, loc, x_alloca) + if LLVM.version() >= v"19" + @test declare_result isa LLVM.DbgRecord + else + @test declare_result isa Instruction + end + + p1 = LLVM.parameters(fn)[1] + p2 = LLVM.parameters(fn)[2] + r = add!(builder, p1, p2) + retinst = ret!(builder, r) + + # instruction-level debug location read/write + @test LLVM.debuglocation(retinst) === nothing + LLVM.debuglocation!(retinst, loc) + got = LLVM.debuglocation(retinst) + @test got !== nothing + @test LLVM.line(got) == 2 + @test LLVM.column(got) == 1 + + # value_before! + val_result = LLVM.value_before!(dib, r, var, expr, loc, retinst) + if LLVM.version() >= v"19" + @test val_result isa LLVM.DbgRecord + else + @test val_result isa Instruction + end + + LLVM.finalize_subprogram!(dib, sp) + LLVM.finalize!(dib) + end + + # the IR should contain the dbg.declare (intrinsic) or #dbg_declare (record) + ir = string(mod) + if LLVM.version() >= v"19" + @test occursin("#dbg_declare", ir) || occursin("dbg.declare", ir) + else + @test occursin("llvm.dbg.declare", ir) + end + end +end + @dispose ctx=Context() begin mod = parse(LLVM.Module, """ define void @foo() !dbg !15 { From cc3780cba35a590a8d88c97ac0dd489bdd463169 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 13:37:07 +0200 Subject: [PATCH 07/27] Add imported-entity, macro, and label factories. Adds wrappers for the four DIImportedEntity flavors, DIMacro / DIMacroFile with macro!/tempmacrofile!, and registers the matching metadata types. Label support was added in step 6 together with the other LLVM-20-gated record helpers. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/src/lib/metadata.md | 19 ++++++ src/debuginfo.jl | 135 +++++++++++++++++++++++++++++++++++++++ test/debuginfo.jl | 41 ++++++++++++ 3 files changed, 195 insertions(+) diff --git a/docs/src/lib/metadata.md b/docs/src/lib/metadata.md index 4d51a95c..226c4080 100644 --- a/docs/src/lib/metadata.md +++ b/docs/src/lib/metadata.md @@ -198,6 +198,25 @@ LLVM.globalvariableexpression! LLVM.tempglobalvariablefwddecl! ``` +### Imported entities + +```@docs +LLVM.DIImportedEntity +LLVM.importedmodulefromnamespace! +LLVM.importedmodulefromalias! +LLVM.importedmodulefrommodule! +LLVM.importeddeclaration! +``` + +### Macros + +```@docs +LLVM.DIMacro +LLVM.DIMacroFile +LLVM.macro! +LLVM.tempmacrofile! +``` + ### Instruction-level insertion The `declare_*!` / `value_*!` methods return an `Instruction` on LLVM ≤ 18 diff --git a/src/debuginfo.jl b/src/debuginfo.jl index b8f281c2..40b28174 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -1498,6 +1498,141 @@ label_at_end!(builder::DIBuilder, label::DILabel, end # @static version check +## imported entity + +export DIImportedEntity +@public importedmodulefromnamespace!, importedmodulefromalias!, + importedmodulefrommodule!, importeddeclaration! + +""" + DIImportedEntity + +An imported entity, such as a C++ `using` declaration or module import. +""" +@checked struct DIImportedEntity <: DINode + ref::API.LLVMMetadataRef +end +register(DIImportedEntity, API.LLVMDIImportedEntityMetadataKind) + +""" + importedmodulefromnamespace!(builder::DIBuilder, scope::DIScope, + ns::DINamespace, file::DIFile, + line::Integer) -> DIImportedEntity + +Create a new `DIImportedEntity` from a namespace. +""" +importedmodulefromnamespace!(builder::DIBuilder, scope::DIScope, ns::DINamespace, + file::DIFile, line::Integer) = + DIImportedEntity(API.LLVMDIBuilderCreateImportedModuleFromNamespace( + builder, scope, ns, file, Cuint(line))) + +""" + importedmodulefromalias!(builder::DIBuilder, scope::DIScope, + imported::DIImportedEntity, file::DIFile, + line::Integer, + elements::Vector{<:Metadata}=Metadata[]) -> DIImportedEntity + +Create a new `DIImportedEntity` from an alias. +""" +function importedmodulefromalias!(builder::DIBuilder, scope::DIScope, + imported::DIImportedEntity, file::DIFile, + line::Integer, + elements::Vector{<:Metadata}=Metadata[]) + elts = convert(Vector{Metadata}, elements) + DIImportedEntity(API.LLVMDIBuilderCreateImportedModuleFromAlias( + builder, scope, imported, file, Cuint(line), + elts, Cuint(length(elts)))) +end + +""" + importedmodulefrommodule!(builder::DIBuilder, scope::DIScope, + mod::DIModule, file::DIFile, line::Integer, + elements::Vector{<:Metadata}=Metadata[]) -> DIImportedEntity + +Create a new `DIImportedEntity` from a module. +""" +function importedmodulefrommodule!(builder::DIBuilder, scope::DIScope, + mod::DIModule, file::DIFile, line::Integer, + elements::Vector{<:Metadata}=Metadata[]) + elts = convert(Vector{Metadata}, elements) + DIImportedEntity(API.LLVMDIBuilderCreateImportedModuleFromModule( + builder, scope, mod, file, Cuint(line), + elts, Cuint(length(elts)))) +end + +""" + importeddeclaration!(builder::DIBuilder, scope::DIScope, decl::Metadata, + file::DIFile, line::Integer, name::AbstractString, + elements::Vector{<:Metadata}=Metadata[]) -> DIImportedEntity + +Create a new `DIImportedEntity` from a declaration. +""" +function importeddeclaration!(builder::DIBuilder, scope::DIScope, decl::Metadata, + file::DIFile, line::Integer, name::AbstractString, + elements::Vector{<:Metadata}=Metadata[]) + elts = convert(Vector{Metadata}, elements) + DIImportedEntity(API.LLVMDIBuilderCreateImportedDeclaration( + builder, scope, decl, file, Cuint(line), + name, Csize_t(length(name)), + elts, Cuint(length(elts)))) +end + + +## macro + +export DIMacro, DIMacroFile +@public macro!, tempmacrofile! + +""" + DIMacro + +A single preprocessor macro definition or undefinition. +""" +@checked struct DIMacro <: DINode + ref::API.LLVMMetadataRef +end +register(DIMacro, API.LLVMDIMacroMetadataKind) + +""" + DIMacroFile + +A collection of macro records corresponding to a single source file. +""" +@checked struct DIMacroFile <: DINode + ref::API.LLVMMetadataRef +end +register(DIMacroFile, API.LLVMDIMacroFileMetadataKind) + +""" + macro!(builder::DIBuilder, parent_macrofile::Union{DIMacroFile,Nothing}, + line::Integer, record_type, name::AbstractString, + value::AbstractString) -> DIMacro + +Create a new [`DIMacro`](@ref). `record_type` is a +`LLVMDWARFMacinfoRecordType` value (e.g. `LLVM.API.LLVMDWARFMacinfoRecordTypeDefine`). +""" +function macro!(builder::DIBuilder, parent_macrofile::Union{DIMacroFile,Nothing}, + line::Integer, record_type, name::AbstractString, + value::AbstractString) + DIMacro(API.LLVMDIBuilderCreateMacro( + builder, something(parent_macrofile, C_NULL), Cuint(line), record_type, + name, Csize_t(length(name)), + value, Csize_t(length(value)))) +end + +""" + tempmacrofile!(builder::DIBuilder, + parent_macrofile::Union{DIMacroFile,Nothing}, + line::Integer, file::DIFile) -> DIMacroFile + +Create a new (temporary) [`DIMacroFile`](@ref). +""" +tempmacrofile!(builder::DIBuilder, parent_macrofile::Union{DIMacroFile,Nothing}, + line::Integer, file::DIFile) = + DIMacroFile(API.LLVMDIBuilderCreateTempMacroFile( + builder, something(parent_macrofile, C_NULL), Cuint(line), file)) + + ## instruction debug location # re-uses the existing `debuglocation` / `debuglocation!` exports on IRBuilder. diff --git a/test/debuginfo.jl b/test/debuginfo.jl index 9c9f13f3..f9b79588 100644 --- a/test/debuginfo.jl +++ b/test/debuginfo.jl @@ -294,6 +294,47 @@ end end end +@testset "DIBuilder: imported entities and macros" begin + @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin + DIBuilder(mod) do dib + file = LLVM.file!(dib, "test.jl", "/tmp") + cu = LLVM.compileunit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, + file, "LLVM.jl Tests") + + ns = LLVM.namespace!(dib, cu, "MyNS") + ie = LLVM.importedmodulefromnamespace!(dib, cu, ns, file, 1) + @test ie isa LLVM.DIImportedEntity + + ie2 = LLVM.importedmodulefromalias!(dib, cu, ie, file, 2) + @test ie2 isa LLVM.DIImportedEntity + + dm = LLVM.dimodule!(dib, cu, "MyMod") + ie3 = LLVM.importedmodulefrommodule!(dib, cu, dm, file, 3) + @test ie3 isa LLVM.DIImportedEntity + + # imported declaration (decl = another scope) + ied = LLVM.importeddeclaration!(dib, cu, ns, file, 4, "alias") + @test ied isa LLVM.DIImportedEntity + + # macros + mf = LLVM.tempmacrofile!(dib, nothing, 1, file) + @test mf isa LLVM.DIMacroFile + + m = LLVM.macro!(dib, mf, 1, + LLVM.API.LLVMDWARFMacinfoRecordTypeDefine, + "FOO", "bar") + @test m isa LLVM.DIMacro + + if LLVM.version() >= v"20" + lbl = LLVM.label!(dib, cu, "my_label", file, 5) + @test lbl isa LLVM.DILabel + end + + LLVM.finalize!(dib) + end + end +end + @dispose ctx=Context() begin mod = parse(LLVM.Module, """ define void @foo() !dbg !15 { From be51712c50b68318c1644179130bc47adb7880f6 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 13:39:54 +0200 Subject: [PATCH 08/27] Add temporary MDNode, RAUW, and replace helpers. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds temporary_mdnode, dispose_temporary, and replace_all_uses_with! for cycle-breaking in recursive debug-info types. replacearrays! (rebinding a composite type's elements) and replacetype! (rewriting a DISubProgram's type) are gated behind LLVM ≥ 21, where they were first exposed through the C API. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/src/lib/metadata.md | 8 +++++ src/debuginfo.jl | 63 ++++++++++++++++++++++++++++++++++++++++ test/debuginfo.jl | 48 ++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+) diff --git a/docs/src/lib/metadata.md b/docs/src/lib/metadata.md index 226c4080..9bc14246 100644 --- a/docs/src/lib/metadata.md +++ b/docs/src/lib/metadata.md @@ -239,6 +239,14 @@ DICompileUnit LLVM.compileunit! ``` +### Mutation helpers + +```@docs +LLVM.temporary_mdnode +LLVM.dispose_temporary +LLVM.replace_all_uses_with! +``` + ### Other ```@docs diff --git a/src/debuginfo.jl b/src/debuginfo.jl index 40b28174..6cfc46ed 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -1656,6 +1656,69 @@ debuglocation!(inst::Instruction, loc::DILocation) = API.LLVMInstructionSetDebugLoc(inst, loc) +## mutation / advanced helpers + +@public temporary_mdnode, dispose_temporary, replace_all_uses_with! + +""" + temporary_mdnode(operands::Vector{<:Metadata}=Metadata[]) -> MDNode + +Create a temporary metadata node in the task-local [`context`](@ref) with the +given operands. Temporary nodes are useful for constructing cycles and must be +either replaced via [`replace_all_uses_with!`](@ref) or disposed of via +[`dispose_temporary`](@ref). +""" +function temporary_mdnode(operands::Vector{<:Metadata}=Metadata[]) + ops = convert(Vector{Metadata}, operands) + ref = API.LLVMTemporaryMDNode(context(), ops, Csize_t(length(ops))) + Metadata(ref) +end + +""" + dispose_temporary(md::Metadata) + +Dispose of a temporary metadata node returned by [`temporary_mdnode`](@ref). +""" +dispose_temporary(md::Metadata) = API.LLVMDisposeTemporaryMDNode(md) + +""" + replace_all_uses_with!(temp::Metadata, replacement::Metadata) + +Replace all uses of `temp` with `replacement`, and dispose of `temp`. +""" +replace_all_uses_with!(temp::Metadata, replacement::Metadata) = + API.LLVMMetadataReplaceAllUsesWith(temp, replacement) + + +@static if version() >= v"21" + +@public replacearrays!, replacetype! + +""" + replacearrays!(builder::DIBuilder, T::DICompositeType, + elements::Vector{<:Metadata}) + +Replace the elements array of the given composite type `T`. Requires LLVM 21+. +""" +function replacearrays!(builder::DIBuilder, T::DICompositeType, + elements::Vector{<:Metadata}) + elts = convert(Vector{Metadata}, elements) + tref = Ref(T.ref) + API.LLVMReplaceArrays(builder, tref, elts, Cuint(length(elts))) + return Metadata(tref[])::DICompositeType +end + +""" + replacetype!(sp::DISubProgram, ty::DISubroutineType) + +Replace the type of the given subprogram. Requires LLVM 21+. +""" +replacetype!(sp::DISubProgram, ty::DISubroutineType) = + API.LLVMDISubprogramReplaceType(sp, ty) + +end # @static version check + + ## other export DEBUG_METADATA_VERSION, strip_debuginfo!, subprogram, subprogram! diff --git a/test/debuginfo.jl b/test/debuginfo.jl index f9b79588..eeb20184 100644 --- a/test/debuginfo.jl +++ b/test/debuginfo.jl @@ -335,6 +335,54 @@ end end end +@testset "DIBuilder: mutation helpers" begin + @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin + DIBuilder(mod) do dib + # temporary_mdnode + dispose_temporary on an unused temp + temp = LLVM.temporary_mdnode() + @test temp isa LLVM.Metadata + LLVM.dispose_temporary(temp) + + # replace_all_uses_with! on a used temp + DW_ATE_signed = 0x05 + i64 = LLVM.basictype!(dib, "Int64", 64, DW_ATE_signed) + + temp2 = LLVM.temporary_mdnode(LLVM.Metadata[i64]) + real_node = MDNode([i64]) + LLVM.replace_all_uses_with!(temp2, real_node) + # temp2 is disposed as a side effect of RAUW + + LLVM.finalize!(dib) + end + end + + # cycle-breaking: forward decl + RAUW (works on all LLVM versions) + @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin + DIBuilder(mod) do dib + file = LLVM.file!(dib, "test.jl", "/tmp") + cu = LLVM.compileunit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, + file, "LLVM.jl Tests") + + DW_TAG_structure_type = 0x13 + DW_ATE_signed = 0x05 + + fwd = LLVM.replaceablecompositetype!(dib, DW_TAG_structure_type, "Node", + cu, file, 1; size_in_bits=64) + i64 = LLVM.basictype!(dib, "Int64", 64, DW_ATE_signed) + ptr_to_fwd = LLVM.pointertype!(dib, fwd, 64) + + mem_val = LLVM.membertype!(dib, cu, "value", file, 1, 64, 64, 0, i64) + mem_next = LLVM.membertype!(dib, cu, "next", file, 2, 64, 64, 64, ptr_to_fwd) + + real_struct = LLVM.structtype!(dib, cu, "Node", file, 1, 128, 64, + LLVM.Metadata[mem_val, mem_next]) + LLVM.replace_all_uses_with!(fwd, real_struct) + + LLVM.finalize!(dib) + end + end +end + @dispose ctx=Context() begin mod = parse(LLVM.Module, """ define void @foo() !dbg !15 { From 590813c884b6e680423fea5a31933ada2b8c3d3b Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 13:45:39 +0200 Subject: [PATCH 09/27] Close remaining LLVM-C DebugInfo.h gaps. Wraps the five functions that were missed in the earlier passes: getorcreatearray!, getorcreatetypearray!, objcivar!, objcproperty!, and debug_metadata_version(::Module) for the per-module DWARF version. Brings our coverage of llvm-c/DebugInfo.h to 100%. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/src/lib/metadata.md | 15 ++++++++ src/debuginfo.jl | 77 +++++++++++++++++++++++++++++++++++++++- test/debuginfo.jl | 30 ++++++++++++++++ 3 files changed, 121 insertions(+), 1 deletion(-) diff --git a/docs/src/lib/metadata.md b/docs/src/lib/metadata.md index 9bc14246..8ff10193 100644 --- a/docs/src/lib/metadata.md +++ b/docs/src/lib/metadata.md @@ -168,6 +168,20 @@ Subroutine types: LLVM.subroutinetype! ``` +Array-node helpers: + +```@docs +LLVM.getorcreatearray! +LLVM.getorcreatetypearray! +``` + +Objective-C: + +```@docs +LLVM.objcivar! +LLVM.objcproperty! +``` + ### Subprogram ```@docs @@ -251,6 +265,7 @@ LLVM.replace_all_uses_with! ```@docs DEBUG_METADATA_VERSION +LLVM.debug_metadata_version strip_debuginfo! subprogram(::LLVM.Function) subprogram! diff --git a/src/debuginfo.jl b/src/debuginfo.jl index 6cfc46ed..b0746934 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -808,7 +808,9 @@ function subroutinetype!(builder::DIBuilder, file::DIFile, end -# subrange helpers +# subrange / array helpers + +@public getorcreatearray!, getorcreatetypearray! """ getorcreatesubrange!(builder::DIBuilder, lower_bound::Integer, count::Integer) @@ -820,6 +822,70 @@ getorcreatesubrange!(builder::DIBuilder, lower_bound::Integer, count::Integer) = DISubrange(API.LLVMDIBuilderGetOrCreateSubrange( builder, Int64(lower_bound), Int64(count))) +""" + getorcreatearray!(builder::DIBuilder, elements::Vector{<:Metadata}) + +Get or create a generic metadata array node, used for lists such as +`elements` fields of composite types. +""" +function getorcreatearray!(builder::DIBuilder, elements::Vector{<:Metadata}) + elts = convert(Vector{Metadata}, elements) + Metadata(API.LLVMDIBuilderGetOrCreateArray(builder, elts, Csize_t(length(elts)))) +end + +""" + getorcreatetypearray!(builder::DIBuilder, types::Vector{<:Metadata}) + +Get or create a metadata node for a type array, used for e.g. template +parameter lists. +""" +function getorcreatetypearray!(builder::DIBuilder, types::Vector{<:Metadata}) + tys = convert(Vector{Metadata}, types) + Metadata(API.LLVMDIBuilderGetOrCreateTypeArray(builder, tys, Csize_t(length(tys)))) +end + + +# ObjC + +@public objcivar!, objcproperty! + +""" + objcivar!(builder::DIBuilder, name::AbstractString, file::DIFile, + line::Integer, size_in_bits::Integer, align_in_bits::Integer, + offset_in_bits::Integer, type::DIType, property_node::Metadata; + flags=API.LLVMDIFlagZero) -> DIDerivedType + +Create a new Objective-C instance variable. +""" +function objcivar!(builder::DIBuilder, name::AbstractString, file::DIFile, + line::Integer, size_in_bits::Integer, align_in_bits::Integer, + offset_in_bits::Integer, type::DIType, property_node::Metadata; + flags=API.LLVMDIFlagZero) + DIDerivedType(API.LLVMDIBuilderCreateObjCIVar( + builder, name, Csize_t(length(name)), + file, Cuint(line), + UInt64(size_in_bits), UInt32(align_in_bits), UInt64(offset_in_bits), + flags, type, property_node)) +end + +""" + objcproperty!(builder::DIBuilder, name::AbstractString, file::DIFile, + line::Integer, getter::AbstractString, setter::AbstractString, + attributes::Integer, type::DIType) -> DIDerivedType + +Create a new Objective-C `@property` descriptor. +""" +function objcproperty!(builder::DIBuilder, name::AbstractString, file::DIFile, + line::Integer, getter::AbstractString, setter::AbstractString, + attributes::Integer, type::DIType) + DIDerivedType(API.LLVMDIBuilderCreateObjCProperty( + builder, name, Csize_t(length(name)), + file, Cuint(line), + getter, Csize_t(length(getter)), + setter, Csize_t(length(setter)), + Cuint(attributes), type)) +end + # LLVM 21+ additions @@ -1730,6 +1796,15 @@ The current debug info version number, as supported by LLVM. """ DEBUG_METADATA_VERSION() = API.LLVMDebugMetadataVersion() +""" + debug_metadata_version(mod::Module) + +Get the debug info version number emitted in the given module, or `0` if none +is attached. +""" +debug_metadata_version(mod::Module) = Int(API.LLVMGetModuleDebugMetadataVersion(mod)) +@public debug_metadata_version + """ strip_debuginfo!(mod::Module) diff --git a/test/debuginfo.jl b/test/debuginfo.jl index eeb20184..e268a752 100644 --- a/test/debuginfo.jl +++ b/test/debuginfo.jl @@ -383,6 +383,36 @@ end end end +@testset "DIBuilder: array / type-array / ObjC / module version" begin + DW_ATE_signed = 0x05 + + @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin + DIBuilder(mod) do dib + file = LLVM.file!(dib, "test.jl", "/tmp") + cu = LLVM.compileunit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, + file, "LLVM.jl Tests") + i64 = LLVM.basictype!(dib, "Int64", 64, DW_ATE_signed) + + ta = LLVM.getorcreatetypearray!(dib, LLVM.Metadata[i64, i64]) + @test ta isa LLVM.Metadata + arr = LLVM.getorcreatearray!(dib, LLVM.Metadata[i64]) + @test arr isa LLVM.Metadata + + # ObjC (not widely used from Julia but round-tripped) + prop = LLVM.objcproperty!(dib, "count", file, 1, + "getCount", "setCount:", 0, i64) + @test prop isa LLVM.DIDerivedType + ivar = LLVM.objcivar!(dib, "_count", file, 1, 64, 64, 0, i64, prop) + @test ivar isa LLVM.DIDerivedType + + LLVM.finalize!(dib) + end + + # module-level debug version accessor (returns 0 when no flag set) + @test LLVM.debug_metadata_version(mod) isa Int + end +end + @dispose ctx=Context() begin mod = parse(LLVM.Module, """ define void @foo() !dbg !15 { From 0dbb35eb36dc48f2094a85cfe6ece8ccd97de6d2 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 14:22:08 +0200 Subject: [PATCH 10/27] Auto-finalize DIBuilder on dispose. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously users had to call LLVM.finalize! explicitly before dispose, per the C API's stated contract. That's a footgun: forgetting it leaks temporary metadata. dispose now calls LLVMDIBuilderFinalize first (idempotent in LLVM), making the do-block pattern just-work. finalize! stays callable for the rare case where the module has to be consumed before dispose runs. finalize_subprogram! is no longer marked @public — it's an advanced streaming helper, not a typical user API. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/debuginfo.jl | 30 ++++++++++++++++++------------ test/debuginfo.jl | 26 ++++++++------------------ 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/src/debuginfo.jl b/src/debuginfo.jl index b0746934..6f8d3f9a 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -7,8 +7,10 @@ export DIBuilder A builder for constructing debug information metadata. -This object needs to be disposed of using [`dispose`](@ref), after first having -called [`finalize!`](@ref). +This object needs to be disposed of using [`dispose`](@ref), which also resolves +any pending temporary metadata. Call [`finalize!`](@ref) explicitly only if you +need to use the finalized debug info (e.g. emit code) *before* disposing of the +builder. """ @checked struct DIBuilder ref::API.LLVMDIBuilderRef @@ -23,9 +25,8 @@ Base.unsafe_convert(::Type{API.LLVMDIBuilderRef}, builder::DIBuilder) = Create a new debug info builder that emits metadata into `mod`. When `allow_unresolved` is `true` (the default), the builder collects unresolved -metadata nodes attached to the module so that cycles can be resolved during a -call to [`finalize!`](@ref). When `false`, the builder errors on unresolved -nodes instead. +metadata nodes attached to the module so that cycles can be resolved during +[`dispose`](@ref). When `false`, the builder errors on unresolved nodes instead. """ function DIBuilder(mod::Module; allow_unresolved::Bool=true) ref = allow_unresolved ? API.LLVMCreateDIBuilder(mod) : @@ -36,9 +37,13 @@ end """ dispose(builder::DIBuilder) -Dispose of a debug info builder. [`finalize!`](@ref) must be called first. +Finalize the debug info (resolving any pending temporary metadata) and dispose +of the builder. """ -dispose(builder::DIBuilder) = mark_dispose(API.LLVMDisposeDIBuilder, builder) +function dispose(builder::DIBuilder) + API.LLVMDIBuilderFinalize(builder) + mark_dispose(API.LLVMDisposeDIBuilder, builder) +end function DIBuilder(f::Core.Function, args...; kwargs...) builder = DIBuilder(args...; kwargs...) @@ -55,8 +60,9 @@ Base.show(io::IO, builder::DIBuilder) = @printf(io, "DIBuilder(%p)", builder.ref finalize!(builder::DIBuilder) Resolve any unresolved metadata nodes and mark all compile units finalized. -Must be called before [`dispose`](@ref) and before using the emitted debug -information. +Called automatically by [`dispose`](@ref); call explicitly only if the +DI-enriched module must be consumed (e.g. for code emission) before the +builder is disposed of. """ finalize!(builder::DIBuilder) = API.LLVMDIBuilderFinalize(builder) @@ -988,7 +994,6 @@ end # @static if version() >= v"21" ## subprogram export DISubProgram, line -@public finalize_subprogram! """ DISubProgram @@ -1034,8 +1039,9 @@ end """ finalize_subprogram!(builder::DIBuilder, sp::DISubProgram) -Finalize the given subprogram, allowing later changes to be disallowed. Must be -called before [`finalize!`](@ref). +Finalize a single subprogram before the rest of the builder. Advanced helper +for streaming-style DI construction; most callers should simply rely on +[`dispose`](@ref) to finalize everything. """ finalize_subprogram!(builder::DIBuilder, sp::DISubProgram) = API.LLVMDIBuilderFinalizeSubprogram(builder, sp) diff --git a/test/debuginfo.jl b/test/debuginfo.jl index e268a752..63f2b4c3 100644 --- a/test/debuginfo.jl +++ b/test/debuginfo.jl @@ -3,19 +3,25 @@ DEBUG_METADATA_VERSION() @testset "DIBuilder lifecycle" begin + # dispose auto-finalizes @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin dib = DIBuilder(mod) - LLVM.finalize!(dib) dispose(dib) end @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin dib = DIBuilder(mod; allow_unresolved=false) - LLVM.finalize!(dib) dispose(dib) end # do-block form + @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin + DIBuilder(mod) do dib + # nothing — dispose will finalize on exit + end + end + + # explicit finalize! before dispose is still valid (idempotent) @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin DIBuilder(mod) do dib LLVM.finalize!(dib) @@ -42,8 +48,6 @@ end dm = LLVM.dimodule!(dib, cu, "MyModule") @test dm isa DIModule @test LLVM.name(dm) == "MyModule" - - LLVM.finalize!(dib) end # emitted DWARF should round-trip as text IR (compile unit is retained) @@ -76,8 +80,6 @@ end outer = DILocation(5, 1, cu) inner = DILocation(10, 20, lb, outer) @test LLVM.inlined_at(inner) == outer - - LLVM.finalize!(dib) end end end @@ -167,8 +169,6 @@ end # forward decl / replaceable composite @test LLVM.forwarddecl!(dib, DW_TAG_structure_type, "Fwd", cu, file, 1) isa LLVM.DICompositeType @test LLVM.replaceablecompositetype!(dib, DW_TAG_structure_type, "Rep", cu, file, 1) isa LLVM.DICompositeType - - LLVM.finalize!(dib) end end end @@ -222,7 +222,6 @@ end @test tgv isa LLVM.DIGlobalVariable LLVM.finalize_subprogram!(dib, sp) - LLVM.finalize!(dib) end end end @@ -281,7 +280,6 @@ end end LLVM.finalize_subprogram!(dib, sp) - LLVM.finalize!(dib) end # the IR should contain the dbg.declare (intrinsic) or #dbg_declare (record) @@ -329,8 +327,6 @@ end lbl = LLVM.label!(dib, cu, "my_label", file, 5) @test lbl isa LLVM.DILabel end - - LLVM.finalize!(dib) end end end @@ -351,8 +347,6 @@ end real_node = MDNode([i64]) LLVM.replace_all_uses_with!(temp2, real_node) # temp2 is disposed as a side effect of RAUW - - LLVM.finalize!(dib) end end @@ -377,8 +371,6 @@ end real_struct = LLVM.structtype!(dib, cu, "Node", file, 1, 128, 64, LLVM.Metadata[mem_val, mem_next]) LLVM.replace_all_uses_with!(fwd, real_struct) - - LLVM.finalize!(dib) end end end @@ -404,8 +396,6 @@ end @test prop isa LLVM.DIDerivedType ivar = LLVM.objcivar!(dib, "_count", file, 1, 64, 64, 0, i64, prop) @test ivar isa LLVM.DIDerivedType - - LLVM.finalize!(dib) end # module-level debug version accessor (returns 0 when no flag set) From c3ea4d0c48236c17b87d72562ccd013f565af55e Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 14:26:40 +0200 Subject: [PATCH 11/27] Clarify that finalize_subprogram! is strictly an early-out. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DIBuilder::finalize() already iterates AllSubprograms and calls finalizeSubprogram on each (DIBuilder.cpp:102). Unlike finalize!, the subprogram-level call is not needed for correctness — only as an optimization/streaming hint. Updated docstring to say so. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/debuginfo.jl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/debuginfo.jl b/src/debuginfo.jl index 6f8d3f9a..1c2be0bd 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -1039,9 +1039,13 @@ end """ finalize_subprogram!(builder::DIBuilder, sp::DISubProgram) -Finalize a single subprogram before the rest of the builder. Advanced helper -for streaming-style DI construction; most callers should simply rely on -[`dispose`](@ref) to finalize everything. +Finalize a single subprogram early, sealing its retained-nodes list. After +this, no more local variables can be added to `sp`. + +Calling this is never required for correctness — [`dispose`](@ref) / +[`finalize!`](@ref) finalize every tracked subprogram automatically. Use it +only when streaming many subprograms through the builder and wanting to +release their bookkeeping early. """ finalize_subprogram!(builder::DIBuilder, sp::DISubProgram) = API.LLVMDIBuilderFinalizeSubprogram(builder, sp) From fad46d163f1e9a064f3cdd8ae7b093d5db762ea1 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 15:04:44 +0200 Subject: [PATCH 12/27] Move subprogram!'s linkage_name and scope_line to kwargs. The common case (e.g. Julia code) has linkage_name empty or equal to name, and scope_line equal to line. The C API requires both positionally; Julia can do better by defaulting linkage_name="" (LLVM falls back to name) and scope_line=line. Call sites previously reading subprogram!(dib, scope, "add", "add", file, 1, stype, 1) now read subprogram!(dib, scope, "add", file, 1, stype). Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/src/lib/metadata.md | 2 +- src/debuginfo.jl | 12 +++++++----- test/debuginfo.jl | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/src/lib/metadata.md b/docs/src/lib/metadata.md index 8ff10193..2ea23266 100644 --- a/docs/src/lib/metadata.md +++ b/docs/src/lib/metadata.md @@ -187,7 +187,7 @@ LLVM.objcproperty! ```@docs DISubProgram line(::DISubProgram) -LLVM.subprogram!(::DIBuilder, ::DIScope, ::AbstractString, ::AbstractString, ::DIFile, ::Integer, ::LLVM.DISubroutineType, ::Integer) +LLVM.subprogram!(::DIBuilder, ::DIScope, ::AbstractString, ::DIFile, ::Integer, ::LLVM.DISubroutineType) ``` ### Variables diff --git a/src/debuginfo.jl b/src/debuginfo.jl index 1c2be0bd..dc1ecb89 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -1014,17 +1014,19 @@ line(subprogram::DISubProgram) = Int(API.LLVMDISubprogramGetLine(subprogram)) """ subprogram!(builder::DIBuilder, scope::DIScope, name::AbstractString, - linkage_name::AbstractString, file::DIFile, line::Integer, - type::DISubroutineType, scope_line::Integer; + file::DIFile, line::Integer, type::DISubroutineType; + linkage_name::AbstractString="", scope_line::Integer=line, is_local_to_unit::Bool=false, is_definition::Bool=true, flags=API.LLVMDIFlagZero, is_optimized::Bool=false) -> DISubProgram -Create a new [`DISubProgram`](@ref) describing a function. +Create a new [`DISubProgram`](@ref) describing a function. When +`linkage_name` is empty, LLVM falls back to `name`. `scope_line` +defaults to the function's `line`, which is the usual case. """ function subprogram!(builder::DIBuilder, scope::DIScope, name::AbstractString, - linkage_name::AbstractString, file::DIFile, line::Integer, - type::DISubroutineType, scope_line::Integer; + file::DIFile, line::Integer, type::DISubroutineType; + linkage_name::AbstractString="", scope_line::Integer=line, is_local_to_unit::Bool=false, is_definition::Bool=true, flags=API.LLVMDIFlagZero, is_optimized::Bool=false) diff --git a/test/debuginfo.jl b/test/debuginfo.jl index 63f2b4c3..2e9953af 100644 --- a/test/debuginfo.jl +++ b/test/debuginfo.jl @@ -186,7 +186,7 @@ end stype = LLVM.subroutinetype!(dib, file, LLVM.Metadata[i64, i64, i64]) # subprogram - sp = LLVM.subprogram!(dib, file, "add", "add", file, 1, stype, 1) + sp = LLVM.subprogram!(dib, file, "add", file, 1, stype) @test sp isa DISubProgram @test LLVM.line(sp) == 1 @@ -236,7 +236,7 @@ end file, "LLVM.jl Tests") i64 = LLVM.basictype!(dib, "Int64", 64, DW_ATE_signed) stype = LLVM.subroutinetype!(dib, file, LLVM.Metadata[i64, i64, i64]) - sp = LLVM.subprogram!(dib, file, "add", "add", file, 1, stype, 1) + sp = LLVM.subprogram!(dib, file, "add", file, 1, stype) ft = LLVM.FunctionType(LLVM.Int64Type(), [LLVM.Int64Type(), LLVM.Int64Type()]) fn = LLVM.Function(mod, "add", ft) From a5fb9ad81024c5258351c9a97152d233db8476eb Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 15:05:37 +0200 Subject: [PATCH 13/27] Add named DW_TAG convenience wrappers for common qualifiers. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds consttype!, volatiletype!, lvaluereferencetype!, and rvaluereferencetype! as thin aliases for qualifiedtype! / referencetype! with the relevant DW_TAG. Spares users from remembering the four DWARF tag numbers that cover 95% of the qualified-type / C++ reference use cases. DW_TAG constants themselves aren't re-exported — that's a separate, principled job for all enums/constants in llvm-c. The wrappers hardcode the numeric tags internally so they remain usable today. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/src/lib/metadata.md | 4 ++++ src/debuginfo.jl | 48 +++++++++++++++++++++++++++++++++++++++- test/debuginfo.jl | 6 +++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/docs/src/lib/metadata.md b/docs/src/lib/metadata.md index 2ea23266..ee0d811b 100644 --- a/docs/src/lib/metadata.md +++ b/docs/src/lib/metadata.md @@ -136,8 +136,12 @@ Derived types (pointers, qualifiers, members, inheritance, ...): ```@docs LLVM.pointertype! LLVM.referencetype! +LLVM.lvaluereferencetype! +LLVM.rvaluereferencetype! LLVM.typedeftype! LLVM.qualifiedtype! +LLVM.consttype! +LLVM.volatiletype! LLVM.artificialtype! LLVM.objectpointertype! LLVM.membertype! diff --git a/src/debuginfo.jl b/src/debuginfo.jl index dc1ecb89..5b328774 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -487,12 +487,58 @@ end qualifiedtype!(builder::DIBuilder, tag::Integer, type::DIType) -> DIDerivedType Create a new qualified type, such as `const T` (`DW_TAG_const_type`) or -`volatile T` (`DW_TAG_volatile_type`). +`volatile T` (`DW_TAG_volatile_type`). See also the named convenience +wrappers [`consttype!`](@ref) and [`volatiletype!`](@ref). """ function qualifiedtype!(builder::DIBuilder, tag::Integer, type::DIType) DIDerivedType(API.LLVMDIBuilderCreateQualifiedType(builder, Cuint(tag), type)) end +@public consttype!, volatiletype!, lvaluereferencetype!, rvaluereferencetype! + +# DWARF tag values used by the convenience wrappers below. Not exported; part +# of a wider DWARF-constants cleanup. +const _DW_TAG_reference_type = 0x10 +const _DW_TAG_const_type = 0x26 +const _DW_TAG_volatile_type = 0x35 +const _DW_TAG_rvalue_reference_type = 0x42 + +""" + consttype!(builder::DIBuilder, type::DIType) -> DIDerivedType + +Create a `const`-qualified type. Shorthand for +`qualifiedtype!(builder, DW_TAG_const_type, type)`. +""" +consttype!(builder::DIBuilder, type::DIType) = + qualifiedtype!(builder, _DW_TAG_const_type, type) + +""" + volatiletype!(builder::DIBuilder, type::DIType) -> DIDerivedType + +Create a `volatile`-qualified type. Shorthand for +`qualifiedtype!(builder, DW_TAG_volatile_type, type)`. +""" +volatiletype!(builder::DIBuilder, type::DIType) = + qualifiedtype!(builder, _DW_TAG_volatile_type, type) + +""" + lvaluereferencetype!(builder::DIBuilder, type::DIType) -> DIDerivedType + +Create a C++ `T&` reference type. Shorthand for +`referencetype!(builder, DW_TAG_reference_type, type)`. +""" +lvaluereferencetype!(builder::DIBuilder, type::DIType) = + referencetype!(builder, _DW_TAG_reference_type, type) + +""" + rvaluereferencetype!(builder::DIBuilder, type::DIType) -> DIDerivedType + +Create a C++ `T&&` rvalue-reference type. Shorthand for +`referencetype!(builder, DW_TAG_rvalue_reference_type, type)`. +""" +rvaluereferencetype!(builder::DIBuilder, type::DIType) = + referencetype!(builder, _DW_TAG_rvalue_reference_type, type) + """ artificialtype!(builder::DIBuilder, type::DIType) -> DIDerivedType diff --git a/test/debuginfo.jl b/test/debuginfo.jl index 2e9953af..03a1d9d2 100644 --- a/test/debuginfo.jl +++ b/test/debuginfo.jl @@ -125,6 +125,12 @@ end ref = LLVM.referencetype!(dib, DW_TAG_reference_type, i64) @test ref isa LLVM.DIDerivedType + # named convenience wrappers + @test LLVM.consttype!(dib, i64) isa LLVM.DIDerivedType + @test LLVM.volatiletype!(dib, i64) isa LLVM.DIDerivedType + @test LLVM.lvaluereferencetype!(dib, i64) isa LLVM.DIDerivedType + @test LLVM.rvaluereferencetype!(dib, i64) isa LLVM.DIDerivedType + # composite types mem = LLVM.membertype!(dib, cu, "x", file, 2, 64, 64, 0, i64) @test mem isa LLVM.DIDerivedType From 8f6a971cc423649316013c1464d9ac05a0185b94 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 15:15:36 +0200 Subject: [PATCH 14/27] Split compound DIBuilder factory names with underscores. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Matches existing LLVM.jl precedent: multi-word descriptive names use underscores (function_type, return_type, called_type, value_type, fast_math, replace_metadata_uses!, parameter_attributes), while only LLVM-jargon acronyms stay compressed (addrspace, callconv, datalayout). The DIBuilder factories I originally landed as basictype!, pointertype!, structtype!, compileunit!, autovariable!, etc. broke that rule; rename them to basic_type!, pointer_type!, struct_type!, compile_unit!, auto_variable!, etc. Also folds the metadata-replace helper into the existing replace_uses! (wrapping LLVMReplaceAllUsesWith on Value) as a Metadata method — same semantics, cleaner dispatch than a parallel replace_all_uses_with!. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/src/lib/metadata.md | 90 ++++++------- src/debuginfo.jl | 271 ++++++++++++++++++++------------------- test/debuginfo.jl | 136 ++++++++++---------- 3 files changed, 249 insertions(+), 248 deletions(-) diff --git a/docs/src/lib/metadata.md b/docs/src/lib/metadata.md index ee0d811b..0c89991c 100644 --- a/docs/src/lib/metadata.md +++ b/docs/src/lib/metadata.md @@ -91,8 +91,8 @@ LLVM.file! ```@docs LLVM.DILexicalBlock LLVM.DILexicalBlockFile -LLVM.lexicalblock! -LLVM.lexicalblockfile! +LLVM.lexical_block! +LLVM.lexical_block_file! ``` ### Module @@ -126,64 +126,64 @@ LLVM.DISubrange Built-in factories for primitive types: ```@docs -LLVM.basictype! -LLVM.unspecifiedtype! -LLVM.nullptrtype! +LLVM.basic_type! +LLVM.unspecified_type! +LLVM.nullptr_type! ``` Derived types (pointers, qualifiers, members, inheritance, ...): ```@docs -LLVM.pointertype! -LLVM.referencetype! -LLVM.lvaluereferencetype! -LLVM.rvaluereferencetype! -LLVM.typedeftype! -LLVM.qualifiedtype! -LLVM.consttype! -LLVM.volatiletype! -LLVM.artificialtype! -LLVM.objectpointertype! -LLVM.membertype! -LLVM.bitfieldmembertype! -LLVM.staticmembertype! -LLVM.memberpointertype! +LLVM.pointer_type! +LLVM.reference_type! +LLVM.lvalue_reference_type! +LLVM.rvalue_reference_type! +LLVM.typedef_type! +LLVM.qualified_type! +LLVM.const_type! +LLVM.volatile_type! +LLVM.artificial_type! +LLVM.object_pointer_type! +LLVM.member_type! +LLVM.bitfield_member_type! +LLVM.static_member_type! +LLVM.member_pointer_type! LLVM.inheritance! ``` Composite types: ```@docs -LLVM.structtype! -LLVM.uniontype! -LLVM.classtype! -LLVM.arraytype! -LLVM.vectortype! +LLVM.struct_type! +LLVM.union_type! +LLVM.class_type! +LLVM.array_type! +LLVM.vector_type! LLVM.enumerator! -LLVM.enumerationtype! -LLVM.forwarddecl! -LLVM.replaceablecompositetype! -LLVM.getorcreatesubrange! +LLVM.enumeration_type! +LLVM.forward_decl! +LLVM.replaceable_composite_type! +LLVM.get_or_create_subrange! ``` Subroutine types: ```@docs -LLVM.subroutinetype! +LLVM.subroutine_type! ``` Array-node helpers: ```@docs -LLVM.getorcreatearray! -LLVM.getorcreatetypearray! +LLVM.get_or_create_array! +LLVM.get_or_create_type_array! ``` Objective-C: ```@docs -LLVM.objcivar! -LLVM.objcproperty! +LLVM.objc_ivar! +LLVM.objc_property! ``` ### Subprogram @@ -199,8 +199,8 @@ LLVM.subprogram!(::DIBuilder, ::DIScope, ::AbstractString, ::DIFile, ::Integer, Factories for local variables and parameters: ```@docs -LLVM.autovariable! -LLVM.parametervariable! +LLVM.auto_variable! +LLVM.parameter_variable! ``` ### Expressions @@ -209,21 +209,21 @@ LLVM.parametervariable! LLVM.DIExpression LLVM.DIGlobalVariableExpression LLVM.expression! -LLVM.constantvalueexpression! +LLVM.constant_value_expression! LLVM.variable LLVM.expression -LLVM.globalvariableexpression! -LLVM.tempglobalvariablefwddecl! +LLVM.global_variable_expression! +LLVM.temp_global_variable_fwd_decl! ``` ### Imported entities ```@docs LLVM.DIImportedEntity -LLVM.importedmodulefromnamespace! -LLVM.importedmodulefromalias! -LLVM.importedmodulefrommodule! -LLVM.importeddeclaration! +LLVM.imported_module_from_namespace! +LLVM.imported_module_from_alias! +LLVM.imported_module_from_module! +LLVM.imported_declaration! ``` ### Macros @@ -232,7 +232,7 @@ LLVM.importeddeclaration! LLVM.DIMacro LLVM.DIMacroFile LLVM.macro! -LLVM.tempmacrofile! +LLVM.temp_macro_file! ``` ### Instruction-level insertion @@ -254,7 +254,7 @@ LLVM.debuglocation!(::Instruction, ::DILocation) ```@docs DICompileUnit -LLVM.compileunit! +LLVM.compile_unit! ``` ### Mutation helpers @@ -262,7 +262,7 @@ LLVM.compileunit! ```@docs LLVM.temporary_mdnode LLVM.dispose_temporary -LLVM.replace_all_uses_with! +LLVM.replace_uses!(::LLVM.Metadata, ::LLVM.Metadata) ``` ### Other diff --git a/src/debuginfo.jl b/src/debuginfo.jl index 5b328774..7d71cb8f 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -305,12 +305,12 @@ end export DIType, DIEnumerator, DISubrange, name, offset, line, flags @public align, - basictype!, unspecifiedtype!, pointertype!, referencetype!, nullptrtype!, - typedeftype!, qualifiedtype!, artificialtype!, objectpointertype!, - inheritance!, membertype!, bitfieldmembertype!, staticmembertype!, - memberpointertype!, structtype!, uniontype!, classtype!, arraytype!, - vectortype!, enumerationtype!, enumerator!, forwarddecl!, - replaceablecompositetype!, subroutinetype!, getorcreatesubrange! + basic_type!, unspecified_type!, pointer_type!, reference_type!, nullptr_type!, + typedef_type!, qualified_type!, artificial_type!, object_pointer_type!, + inheritance!, member_type!, bitfield_member_type!, static_member_type!, + member_pointer_type!, struct_type!, union_type!, class_type!, array_type!, + vector_type!, enumeration_type!, enumerator!, forward_decl!, + replaceable_composite_type!, subroutine_type!, get_or_create_subrange! """ DIType @@ -408,13 +408,13 @@ tag(node::DINode) = Int(API.LLVMGetDINodeTag(node)) # basic types """ - basictype!(builder::DIBuilder, name::AbstractString, size_in_bits::Integer, + basic_type!(builder::DIBuilder, name::AbstractString, size_in_bits::Integer, encoding::Integer; flags=API.LLVMDIFlagZero) -> DIBasicType Create a new [`DIBasicType`](@ref), such as an integer or floating-point type. `encoding` is a `DW_ATE_*` value (see the DWARF standard). """ -function basictype!(builder::DIBuilder, name::AbstractString, size_in_bits::Integer, +function basic_type!(builder::DIBuilder, name::AbstractString, size_in_bits::Integer, encoding::Integer; flags=API.LLVMDIFlagZero) DIBasicType(API.LLVMDIBuilderCreateBasicType( builder, name, Csize_t(length(name)), @@ -422,11 +422,11 @@ function basictype!(builder::DIBuilder, name::AbstractString, size_in_bits::Inte end """ - unspecifiedtype!(builder::DIBuilder, name::AbstractString) -> DIBasicType + unspecified_type!(builder::DIBuilder, name::AbstractString) -> DIBasicType Create a new unspecified type (`DW_TAG_unspecified_type`), e.g. a C++ `decltype(nullptr)`. """ -function unspecifiedtype!(builder::DIBuilder, name::AbstractString) +function unspecified_type!(builder::DIBuilder, name::AbstractString) DIBasicType(API.LLVMDIBuilderCreateUnspecifiedType( builder, name, Csize_t(length(name)))) end @@ -435,13 +435,13 @@ end # derived types """ - pointertype!(builder::DIBuilder, pointee_type::DIType, size_in_bits::Integer; + pointer_type!(builder::DIBuilder, pointee_type::DIType, size_in_bits::Integer; align_in_bits::Integer=0, address_space::Integer=0, name::AbstractString="") -> DIDerivedType Create a new pointer type. """ -function pointertype!(builder::DIBuilder, pointee_type::DIType, size_in_bits::Integer; +function pointer_type!(builder::DIBuilder, pointee_type::DIType, size_in_bits::Integer; align_in_bits::Integer=0, address_space::Integer=0, name::AbstractString="") DIDerivedType(API.LLVMDIBuilderCreatePointerType( @@ -451,31 +451,31 @@ function pointertype!(builder::DIBuilder, pointee_type::DIType, size_in_bits::In end """ - referencetype!(builder::DIBuilder, tag::Integer, type::DIType) -> DIDerivedType + reference_type!(builder::DIBuilder, tag::Integer, type::DIType) -> DIDerivedType Create a new reference type (C++ `T&` / `T&&`), with the given DWARF `tag` (e.g. `DW_TAG_reference_type` or `DW_TAG_rvalue_reference_type`). """ -function referencetype!(builder::DIBuilder, tag::Integer, type::DIType) +function reference_type!(builder::DIBuilder, tag::Integer, type::DIType) DIDerivedType(API.LLVMDIBuilderCreateReferenceType(builder, Cuint(tag), type)) end """ - nullptrtype!(builder::DIBuilder) -> DIBasicType + nullptr_type!(builder::DIBuilder) -> DIBasicType Create a new type representing a null pointer. """ -nullptrtype!(builder::DIBuilder) = +nullptr_type!(builder::DIBuilder) = DIBasicType(API.LLVMDIBuilderCreateNullPtrType(builder)) """ - typedeftype!(builder::DIBuilder, type::DIType, name::AbstractString, + typedef_type!(builder::DIBuilder, type::DIType, name::AbstractString, file::DIFile, line::Integer, scope::DIScope; align_in_bits::Integer=0) -> DIDerivedType Create a new typedef type. """ -function typedeftype!(builder::DIBuilder, type::DIType, name::AbstractString, +function typedef_type!(builder::DIBuilder, type::DIType, name::AbstractString, file::DIFile, line::Integer, scope::DIScope; align_in_bits::Integer=0) DIDerivedType(API.LLVMDIBuilderCreateTypedef( @@ -484,17 +484,17 @@ function typedeftype!(builder::DIBuilder, type::DIType, name::AbstractString, end """ - qualifiedtype!(builder::DIBuilder, tag::Integer, type::DIType) -> DIDerivedType + qualified_type!(builder::DIBuilder, tag::Integer, type::DIType) -> DIDerivedType Create a new qualified type, such as `const T` (`DW_TAG_const_type`) or `volatile T` (`DW_TAG_volatile_type`). See also the named convenience -wrappers [`consttype!`](@ref) and [`volatiletype!`](@ref). +wrappers [`const_type!`](@ref) and [`volatile_type!`](@ref). """ -function qualifiedtype!(builder::DIBuilder, tag::Integer, type::DIType) +function qualified_type!(builder::DIBuilder, tag::Integer, type::DIType) DIDerivedType(API.LLVMDIBuilderCreateQualifiedType(builder, Cuint(tag), type)) end -@public consttype!, volatiletype!, lvaluereferencetype!, rvaluereferencetype! +@public const_type!, volatile_type!, lvalue_reference_type!, rvalue_reference_type! # DWARF tag values used by the convenience wrappers below. Not exported; part # of a wider DWARF-constants cleanup. @@ -504,55 +504,55 @@ const _DW_TAG_volatile_type = 0x35 const _DW_TAG_rvalue_reference_type = 0x42 """ - consttype!(builder::DIBuilder, type::DIType) -> DIDerivedType + const_type!(builder::DIBuilder, type::DIType) -> DIDerivedType Create a `const`-qualified type. Shorthand for -`qualifiedtype!(builder, DW_TAG_const_type, type)`. +`qualified_type!(builder, DW_TAG_const_type, type)`. """ -consttype!(builder::DIBuilder, type::DIType) = - qualifiedtype!(builder, _DW_TAG_const_type, type) +const_type!(builder::DIBuilder, type::DIType) = + qualified_type!(builder, _DW_TAG_const_type, type) """ - volatiletype!(builder::DIBuilder, type::DIType) -> DIDerivedType + volatile_type!(builder::DIBuilder, type::DIType) -> DIDerivedType Create a `volatile`-qualified type. Shorthand for -`qualifiedtype!(builder, DW_TAG_volatile_type, type)`. +`qualified_type!(builder, DW_TAG_volatile_type, type)`. """ -volatiletype!(builder::DIBuilder, type::DIType) = - qualifiedtype!(builder, _DW_TAG_volatile_type, type) +volatile_type!(builder::DIBuilder, type::DIType) = + qualified_type!(builder, _DW_TAG_volatile_type, type) """ - lvaluereferencetype!(builder::DIBuilder, type::DIType) -> DIDerivedType + lvalue_reference_type!(builder::DIBuilder, type::DIType) -> DIDerivedType Create a C++ `T&` reference type. Shorthand for -`referencetype!(builder, DW_TAG_reference_type, type)`. +`reference_type!(builder, DW_TAG_reference_type, type)`. """ -lvaluereferencetype!(builder::DIBuilder, type::DIType) = - referencetype!(builder, _DW_TAG_reference_type, type) +lvalue_reference_type!(builder::DIBuilder, type::DIType) = + reference_type!(builder, _DW_TAG_reference_type, type) """ - rvaluereferencetype!(builder::DIBuilder, type::DIType) -> DIDerivedType + rvalue_reference_type!(builder::DIBuilder, type::DIType) -> DIDerivedType Create a C++ `T&&` rvalue-reference type. Shorthand for -`referencetype!(builder, DW_TAG_rvalue_reference_type, type)`. +`reference_type!(builder, DW_TAG_rvalue_reference_type, type)`. """ -rvaluereferencetype!(builder::DIBuilder, type::DIType) = - referencetype!(builder, _DW_TAG_rvalue_reference_type, type) +rvalue_reference_type!(builder::DIBuilder, type::DIType) = + reference_type!(builder, _DW_TAG_rvalue_reference_type, type) """ - artificialtype!(builder::DIBuilder, type::DIType) -> DIDerivedType + artificial_type!(builder::DIBuilder, type::DIType) -> DIDerivedType Create a new artificial type (`DI_FLAG_ARTIFICIAL`), e.g. an implicit `this`. """ -artificialtype!(builder::DIBuilder, type::DIType) = +artificial_type!(builder::DIBuilder, type::DIType) = DIDerivedType(API.LLVMDIBuilderCreateArtificialType(builder, type)) """ - objectpointertype!(builder::DIBuilder, type::DIType) -> DIDerivedType + object_pointer_type!(builder::DIBuilder, type::DIType) -> DIDerivedType Create a new type identifying an object pointer (`DI_FLAG_OBJECT_POINTER`). """ -objectpointertype!(builder::DIBuilder, type::DIType) = +object_pointer_type!(builder::DIBuilder, type::DIType) = DIDerivedType(API.LLVMDIBuilderCreateObjectPointerType(builder, type)) """ @@ -570,14 +570,14 @@ function inheritance!(builder::DIBuilder, derived::DIType, base::DIType, end """ - membertype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + member_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, file::DIFile, line::Integer, size_in_bits::Integer, align_in_bits::Integer, offset_in_bits::Integer, type::DIType; flags=API.LLVMDIFlagZero) -> DIDerivedType Create a new member (field) of a composite type. """ -function membertype!(builder::DIBuilder, scope::DIScope, name::AbstractString, +function member_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, file::DIFile, line::Integer, size_in_bits::Integer, align_in_bits::Integer, offset_in_bits::Integer, type::DIType; flags=API.LLVMDIFlagZero) @@ -589,14 +589,14 @@ function membertype!(builder::DIBuilder, scope::DIScope, name::AbstractString, end """ - bitfieldmembertype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + bitfield_member_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, file::DIFile, line::Integer, size_in_bits::Integer, offset_in_bits::Integer, storage_offset_in_bits::Integer, type::DIType; flags=API.LLVMDIFlagZero) -> DIDerivedType Create a new bit-field member of a composite type. """ -function bitfieldmembertype!(builder::DIBuilder, scope::DIScope, name::AbstractString, +function bitfield_member_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, file::DIFile, line::Integer, size_in_bits::Integer, offset_in_bits::Integer, storage_offset_in_bits::Integer, type::DIType; flags=API.LLVMDIFlagZero) @@ -608,7 +608,7 @@ function bitfieldmembertype!(builder::DIBuilder, scope::DIScope, name::AbstractS end """ - staticmembertype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + static_member_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, file::DIFile, line::Integer, type::DIType; flags=API.LLVMDIFlagZero, constant_val=nothing, @@ -616,7 +616,7 @@ end Create a new static member of a composite type. """ -function staticmembertype!(builder::DIBuilder, scope::DIScope, name::AbstractString, +function static_member_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, file::DIFile, line::Integer, type::DIType; flags=API.LLVMDIFlagZero, constant_val=nothing, @@ -628,14 +628,14 @@ function staticmembertype!(builder::DIBuilder, scope::DIScope, name::AbstractStr end """ - memberpointertype!(builder::DIBuilder, pointee_type::DIType, class_type::DIType, + member_pointer_type!(builder::DIBuilder, pointee_type::DIType, class_type::DIType, size_in_bits::Integer; align_in_bits::Integer=0, flags=API.LLVMDIFlagZero) -> DIDerivedType Create a new pointer-to-member type for C++. """ -function memberpointertype!(builder::DIBuilder, pointee_type::DIType, class_type::DIType, +function member_pointer_type!(builder::DIBuilder, pointee_type::DIType, class_type::DIType, size_in_bits::Integer; align_in_bits::Integer=0, flags=API.LLVMDIFlagZero) @@ -648,7 +648,7 @@ end # composite types """ - structtype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + struct_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, file::DIFile, line::Integer, size_in_bits::Integer, align_in_bits::Integer, elements::Vector{<:Metadata}; flags=API.LLVMDIFlagZero, derived_from=nothing, @@ -657,7 +657,7 @@ end Create a new struct type. """ -function structtype!(builder::DIBuilder, scope::DIScope, name::AbstractString, +function struct_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, file::DIFile, line::Integer, size_in_bits::Integer, align_in_bits::Integer, elements::Vector{<:Metadata}; flags=API.LLVMDIFlagZero, derived_from=nothing, @@ -676,7 +676,7 @@ function structtype!(builder::DIBuilder, scope::DIScope, name::AbstractString, end """ - uniontype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + union_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, file::DIFile, line::Integer, size_in_bits::Integer, align_in_bits::Integer, elements::Vector{<:Metadata}; flags=API.LLVMDIFlagZero, runtime_lang::Integer=0, @@ -684,7 +684,7 @@ end Create a new union type. """ -function uniontype!(builder::DIBuilder, scope::DIScope, name::AbstractString, +function union_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, file::DIFile, line::Integer, size_in_bits::Integer, align_in_bits::Integer, elements::Vector{<:Metadata}; flags=API.LLVMDIFlagZero, runtime_lang::Integer=0, @@ -700,7 +700,7 @@ function uniontype!(builder::DIBuilder, scope::DIScope, name::AbstractString, end """ - classtype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + class_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, file::DIFile, line::Integer, size_in_bits::Integer, align_in_bits::Integer, offset_in_bits::Integer, elements::Vector{<:Metadata}; @@ -710,7 +710,7 @@ end Create a new C++ class type. """ -function classtype!(builder::DIBuilder, scope::DIScope, name::AbstractString, +function class_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, file::DIFile, line::Integer, size_in_bits::Integer, align_in_bits::Integer, offset_in_bits::Integer, elements::Vector{<:Metadata}; @@ -731,13 +731,13 @@ function classtype!(builder::DIBuilder, scope::DIScope, name::AbstractString, end """ - arraytype!(builder::DIBuilder, size::Integer, align_in_bits::Integer, + array_type!(builder::DIBuilder, size::Integer, align_in_bits::Integer, element_type::DIType, subscripts::Vector{<:Metadata}) -> DICompositeType Create a new array type. Subscripts are typically built with -[`getorcreatesubrange!`](@ref). +[`get_or_create_subrange!`](@ref). """ -function arraytype!(builder::DIBuilder, size::Integer, align_in_bits::Integer, +function array_type!(builder::DIBuilder, size::Integer, align_in_bits::Integer, element_type::DIType, subscripts::Vector{<:Metadata}) subs = convert(Vector{Metadata}, subscripts) DICompositeType(API.LLVMDIBuilderCreateArrayType( @@ -746,13 +746,13 @@ function arraytype!(builder::DIBuilder, size::Integer, align_in_bits::Integer, end """ - vectortype!(builder::DIBuilder, size::Integer, align_in_bits::Integer, + vector_type!(builder::DIBuilder, size::Integer, align_in_bits::Integer, element_type::DIType, subscripts::Vector{<:Metadata}) -> DICompositeType Create a new vector type. Subscripts are typically built with -[`getorcreatesubrange!`](@ref). +[`get_or_create_subrange!`](@ref). """ -function vectortype!(builder::DIBuilder, size::Integer, align_in_bits::Integer, +function vector_type!(builder::DIBuilder, size::Integer, align_in_bits::Integer, element_type::DIType, subscripts::Vector{<:Metadata}) subs = convert(Vector{Metadata}, subscripts) DICompositeType(API.LLVMDIBuilderCreateVectorType( @@ -773,7 +773,7 @@ function enumerator!(builder::DIBuilder, name::AbstractString, value::Integer; end """ - enumerationtype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + enumeration_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, file::DIFile, line::Integer, size_in_bits::Integer, align_in_bits::Integer, elements::Vector{<:Metadata}; class_ty=nothing) -> DICompositeType @@ -781,7 +781,7 @@ end Create a new enumeration type. `elements` should be a vector of [`DIEnumerator`](@ref) metadata nodes. """ -function enumerationtype!(builder::DIBuilder, scope::DIScope, name::AbstractString, +function enumeration_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, file::DIFile, line::Integer, size_in_bits::Integer, align_in_bits::Integer, elements::Vector{<:Metadata}; class_ty=nothing) @@ -795,7 +795,7 @@ function enumerationtype!(builder::DIBuilder, scope::DIScope, name::AbstractStri end """ - forwarddecl!(builder::DIBuilder, tag::Integer, name::AbstractString, + forward_decl!(builder::DIBuilder, tag::Integer, name::AbstractString, scope::DIScope, file::DIFile, line::Integer; runtime_lang::Integer=0, size_in_bits::Integer=0, align_in_bits::Integer=0, @@ -803,7 +803,7 @@ end Create a new forward declaration to a composite type. """ -function forwarddecl!(builder::DIBuilder, tag::Integer, name::AbstractString, +function forward_decl!(builder::DIBuilder, tag::Integer, name::AbstractString, scope::DIScope, file::DIFile, line::Integer; runtime_lang::Integer=0, size_in_bits::Integer=0, align_in_bits::Integer=0, @@ -816,7 +816,7 @@ function forwarddecl!(builder::DIBuilder, tag::Integer, name::AbstractString, end """ - replaceablecompositetype!(builder::DIBuilder, tag::Integer, + replaceable_composite_type!(builder::DIBuilder, tag::Integer, name::AbstractString, scope::DIScope, file::DIFile, line::Integer; runtime_lang::Integer=0, size_in_bits::Integer=0, @@ -826,7 +826,7 @@ end Create a new replaceable composite type forward declaration. """ -function replaceablecompositetype!(builder::DIBuilder, tag::Integer, +function replaceable_composite_type!(builder::DIBuilder, tag::Integer, name::AbstractString, scope::DIScope, file::DIFile, line::Integer; runtime_lang::Integer=0, size_in_bits::Integer=0, @@ -844,14 +844,14 @@ end # subroutine types """ - subroutinetype!(builder::DIBuilder, file::DIFile, + subroutine_type!(builder::DIBuilder, file::DIFile, parameter_types::Vector{<:Metadata}; flags=API.LLVMDIFlagZero) -> DISubroutineType Create a new subroutine type. The first entry of `parameter_types` is the return type; the rest are the parameter types. """ -function subroutinetype!(builder::DIBuilder, file::DIFile, +function subroutine_type!(builder::DIBuilder, file::DIFile, parameter_types::Vector{<:Metadata}; flags=API.LLVMDIFlagZero) params = convert(Vector{Metadata}, parameter_types) @@ -862,36 +862,36 @@ end # subrange / array helpers -@public getorcreatearray!, getorcreatetypearray! +@public get_or_create_array!, get_or_create_type_array! """ - getorcreatesubrange!(builder::DIBuilder, lower_bound::Integer, count::Integer) + get_or_create_subrange!(builder::DIBuilder, lower_bound::Integer, count::Integer) Get or create a subrange metadata node, describing one dimension of an array or vector type. """ -getorcreatesubrange!(builder::DIBuilder, lower_bound::Integer, count::Integer) = +get_or_create_subrange!(builder::DIBuilder, lower_bound::Integer, count::Integer) = DISubrange(API.LLVMDIBuilderGetOrCreateSubrange( builder, Int64(lower_bound), Int64(count))) """ - getorcreatearray!(builder::DIBuilder, elements::Vector{<:Metadata}) + get_or_create_array!(builder::DIBuilder, elements::Vector{<:Metadata}) Get or create a generic metadata array node, used for lists such as `elements` fields of composite types. """ -function getorcreatearray!(builder::DIBuilder, elements::Vector{<:Metadata}) +function get_or_create_array!(builder::DIBuilder, elements::Vector{<:Metadata}) elts = convert(Vector{Metadata}, elements) Metadata(API.LLVMDIBuilderGetOrCreateArray(builder, elts, Csize_t(length(elts)))) end """ - getorcreatetypearray!(builder::DIBuilder, types::Vector{<:Metadata}) + get_or_create_type_array!(builder::DIBuilder, types::Vector{<:Metadata}) Get or create a metadata node for a type array, used for e.g. template parameter lists. """ -function getorcreatetypearray!(builder::DIBuilder, types::Vector{<:Metadata}) +function get_or_create_type_array!(builder::DIBuilder, types::Vector{<:Metadata}) tys = convert(Vector{Metadata}, types) Metadata(API.LLVMDIBuilderGetOrCreateTypeArray(builder, tys, Csize_t(length(tys)))) end @@ -899,17 +899,17 @@ end # ObjC -@public objcivar!, objcproperty! +@public objc_ivar!, objc_property! """ - objcivar!(builder::DIBuilder, name::AbstractString, file::DIFile, + objc_ivar!(builder::DIBuilder, name::AbstractString, file::DIFile, line::Integer, size_in_bits::Integer, align_in_bits::Integer, offset_in_bits::Integer, type::DIType, property_node::Metadata; flags=API.LLVMDIFlagZero) -> DIDerivedType Create a new Objective-C instance variable. """ -function objcivar!(builder::DIBuilder, name::AbstractString, file::DIFile, +function objc_ivar!(builder::DIBuilder, name::AbstractString, file::DIFile, line::Integer, size_in_bits::Integer, align_in_bits::Integer, offset_in_bits::Integer, type::DIType, property_node::Metadata; flags=API.LLVMDIFlagZero) @@ -921,13 +921,13 @@ function objcivar!(builder::DIBuilder, name::AbstractString, file::DIFile, end """ - objcproperty!(builder::DIBuilder, name::AbstractString, file::DIFile, + objc_property!(builder::DIBuilder, name::AbstractString, file::DIFile, line::Integer, getter::AbstractString, setter::AbstractString, attributes::Integer, type::DIType) -> DIDerivedType Create a new Objective-C `@property` descriptor. """ -function objcproperty!(builder::DIBuilder, name::AbstractString, file::DIFile, +function objc_property!(builder::DIBuilder, name::AbstractString, file::DIFile, line::Integer, getter::AbstractString, setter::AbstractString, attributes::Integer, type::DIType) DIDerivedType(API.LLVMDIBuilderCreateObjCProperty( @@ -943,16 +943,16 @@ end @static if version() >= v"21" -@public settype!, subrangetype!, dynamicarraytype!, enumeratorarb! +@public set_type!, subrange_type!, dynamic_array_type!, enumerator_arbitrary! """ - settype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + set_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, file::DIFile, line::Integer, size_in_bits::Integer, align_in_bits::Integer, base_type::DIType) -> DIDerivedType Create a new set type (`DW_TAG_set_type`). Requires LLVM 21+. """ -function settype!(builder::DIBuilder, scope::DIScope, name::AbstractString, +function set_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, file::DIFile, line::Integer, size_in_bits::Integer, align_in_bits::Integer, base_type::DIType) DIDerivedType(API.LLVMDIBuilderCreateSetType( @@ -962,7 +962,7 @@ function settype!(builder::DIBuilder, scope::DIScope, name::AbstractString, end """ - subrangetype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + subrange_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, line::Integer, file::DIFile, size_in_bits::Integer, align_in_bits::Integer, base_type::DIType; flags=API.LLVMDIFlagZero, @@ -971,7 +971,7 @@ end Create a new subrange type. Requires LLVM 21+. """ -function subrangetype!(builder::DIBuilder, scope::DIScope, name::AbstractString, +function subrange_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, line::Integer, file::DIFile, size_in_bits::Integer, align_in_bits::Integer, base_type::DIType; flags=API.LLVMDIFlagZero, @@ -988,7 +988,7 @@ function subrangetype!(builder::DIBuilder, scope::DIScope, name::AbstractString, end """ - dynamicarraytype!(builder::DIBuilder, scope::DIScope, name::AbstractString, + dynamic_array_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, line::Integer, file::DIFile, size::Integer, align_in_bits::Integer, element_type::DIType, subscripts::Vector{<:Metadata}; @@ -999,7 +999,7 @@ end Create a new dynamic array type (Fortran assumed-shape/deferred-shape arrays). Requires LLVM 21+. """ -function dynamicarraytype!(builder::DIBuilder, scope::DIScope, name::AbstractString, +function dynamic_array_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, line::Integer, file::DIFile, size::Integer, align_in_bits::Integer, element_type::DIType, subscripts::Vector{<:Metadata}; @@ -1020,13 +1020,13 @@ function dynamicarraytype!(builder::DIBuilder, scope::DIScope, name::AbstractStr end """ - enumeratorarb!(builder::DIBuilder, name::AbstractString, + enumerator_arbitrary!(builder::DIBuilder, name::AbstractString, size_in_bits::Integer, words::Vector{UInt64}; unsigned::Bool=false) -> DIEnumerator Create a new arbitrary-precision enumerator. Requires LLVM 21+. """ -function enumeratorarb!(builder::DIBuilder, name::AbstractString, +function enumerator_arbitrary!(builder::DIBuilder, name::AbstractString, size_in_bits::Integer, words::Vector{UInt64}; unsigned::Bool=false) DIEnumerator(API.LLVMDIBuilderCreateEnumeratorOfArbitraryPrecision( @@ -1102,7 +1102,7 @@ finalize_subprogram!(builder::DIBuilder, sp::DISubProgram) = ## compile unit export DICompileUnit -@public compileunit! +@public compile_unit! """ DICompileUnit @@ -1115,7 +1115,7 @@ end register(DICompileUnit, API.LLVMDICompileUnitMetadataKind) """ - compileunit!(builder::DIBuilder, lang, file::DIFile, producer::AbstractString; + compile_unit!(builder::DIBuilder, lang, file::DIFile, producer::AbstractString; optimized::Bool=true, flags::AbstractString="", runtime_version::Integer=0, split_name::Union{AbstractString,Nothing}=nothing, @@ -1128,7 +1128,7 @@ register(DICompileUnit, API.LLVMDICompileUnitMetadataKind) Create a new [`DICompileUnit`](@ref). `lang` is a `LLVMDWARFSourceLanguage` value (e.g. `LLVM.API.LLVMDWARFSourceLanguageJulia`). """ -function compileunit!(builder::DIBuilder, lang, file::DIFile, producer::AbstractString; +function compile_unit!(builder::DIBuilder, lang, file::DIFile, producer::AbstractString; optimized::Bool=true, flags::AbstractString="", runtime_version::Integer=0, @@ -1194,10 +1194,10 @@ end ## variable factories -@public autovariable!, parametervariable! +@public auto_variable!, parameter_variable! """ - autovariable!(builder::DIBuilder, scope::DIScope, name::AbstractString, + auto_variable!(builder::DIBuilder, scope::DIScope, name::AbstractString, file::DIFile, line::Integer, type::DIType; always_preserve::Bool=false, flags=API.LLVMDIFlagZero, align_in_bits::Integer=0) -> DILocalVariable @@ -1205,7 +1205,7 @@ end Create a new local variable descriptor (for a compiler-introduced automatic variable). """ -function autovariable!(builder::DIBuilder, scope::DIScope, name::AbstractString, +function auto_variable!(builder::DIBuilder, scope::DIScope, name::AbstractString, file::DIFile, line::Integer, type::DIType; always_preserve::Bool=false, flags=API.LLVMDIFlagZero, align_in_bits::Integer=0) @@ -1216,7 +1216,7 @@ function autovariable!(builder::DIBuilder, scope::DIScope, name::AbstractString, end """ - parametervariable!(builder::DIBuilder, scope::DIScope, name::AbstractString, + parameter_variable!(builder::DIBuilder, scope::DIScope, name::AbstractString, arg_no::Integer, file::DIFile, line::Integer, type::DIType; always_preserve::Bool=false, flags=API.LLVMDIFlagZero) -> DILocalVariable @@ -1224,7 +1224,7 @@ end Create a new descriptor for a function parameter variable. `arg_no` is the 1-based parameter index. """ -function parametervariable!(builder::DIBuilder, scope::DIScope, name::AbstractString, +function parameter_variable!(builder::DIBuilder, scope::DIScope, name::AbstractString, arg_no::Integer, file::DIFile, line::Integer, type::DIType; always_preserve::Bool=false, flags=API.LLVMDIFlagZero) @@ -1238,7 +1238,7 @@ end ## expression export DIExpression, DIGlobalVariableExpression, variable, expression -@public expression!, constantvalueexpression! +@public expression!, constant_value_expression! """ DIExpression @@ -1273,11 +1273,11 @@ function expression!(builder::DIBuilder, addr::Vector{UInt64}=UInt64[]) end """ - constantvalueexpression!(builder::DIBuilder, value::Integer) -> DIExpression + constant_value_expression!(builder::DIBuilder, value::Integer) -> DIExpression Create a new [`DIExpression`](@ref) representing a single constant value. """ -function constantvalueexpression!(builder::DIBuilder, value::Integer) +function constant_value_expression!(builder::DIBuilder, value::Integer) DIExpression(API.LLVMDIBuilderCreateConstantValueExpression( builder, UInt64(value))) end @@ -1305,10 +1305,10 @@ end ## global variable -@public globalvariableexpression!, tempglobalvariablefwddecl! +@public global_variable_expression!, temp_global_variable_fwd_decl! """ - globalvariableexpression!(builder::DIBuilder, scope::DIScope, + global_variable_expression!(builder::DIBuilder, scope::DIScope, name::AbstractString, linkage::AbstractString, file::DIFile, line::Integer, type::DIType, local_to_unit::Bool, expression::DIExpression; @@ -1317,7 +1317,7 @@ end Create a new global variable descriptor paired with a DWARF expression. """ -function globalvariableexpression!(builder::DIBuilder, scope::DIScope, +function global_variable_expression!(builder::DIBuilder, scope::DIScope, name::AbstractString, linkage::AbstractString, file::DIFile, line::Integer, type::DIType, local_to_unit::Bool, expression::DIExpression; @@ -1331,7 +1331,7 @@ function globalvariableexpression!(builder::DIBuilder, scope::DIScope, end """ - tempglobalvariablefwddecl!(builder::DIBuilder, scope::DIScope, + temp_global_variable_fwd_decl!(builder::DIBuilder, scope::DIScope, name::AbstractString, linkage::AbstractString, file::DIFile, line::Integer, type::DIType, local_to_unit::Bool; @@ -1340,7 +1340,7 @@ end Create a new temporary forward declaration for a global variable. """ -function tempglobalvariablefwddecl!(builder::DIBuilder, scope::DIScope, +function temp_global_variable_fwd_decl!(builder::DIBuilder, scope::DIScope, name::AbstractString, linkage::AbstractString, file::DIFile, line::Integer, type::DIType, local_to_unit::Bool; @@ -1357,7 +1357,7 @@ end ## lexical block export DILexicalBlock, DILexicalBlockFile -@public lexicalblock!, lexicalblockfile! +@public lexical_block!, lexical_block_file! """ DILexicalBlock @@ -1380,25 +1380,25 @@ end register(DILexicalBlockFile, API.LLVMDILexicalBlockFileMetadataKind) """ - lexicalblock!(builder::DIBuilder, scope::DIScope, file::DIFile, + lexical_block!(builder::DIBuilder, scope::DIScope, file::DIFile, line::Integer, column::Integer) -> DILexicalBlock Create a new [`DILexicalBlock`](@ref) describing a nested source scope. """ -function lexicalblock!(builder::DIBuilder, scope::DIScope, file::DIFile, +function lexical_block!(builder::DIBuilder, scope::DIScope, file::DIFile, line::Integer, column::Integer) DILexicalBlock(API.LLVMDIBuilderCreateLexicalBlock( builder, scope, file, Cuint(line), Cuint(column))) end """ - lexicalblockfile!(builder::DIBuilder, scope::DIScope, file::DIFile, + lexical_block_file!(builder::DIBuilder, scope::DIScope, file::DIFile, discriminator::Integer=0) -> DILexicalBlockFile Create a new [`DILexicalBlockFile`](@ref) for tracking source-file changes within a lexical scope. """ -function lexicalblockfile!(builder::DIBuilder, scope::DIScope, file::DIFile, +function lexical_block_file!(builder::DIBuilder, scope::DIScope, file::DIFile, discriminator::Integer=0) DILexicalBlockFile(API.LLVMDIBuilderCreateLexicalBlockFile( builder, scope, file, Cuint(discriminator))) @@ -1625,8 +1625,8 @@ end # @static version check ## imported entity export DIImportedEntity -@public importedmodulefromnamespace!, importedmodulefromalias!, - importedmodulefrommodule!, importeddeclaration! +@public imported_module_from_namespace!, imported_module_from_alias!, + imported_module_from_module!, imported_declaration! """ DIImportedEntity @@ -1639,26 +1639,26 @@ end register(DIImportedEntity, API.LLVMDIImportedEntityMetadataKind) """ - importedmodulefromnamespace!(builder::DIBuilder, scope::DIScope, + imported_module_from_namespace!(builder::DIBuilder, scope::DIScope, ns::DINamespace, file::DIFile, line::Integer) -> DIImportedEntity Create a new `DIImportedEntity` from a namespace. """ -importedmodulefromnamespace!(builder::DIBuilder, scope::DIScope, ns::DINamespace, +imported_module_from_namespace!(builder::DIBuilder, scope::DIScope, ns::DINamespace, file::DIFile, line::Integer) = DIImportedEntity(API.LLVMDIBuilderCreateImportedModuleFromNamespace( builder, scope, ns, file, Cuint(line))) """ - importedmodulefromalias!(builder::DIBuilder, scope::DIScope, + imported_module_from_alias!(builder::DIBuilder, scope::DIScope, imported::DIImportedEntity, file::DIFile, line::Integer, elements::Vector{<:Metadata}=Metadata[]) -> DIImportedEntity Create a new `DIImportedEntity` from an alias. """ -function importedmodulefromalias!(builder::DIBuilder, scope::DIScope, +function imported_module_from_alias!(builder::DIBuilder, scope::DIScope, imported::DIImportedEntity, file::DIFile, line::Integer, elements::Vector{<:Metadata}=Metadata[]) @@ -1669,13 +1669,13 @@ function importedmodulefromalias!(builder::DIBuilder, scope::DIScope, end """ - importedmodulefrommodule!(builder::DIBuilder, scope::DIScope, + imported_module_from_module!(builder::DIBuilder, scope::DIScope, mod::DIModule, file::DIFile, line::Integer, elements::Vector{<:Metadata}=Metadata[]) -> DIImportedEntity Create a new `DIImportedEntity` from a module. """ -function importedmodulefrommodule!(builder::DIBuilder, scope::DIScope, +function imported_module_from_module!(builder::DIBuilder, scope::DIScope, mod::DIModule, file::DIFile, line::Integer, elements::Vector{<:Metadata}=Metadata[]) elts = convert(Vector{Metadata}, elements) @@ -1685,13 +1685,13 @@ function importedmodulefrommodule!(builder::DIBuilder, scope::DIScope, end """ - importeddeclaration!(builder::DIBuilder, scope::DIScope, decl::Metadata, + imported_declaration!(builder::DIBuilder, scope::DIScope, decl::Metadata, file::DIFile, line::Integer, name::AbstractString, elements::Vector{<:Metadata}=Metadata[]) -> DIImportedEntity Create a new `DIImportedEntity` from a declaration. """ -function importeddeclaration!(builder::DIBuilder, scope::DIScope, decl::Metadata, +function imported_declaration!(builder::DIBuilder, scope::DIScope, decl::Metadata, file::DIFile, line::Integer, name::AbstractString, elements::Vector{<:Metadata}=Metadata[]) elts = convert(Vector{Metadata}, elements) @@ -1705,7 +1705,7 @@ end ## macro export DIMacro, DIMacroFile -@public macro!, tempmacrofile! +@public macro!, temp_macro_file! """ DIMacro @@ -1745,13 +1745,13 @@ function macro!(builder::DIBuilder, parent_macrofile::Union{DIMacroFile,Nothing} end """ - tempmacrofile!(builder::DIBuilder, + temp_macro_file!(builder::DIBuilder, parent_macrofile::Union{DIMacroFile,Nothing}, line::Integer, file::DIFile) -> DIMacroFile Create a new (temporary) [`DIMacroFile`](@ref). """ -tempmacrofile!(builder::DIBuilder, parent_macrofile::Union{DIMacroFile,Nothing}, +temp_macro_file!(builder::DIBuilder, parent_macrofile::Union{DIMacroFile,Nothing}, line::Integer, file::DIFile) = DIMacroFile(API.LLVMDIBuilderCreateTempMacroFile( builder, something(parent_macrofile, C_NULL), Cuint(line), file)) @@ -1782,14 +1782,14 @@ debuglocation!(inst::Instruction, loc::DILocation) = ## mutation / advanced helpers -@public temporary_mdnode, dispose_temporary, replace_all_uses_with! +@public temporary_mdnode, dispose_temporary """ temporary_mdnode(operands::Vector{<:Metadata}=Metadata[]) -> MDNode Create a temporary metadata node in the task-local [`context`](@ref) with the given operands. Temporary nodes are useful for constructing cycles and must be -either replaced via [`replace_all_uses_with!`](@ref) or disposed of via +either replaced via [`replace_uses!`](@ref) or disposed of via [`dispose_temporary`](@ref). """ function temporary_mdnode(operands::Vector{<:Metadata}=Metadata[]) @@ -1806,25 +1806,26 @@ Dispose of a temporary metadata node returned by [`temporary_mdnode`](@ref). dispose_temporary(md::Metadata) = API.LLVMDisposeTemporaryMDNode(md) """ - replace_all_uses_with!(temp::Metadata, replacement::Metadata) + replace_uses!(temp::Metadata, replacement::Metadata) -Replace all uses of `temp` with `replacement`, and dispose of `temp`. +Replace all uses of temporary metadata `temp` with `replacement`, and dispose +of `temp`. Method on the existing [`replace_uses!`](@ref) for [`Value`](@ref). """ -replace_all_uses_with!(temp::Metadata, replacement::Metadata) = +replace_uses!(temp::Metadata, replacement::Metadata) = API.LLVMMetadataReplaceAllUsesWith(temp, replacement) @static if version() >= v"21" -@public replacearrays!, replacetype! +@public replace_arrays!, replace_type! """ - replacearrays!(builder::DIBuilder, T::DICompositeType, + replace_arrays!(builder::DIBuilder, T::DICompositeType, elements::Vector{<:Metadata}) Replace the elements array of the given composite type `T`. Requires LLVM 21+. """ -function replacearrays!(builder::DIBuilder, T::DICompositeType, +function replace_arrays!(builder::DIBuilder, T::DICompositeType, elements::Vector{<:Metadata}) elts = convert(Vector{Metadata}, elements) tref = Ref(T.ref) @@ -1833,11 +1834,11 @@ function replacearrays!(builder::DIBuilder, T::DICompositeType, end """ - replacetype!(sp::DISubProgram, ty::DISubroutineType) + replace_type!(sp::DISubProgram, ty::DISubroutineType) Replace the type of the given subprogram. Requires LLVM 21+. """ -replacetype!(sp::DISubProgram, ty::DISubroutineType) = +replace_type!(sp::DISubProgram, ty::DISubroutineType) = API.LLVMDISubprogramReplaceType(sp, ty) end # @static version check diff --git a/test/debuginfo.jl b/test/debuginfo.jl index 03a1d9d2..5ee5f61e 100644 --- a/test/debuginfo.jl +++ b/test/debuginfo.jl @@ -37,7 +37,7 @@ end @test LLVM.filename(file) == "test.jl" @test LLVM.directory(file) == "/tmp" - cu = LLVM.compileunit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, + cu = LLVM.compile_unit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, file, "LLVM.jl Tests") @test cu isa DICompileUnit @@ -61,13 +61,13 @@ end @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin DIBuilder(mod) do dib file = LLVM.file!(dib, "test.jl", "/tmp") - cu = LLVM.compileunit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, + cu = LLVM.compile_unit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, file, "LLVM.jl Tests") - lb = LLVM.lexicalblock!(dib, cu, file, 3, 5) + lb = LLVM.lexical_block!(dib, cu, file, 3, 5) @test lb isa DILexicalBlock - lbf = LLVM.lexicalblockfile!(dib, lb, file, 0) + lbf = LLVM.lexical_block_file!(dib, lb, file, 0) @test lbf isa DILexicalBlockFile # DILocation with lexical block scope @@ -93,88 +93,88 @@ end @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin DIBuilder(mod) do dib file = LLVM.file!(dib, "test.jl", "/tmp") - cu = LLVM.compileunit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, + cu = LLVM.compile_unit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, file, "LLVM.jl Tests") # basic types - i64 = LLVM.basictype!(dib, "Int64", 64, DW_ATE_signed) + i64 = LLVM.basic_type!(dib, "Int64", 64, DW_ATE_signed) @test i64 isa LLVM.DIBasicType @test LLVM.name(i64) == "Int64" @test LLVM.align(i64) == 0 @test LLVM.tag(i64) != 0 - @test LLVM.unspecifiedtype!(dib, "unspec") isa LLVM.DIBasicType - @test LLVM.nullptrtype!(dib) isa LLVM.DIBasicType + @test LLVM.unspecified_type!(dib, "unspec") isa LLVM.DIBasicType + @test LLVM.nullptr_type!(dib) isa LLVM.DIBasicType # derived types - ptr = LLVM.pointertype!(dib, i64, 64; name="i64_ptr") + ptr = LLVM.pointer_type!(dib, i64, 64; name="i64_ptr") @test ptr isa LLVM.DIDerivedType - td = LLVM.typedeftype!(dib, i64, "MyInt", file, 1, cu) + td = LLVM.typedef_type!(dib, i64, "MyInt", file, 1, cu) @test td isa LLVM.DIDerivedType - cq = LLVM.qualifiedtype!(dib, DW_TAG_const_type, i64) + cq = LLVM.qualified_type!(dib, DW_TAG_const_type, i64) @test cq isa LLVM.DIDerivedType - at2 = LLVM.artificialtype!(dib, i64) + at2 = LLVM.artificial_type!(dib, i64) @test at2 isa LLVM.DIDerivedType - op = LLVM.objectpointertype!(dib, i64) + op = LLVM.object_pointer_type!(dib, i64) @test op isa LLVM.DIDerivedType - ref = LLVM.referencetype!(dib, DW_TAG_reference_type, i64) + ref = LLVM.reference_type!(dib, DW_TAG_reference_type, i64) @test ref isa LLVM.DIDerivedType # named convenience wrappers - @test LLVM.consttype!(dib, i64) isa LLVM.DIDerivedType - @test LLVM.volatiletype!(dib, i64) isa LLVM.DIDerivedType - @test LLVM.lvaluereferencetype!(dib, i64) isa LLVM.DIDerivedType - @test LLVM.rvaluereferencetype!(dib, i64) isa LLVM.DIDerivedType + @test LLVM.const_type!(dib, i64) isa LLVM.DIDerivedType + @test LLVM.volatile_type!(dib, i64) isa LLVM.DIDerivedType + @test LLVM.lvalue_reference_type!(dib, i64) isa LLVM.DIDerivedType + @test LLVM.rvalue_reference_type!(dib, i64) isa LLVM.DIDerivedType # composite types - mem = LLVM.membertype!(dib, cu, "x", file, 2, 64, 64, 0, i64) + mem = LLVM.member_type!(dib, cu, "x", file, 2, 64, 64, 0, i64) @test mem isa LLVM.DIDerivedType - st = LLVM.structtype!(dib, cu, "Point", file, 1, 64, 64, LLVM.Metadata[mem]) + st = LLVM.struct_type!(dib, cu, "Point", file, 1, 64, 64, LLVM.Metadata[mem]) @test st isa LLVM.DICompositeType @test LLVM.name(st) == "Point" - un = LLVM.uniontype!(dib, cu, "U", file, 1, 64, 64, LLVM.Metadata[mem]) + un = LLVM.union_type!(dib, cu, "U", file, 1, 64, 64, LLVM.Metadata[mem]) @test un isa LLVM.DICompositeType - ct = LLVM.classtype!(dib, cu, "C", file, 1, 64, 64, 0, LLVM.Metadata[mem]) + ct = LLVM.class_type!(dib, cu, "C", file, 1, 64, 64, 0, LLVM.Metadata[mem]) @test ct isa LLVM.DICompositeType # arrays/vectors via subrange - sr = LLVM.getorcreatesubrange!(dib, 0, 10) + sr = LLVM.get_or_create_subrange!(dib, 0, 10) @test sr isa LLVM.DISubrange - aty = LLVM.arraytype!(dib, 640, 64, i64, [sr]) + aty = LLVM.array_type!(dib, 640, 64, i64, [sr]) @test aty isa LLVM.DICompositeType - vty = LLVM.vectortype!(dib, 256, 64, i64, [sr]) + vty = LLVM.vector_type!(dib, 256, 64, i64, [sr]) @test vty isa LLVM.DICompositeType # enumerations e1 = LLVM.enumerator!(dib, "A", 0) @test e1 isa LLVM.DIEnumerator - et = LLVM.enumerationtype!(dib, cu, "Color", file, 1, 32, 32, LLVM.Metadata[e1]) + et = LLVM.enumeration_type!(dib, cu, "Color", file, 1, 32, 32, LLVM.Metadata[e1]) @test et isa LLVM.DICompositeType # bitfield + static + member-pointer + inheritance - @test LLVM.bitfieldmembertype!(dib, cu, "b", file, 1, 3, 0, 0, i64) isa LLVM.DIDerivedType - @test LLVM.staticmembertype!(dib, cu, "s", file, 1, i64) isa LLVM.DIDerivedType - @test LLVM.memberpointertype!(dib, i64, st, 64) isa LLVM.DIDerivedType + @test LLVM.bitfield_member_type!(dib, cu, "b", file, 1, 3, 0, 0, i64) isa LLVM.DIDerivedType + @test LLVM.static_member_type!(dib, cu, "s", file, 1, i64) isa LLVM.DIDerivedType + @test LLVM.member_pointer_type!(dib, i64, st, 64) isa LLVM.DIDerivedType - base = LLVM.classtype!(dib, cu, "Base", file, 1, 64, 64, 0, LLVM.Metadata[]) + base = LLVM.class_type!(dib, cu, "Base", file, 1, 64, 64, 0, LLVM.Metadata[]) @test LLVM.inheritance!(dib, ct, base, 0) isa LLVM.DIDerivedType # subroutine - sroute = LLVM.subroutinetype!(dib, file, LLVM.Metadata[i64, i64]) + sroute = LLVM.subroutine_type!(dib, file, LLVM.Metadata[i64, i64]) @test sroute isa LLVM.DISubroutineType # forward decl / replaceable composite - @test LLVM.forwarddecl!(dib, DW_TAG_structure_type, "Fwd", cu, file, 1) isa LLVM.DICompositeType - @test LLVM.replaceablecompositetype!(dib, DW_TAG_structure_type, "Rep", cu, file, 1) isa LLVM.DICompositeType + @test LLVM.forward_decl!(dib, DW_TAG_structure_type, "Fwd", cu, file, 1) isa LLVM.DICompositeType + @test LLVM.replaceable_composite_type!(dib, DW_TAG_structure_type, "Rep", cu, file, 1) isa LLVM.DICompositeType end end end @@ -185,11 +185,11 @@ end @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin DIBuilder(mod) do dib file = LLVM.file!(dib, "test.jl", "/tmp") - cu = LLVM.compileunit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, + cu = LLVM.compile_unit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, file, "LLVM.jl Tests") - i64 = LLVM.basictype!(dib, "Int64", 64, DW_ATE_signed) - stype = LLVM.subroutinetype!(dib, file, LLVM.Metadata[i64, i64, i64]) + i64 = LLVM.basic_type!(dib, "Int64", 64, DW_ATE_signed) + stype = LLVM.subroutine_type!(dib, file, LLVM.Metadata[i64, i64, i64]) # subprogram sp = LLVM.subprogram!(dib, file, "add", file, 1, stype) @@ -197,24 +197,24 @@ end @test LLVM.line(sp) == 1 # variables - v = LLVM.autovariable!(dib, sp, "x", file, 2, i64) + v = LLVM.auto_variable!(dib, sp, "x", file, 2, i64) @test v isa LLVM.DILocalVariable @test LLVM.line(v) == 2 @test LLVM.file(v) == file @test LLVM.scope(v) == sp - p = LLVM.parametervariable!(dib, sp, "a", 1, file, 1, i64) + p = LLVM.parameter_variable!(dib, sp, "a", 1, file, 1, i64) @test p isa LLVM.DILocalVariable # expressions e = LLVM.expression!(dib) @test e isa LLVM.DIExpression - ce = LLVM.constantvalueexpression!(dib, 42) + ce = LLVM.constant_value_expression!(dib, 42) @test ce isa LLVM.DIExpression # global variable expression + accessors - gve = LLVM.globalvariableexpression!(dib, cu, "g", "g", + gve = LLVM.global_variable_expression!(dib, cu, "g", "g", file, 1, i64, false, e) @test gve isa LLVM.DIGlobalVariableExpression gv = LLVM.variable(gve) @@ -223,7 +223,7 @@ end @test LLVM.expression(gve) isa LLVM.DIExpression # temp global forward decl - tgv = LLVM.tempglobalvariablefwddecl!(dib, cu, "tg", "tg", + tgv = LLVM.temp_global_variable_fwd_decl!(dib, cu, "tg", "tg", file, 2, i64, false) @test tgv isa LLVM.DIGlobalVariable @@ -238,10 +238,10 @@ end @dispose ctx=Context() mod=LLVM.Module("SomeModule") builder=IRBuilder() begin DIBuilder(mod) do dib file = LLVM.file!(dib, "test.jl", "/tmp") - cu = LLVM.compileunit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, + cu = LLVM.compile_unit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, file, "LLVM.jl Tests") - i64 = LLVM.basictype!(dib, "Int64", 64, DW_ATE_signed) - stype = LLVM.subroutinetype!(dib, file, LLVM.Metadata[i64, i64, i64]) + i64 = LLVM.basic_type!(dib, "Int64", 64, DW_ATE_signed) + stype = LLVM.subroutine_type!(dib, file, LLVM.Metadata[i64, i64, i64]) sp = LLVM.subprogram!(dib, file, "add", file, 1, stype) ft = LLVM.FunctionType(LLVM.Int64Type(), [LLVM.Int64Type(), LLVM.Int64Type()]) @@ -252,7 +252,7 @@ end position!(builder, bb) x_alloca = alloca!(builder, LLVM.Int64Type(), "x.addr") - var = LLVM.autovariable!(dib, sp, "x", file, 2, i64) + var = LLVM.auto_variable!(dib, sp, "x", file, 2, i64) expr = LLVM.expression!(dib) loc = DILocation(2, 1, sp) @@ -302,26 +302,26 @@ end @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin DIBuilder(mod) do dib file = LLVM.file!(dib, "test.jl", "/tmp") - cu = LLVM.compileunit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, + cu = LLVM.compile_unit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, file, "LLVM.jl Tests") ns = LLVM.namespace!(dib, cu, "MyNS") - ie = LLVM.importedmodulefromnamespace!(dib, cu, ns, file, 1) + ie = LLVM.imported_module_from_namespace!(dib, cu, ns, file, 1) @test ie isa LLVM.DIImportedEntity - ie2 = LLVM.importedmodulefromalias!(dib, cu, ie, file, 2) + ie2 = LLVM.imported_module_from_alias!(dib, cu, ie, file, 2) @test ie2 isa LLVM.DIImportedEntity dm = LLVM.dimodule!(dib, cu, "MyMod") - ie3 = LLVM.importedmodulefrommodule!(dib, cu, dm, file, 3) + ie3 = LLVM.imported_module_from_module!(dib, cu, dm, file, 3) @test ie3 isa LLVM.DIImportedEntity # imported declaration (decl = another scope) - ied = LLVM.importeddeclaration!(dib, cu, ns, file, 4, "alias") + ied = LLVM.imported_declaration!(dib, cu, ns, file, 4, "alias") @test ied isa LLVM.DIImportedEntity # macros - mf = LLVM.tempmacrofile!(dib, nothing, 1, file) + mf = LLVM.temp_macro_file!(dib, nothing, 1, file) @test mf isa LLVM.DIMacroFile m = LLVM.macro!(dib, mf, 1, @@ -345,13 +345,13 @@ end @test temp isa LLVM.Metadata LLVM.dispose_temporary(temp) - # replace_all_uses_with! on a used temp + # replace_uses! on a used temp DW_ATE_signed = 0x05 - i64 = LLVM.basictype!(dib, "Int64", 64, DW_ATE_signed) + i64 = LLVM.basic_type!(dib, "Int64", 64, DW_ATE_signed) temp2 = LLVM.temporary_mdnode(LLVM.Metadata[i64]) real_node = MDNode([i64]) - LLVM.replace_all_uses_with!(temp2, real_node) + LLVM.replace_uses!(temp2, real_node) # temp2 is disposed as a side effect of RAUW end end @@ -360,23 +360,23 @@ end @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin DIBuilder(mod) do dib file = LLVM.file!(dib, "test.jl", "/tmp") - cu = LLVM.compileunit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, + cu = LLVM.compile_unit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, file, "LLVM.jl Tests") DW_TAG_structure_type = 0x13 DW_ATE_signed = 0x05 - fwd = LLVM.replaceablecompositetype!(dib, DW_TAG_structure_type, "Node", + fwd = LLVM.replaceable_composite_type!(dib, DW_TAG_structure_type, "Node", cu, file, 1; size_in_bits=64) - i64 = LLVM.basictype!(dib, "Int64", 64, DW_ATE_signed) - ptr_to_fwd = LLVM.pointertype!(dib, fwd, 64) + i64 = LLVM.basic_type!(dib, "Int64", 64, DW_ATE_signed) + ptr_to_fwd = LLVM.pointer_type!(dib, fwd, 64) - mem_val = LLVM.membertype!(dib, cu, "value", file, 1, 64, 64, 0, i64) - mem_next = LLVM.membertype!(dib, cu, "next", file, 2, 64, 64, 64, ptr_to_fwd) + mem_val = LLVM.member_type!(dib, cu, "value", file, 1, 64, 64, 0, i64) + mem_next = LLVM.member_type!(dib, cu, "next", file, 2, 64, 64, 64, ptr_to_fwd) - real_struct = LLVM.structtype!(dib, cu, "Node", file, 1, 128, 64, + real_struct = LLVM.struct_type!(dib, cu, "Node", file, 1, 128, 64, LLVM.Metadata[mem_val, mem_next]) - LLVM.replace_all_uses_with!(fwd, real_struct) + LLVM.replace_uses!(fwd, real_struct) end end end @@ -387,20 +387,20 @@ end @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin DIBuilder(mod) do dib file = LLVM.file!(dib, "test.jl", "/tmp") - cu = LLVM.compileunit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, + cu = LLVM.compile_unit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, file, "LLVM.jl Tests") - i64 = LLVM.basictype!(dib, "Int64", 64, DW_ATE_signed) + i64 = LLVM.basic_type!(dib, "Int64", 64, DW_ATE_signed) - ta = LLVM.getorcreatetypearray!(dib, LLVM.Metadata[i64, i64]) + ta = LLVM.get_or_create_type_array!(dib, LLVM.Metadata[i64, i64]) @test ta isa LLVM.Metadata - arr = LLVM.getorcreatearray!(dib, LLVM.Metadata[i64]) + arr = LLVM.get_or_create_array!(dib, LLVM.Metadata[i64]) @test arr isa LLVM.Metadata # ObjC (not widely used from Julia but round-tripped) - prop = LLVM.objcproperty!(dib, "count", file, 1, + prop = LLVM.objc_property!(dib, "count", file, 1, "getCount", "setCount:", 0, i64) @test prop isa LLVM.DIDerivedType - ivar = LLVM.objcivar!(dib, "_count", file, 1, 64, 64, 0, i64, prop) + ivar = LLVM.objc_ivar!(dib, "_count", file, 1, 64, 64, 0, i64, prop) @test ivar isa LLVM.DIDerivedType end From 304104b78733ca5fcf6c4d478acc3770517502f5 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 17:55:09 +0200 Subject: [PATCH 15/27] Skip DIBuilder finalize when no compile unit was registered. Previously, `dispose` (and `finalize!`) unconditionally called `LLVMDIBuilderFinalize`, which in assertion-enabled LLVM aborts with "creating type nodes without a CU is not supported" when no compile unit was registered. Track CU registration in a `has_compile_unit::Ref{Bool}` field on DIBuilder, set by `compile_unit!`. Finalization is skipped when the flag is false; in release LLVM the C call also early-returns in that case, so this only changes behavior under assertions. --- src/debuginfo.jl | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/debuginfo.jl b/src/debuginfo.jl index 7d71cb8f..e9c82cb6 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -7,13 +7,14 @@ export DIBuilder A builder for constructing debug information metadata. -This object needs to be disposed of using [`dispose`](@ref), which also resolves -any pending temporary metadata. Call [`finalize!`](@ref) explicitly only if you -need to use the finalized debug info (e.g. emit code) *before* disposing of the -builder. +This object needs to be disposed of using [`dispose`](@ref), which also +finalizes the debug info. Call [`finalize!`](@ref) explicitly only if you +need to use the finalized debug info (e.g. emit code) *before* disposing of +the builder. """ @checked struct DIBuilder ref::API.LLVMDIBuilderRef + has_compile_unit::Base.RefValue{Bool} end Base.unsafe_convert(::Type{API.LLVMDIBuilderRef}, builder::DIBuilder) = @@ -31,17 +32,21 @@ metadata nodes attached to the module so that cycles can be resolved during function DIBuilder(mod::Module; allow_unresolved::Bool=true) ref = allow_unresolved ? API.LLVMCreateDIBuilder(mod) : API.LLVMCreateDIBuilderDisallowUnresolved(mod) - mark_alloc(DIBuilder(ref)) + mark_alloc(DIBuilder(ref, Ref(false))) end """ dispose(builder::DIBuilder) -Finalize the debug info (resolving any pending temporary metadata) and dispose -of the builder. +Finalize the debug info and dispose of the builder. Finalization populates +the compile unit's enum/retained-type/global/imported-entity/macro arrays, +seals each subprogram's retained-nodes list, and resolves remaining cycles. +If no compile unit was registered with the builder, finalization is skipped. """ function dispose(builder::DIBuilder) - API.LLVMDIBuilderFinalize(builder) + if builder.has_compile_unit[] + API.LLVMDIBuilderFinalize(builder) + end mark_dispose(API.LLVMDisposeDIBuilder, builder) end @@ -62,9 +67,12 @@ Base.show(io::IO, builder::DIBuilder) = @printf(io, "DIBuilder(%p)", builder.ref Resolve any unresolved metadata nodes and mark all compile units finalized. Called automatically by [`dispose`](@ref); call explicitly only if the DI-enriched module must be consumed (e.g. for code emission) before the -builder is disposed of. +builder is disposed of. Skipped if no compile unit has been registered. """ -finalize!(builder::DIBuilder) = API.LLVMDIBuilderFinalize(builder) +function finalize!(builder::DIBuilder) + builder.has_compile_unit[] && API.LLVMDIBuilderFinalize(builder) + return +end ## location information @@ -1141,7 +1149,7 @@ function compile_unit!(builder::DIBuilder, lang, file::DIFile, producer::Abstrac sdk::AbstractString="") split_name_ptr = split_name === nothing ? C_NULL : split_name split_name_len = split_name === nothing ? Csize_t(0) : Csize_t(length(split_name)) - DICompileUnit(API.LLVMDIBuilderCreateCompileUnit( + cu = DICompileUnit(API.LLVMDIBuilderCreateCompileUnit( builder, lang, file, producer, Csize_t(length(producer)), optimized, @@ -1154,6 +1162,8 @@ function compile_unit!(builder::DIBuilder, lang, file::DIFile, producer::Abstrac debug_info_for_profiling, sysroot, Csize_t(length(sysroot)), sdk, Csize_t(length(sdk)))) + builder.has_compile_unit[] = true + return cu end From 93f25ed7e058f4599a6847fe75ba05bcf1eb6f85 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 17:55:37 +0200 Subject: [PATCH 16/27] Rename compile_unit!'s flags kwarg to cmdline. `flags` clashes with the `flags=LLVMDIFlags` kwarg used on every type factory on the same page; `cmdline` better conveys what the string is (command-line options embedded verbatim in the emitted debug info). --- src/debuginfo.jl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/debuginfo.jl b/src/debuginfo.jl index e9c82cb6..69c2b747 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -1124,7 +1124,7 @@ register(DICompileUnit, API.LLVMDICompileUnitMetadataKind) """ compile_unit!(builder::DIBuilder, lang, file::DIFile, producer::AbstractString; - optimized::Bool=true, flags::AbstractString="", + optimized::Bool=true, cmdline::AbstractString="", runtime_version::Integer=0, split_name::Union{AbstractString,Nothing}=nothing, emission_kind=API.LLVMDWARFEmissionFull, @@ -1134,11 +1134,12 @@ register(DICompileUnit, API.LLVMDICompileUnitMetadataKind) sysroot::AbstractString="", sdk::AbstractString="") -> DICompileUnit Create a new [`DICompileUnit`](@ref). `lang` is a `LLVMDWARFSourceLanguage` -value (e.g. `LLVM.API.LLVMDWARFSourceLanguageJulia`). +value (e.g. `LLVM.API.LLVMDWARFSourceLanguageJulia`). `cmdline` is a +command-line string embedded verbatim in the emitted debug info. """ function compile_unit!(builder::DIBuilder, lang, file::DIFile, producer::AbstractString; optimized::Bool=true, - flags::AbstractString="", + cmdline::AbstractString="", runtime_version::Integer=0, split_name::Union{AbstractString,Nothing}=nothing, emission_kind=API.LLVMDWARFEmissionFull, @@ -1153,7 +1154,7 @@ function compile_unit!(builder::DIBuilder, lang, file::DIFile, producer::Abstrac builder, lang, file, producer, Csize_t(length(producer)), optimized, - flags, Csize_t(length(flags)), + cmdline, Csize_t(length(cmdline)), Cuint(runtime_version), split_name_ptr, split_name_len, emission_kind, From b2665b02c14a6239da460799e32e6b597da05936 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 17:55:54 +0200 Subject: [PATCH 17/27] Version-gate object_pointer_type! for the new LLVM 20 signature. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LLVM 20 added a required `Implicit::LLVMBool` parameter to `LLVMDIBuilderCreateObjectPointerType` that controls whether the resulting type also carries `DI_FLAG_ARTIFICIAL`. Expose it as an `implicit::Bool=true` kwarg on LLVM 20+ (preserving the LLVM ≤ 19 implicit-artificial behavior) and keep the two-argument call on older LLVMs. --- src/debuginfo.jl | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/debuginfo.jl b/src/debuginfo.jl index 69c2b747..201ba619 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -555,6 +555,18 @@ Create a new artificial type (`DI_FLAG_ARTIFICIAL`), e.g. an implicit `this`. artificial_type!(builder::DIBuilder, type::DIType) = DIDerivedType(API.LLVMDIBuilderCreateArtificialType(builder, type)) +@static if version() >= v"20" +""" + object_pointer_type!(builder::DIBuilder, type::DIType; + implicit::Bool=true) -> DIDerivedType + +Create a new type identifying an object pointer (`DI_FLAG_OBJECT_POINTER`). +When `implicit` is `true` (the default, matching LLVM ≤ 19 behavior), also +sets `DI_FLAG_ARTIFICIAL`. +""" +object_pointer_type!(builder::DIBuilder, type::DIType; implicit::Bool=true) = + DIDerivedType(API.LLVMDIBuilderCreateObjectPointerType(builder, type, implicit)) +else """ object_pointer_type!(builder::DIBuilder, type::DIType) -> DIDerivedType @@ -562,6 +574,7 @@ Create a new type identifying an object pointer (`DI_FLAG_OBJECT_POINTER`). """ object_pointer_type!(builder::DIBuilder, type::DIType) = DIDerivedType(API.LLVMDIBuilderCreateObjectPointerType(builder, type)) +end # @static """ inheritance!(builder::DIBuilder, derived::DIType, base::DIType, From 78aa93bd0a2bb33537edbb005284e954efffc14d Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 17:56:12 +0200 Subject: [PATCH 18/27] Tighten dbg-record/intrinsic inserters to DILocalVariable. The C entry points (`LLVMDIBuilderInsertDeclare*` / `LLVMDIBuilderInsertDbgValue*`) unconditionally `unwrap` the var argument; passing a `DIGlobalVariable` is undefined behavior (asserts in debug LLVM, silent misbehavior in release). Reflect the actual constraint in the Julia signatures; the docstrings already said DILocalVariable. --- src/debuginfo.jl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/debuginfo.jl b/src/debuginfo.jl index 201ba619..db3747af 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -1495,7 +1495,7 @@ end Insert a new `#dbg_declare` record describing `storage` as the runtime location of `var`, immediately before `instr`. """ -declare_before!(builder::DIBuilder, storage::Value, var::DIVariable, +declare_before!(builder::DIBuilder, storage::Value, var::DILocalVariable, expr::DIExpression, debugloc::DILocation, instr::Instruction) = DbgRecord(API.LLVMDIBuilderInsertDeclareRecordBefore( builder, storage, var, expr, debugloc, instr)) @@ -1507,7 +1507,7 @@ declare_before!(builder::DIBuilder, storage::Value, var::DIVariable, Insert a new `#dbg_declare` record at the end of `block`. """ -declare_at_end!(builder::DIBuilder, storage::Value, var::DIVariable, +declare_at_end!(builder::DIBuilder, storage::Value, var::DILocalVariable, expr::DIExpression, debugloc::DILocation, block::BasicBlock) = DbgRecord(API.LLVMDIBuilderInsertDeclareRecordAtEnd( builder, storage, var, expr, debugloc, block)) @@ -1520,7 +1520,7 @@ declare_at_end!(builder::DIBuilder, storage::Value, var::DIVariable, Insert a new `#dbg_value` record describing `val` as the value of `var`, immediately before `instr`. """ -value_before!(builder::DIBuilder, val::Value, var::DIVariable, +value_before!(builder::DIBuilder, val::Value, var::DILocalVariable, expr::DIExpression, debugloc::DILocation, instr::Instruction) = DbgRecord(API.LLVMDIBuilderInsertDbgValueRecordBefore( builder, val, var, expr, debugloc, instr)) @@ -1532,7 +1532,7 @@ value_before!(builder::DIBuilder, val::Value, var::DIVariable, Insert a new `#dbg_value` record at the end of `block`. """ -value_at_end!(builder::DIBuilder, val::Value, var::DIVariable, +value_at_end!(builder::DIBuilder, val::Value, var::DILocalVariable, expr::DIExpression, debugloc::DILocation, block::BasicBlock) = DbgRecord(API.LLVMDIBuilderInsertDbgValueRecordAtEnd( builder, val, var, expr, debugloc, block)) @@ -1546,7 +1546,7 @@ else # LLVM < 19: legacy intrinsic-based insertion Insert a new `llvm.dbg.declare` intrinsic call immediately before `instr`. """ -declare_before!(builder::DIBuilder, storage::Value, var::DIVariable, +declare_before!(builder::DIBuilder, storage::Value, var::DILocalVariable, expr::DIExpression, debugloc::DILocation, instr::Instruction) = Instruction(API.LLVMDIBuilderInsertDeclareBefore( builder, storage, var, expr, debugloc, instr)) @@ -1558,7 +1558,7 @@ declare_before!(builder::DIBuilder, storage::Value, var::DIVariable, Insert a new `llvm.dbg.declare` intrinsic call at the end of `block`. """ -declare_at_end!(builder::DIBuilder, storage::Value, var::DIVariable, +declare_at_end!(builder::DIBuilder, storage::Value, var::DILocalVariable, expr::DIExpression, debugloc::DILocation, block::BasicBlock) = Instruction(API.LLVMDIBuilderInsertDeclareAtEnd( builder, storage, var, expr, debugloc, block)) @@ -1570,7 +1570,7 @@ declare_at_end!(builder::DIBuilder, storage::Value, var::DIVariable, Insert a new `llvm.dbg.value` intrinsic call immediately before `instr`. """ -value_before!(builder::DIBuilder, val::Value, var::DIVariable, +value_before!(builder::DIBuilder, val::Value, var::DILocalVariable, expr::DIExpression, debugloc::DILocation, instr::Instruction) = Instruction(API.LLVMDIBuilderInsertDbgValueBefore( builder, val, var, expr, debugloc, instr)) @@ -1582,7 +1582,7 @@ value_before!(builder::DIBuilder, val::Value, var::DIVariable, Insert a new `llvm.dbg.value` intrinsic call at the end of `block`. """ -value_at_end!(builder::DIBuilder, val::Value, var::DIVariable, +value_at_end!(builder::DIBuilder, val::Value, var::DILocalVariable, expr::DIExpression, debugloc::DILocation, block::BasicBlock) = Instruction(API.LLVMDIBuilderInsertDbgValueAtEnd( builder, val, var, expr, debugloc, block)) From b0e800f4b13af775ed5cdb6d47c3a90c9b64a8fd Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 17:56:49 +0200 Subject: [PATCH 19/27] Note the untracked-SP no-op in finalize_subprogram! docstring. --- src/debuginfo.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/debuginfo.jl b/src/debuginfo.jl index db3747af..33b786fb 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -1109,7 +1109,8 @@ end finalize_subprogram!(builder::DIBuilder, sp::DISubProgram) Finalize a single subprogram early, sealing its retained-nodes list. After -this, no more local variables can be added to `sp`. +this, no more local variables can be added to `sp`. A no-op if `sp` was not +tracked by `builder` (e.g. created elsewhere or already finalized). Calling this is never required for correctness — [`dispose`](@ref) / [`finalize!`](@ref) finalize every tracked subprogram automatically. Use it From caa642fb996a4f69b43c1944fc67388bb9c91117 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 17:57:28 +0200 Subject: [PATCH 20/27] Split subroutine_type! return and parameter types. Previously `subroutine_type!` took a single `Vector{<:Metadata}` whose 0th entry doubled as the return type -- a convention inherited straight from the C API and easy to forget. Accept the return type as a separate positional argument (with `nothing` for `void`) and let the parameter vector mean what it says. --- src/debuginfo.jl | 19 ++++++++++++++----- test/debuginfo.jl | 8 +++++--- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/debuginfo.jl b/src/debuginfo.jl index 33b786fb..2f75fd27 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -866,16 +866,25 @@ end """ subroutine_type!(builder::DIBuilder, file::DIFile, - parameter_types::Vector{<:Metadata}; + return_type::Union{DIType,Nothing}, + parameter_types::Vector{<:Metadata}=Metadata[]; flags=API.LLVMDIFlagZero) -> DISubroutineType -Create a new subroutine type. The first entry of `parameter_types` is the -return type; the rest are the parameter types. +Create a new subroutine type with the given return and parameter types. Pass +`nothing` for `return_type` to describe a `void`-returning subroutine. """ function subroutine_type!(builder::DIBuilder, file::DIFile, - parameter_types::Vector{<:Metadata}; + return_type::Union{DIType,Nothing}, + parameter_types::Vector{<:Metadata}=Metadata[]; flags=API.LLVMDIFlagZero) - params = convert(Vector{Metadata}, parameter_types) + # LLVM packs the return type as the 0th element of the parameter-types array, + # with a null entry standing for `void`. + params = API.LLVMMetadataRef[ + return_type === nothing ? C_NULL : + Base.unsafe_convert(API.LLVMMetadataRef, return_type)] + for p in parameter_types + push!(params, Base.unsafe_convert(API.LLVMMetadataRef, p)) + end DISubroutineType(API.LLVMDIBuilderCreateSubroutineType( builder, file, params, Cuint(length(params)), flags)) end diff --git a/test/debuginfo.jl b/test/debuginfo.jl index 5ee5f61e..14391fda 100644 --- a/test/debuginfo.jl +++ b/test/debuginfo.jl @@ -169,8 +169,10 @@ end @test LLVM.inheritance!(dib, ct, base, 0) isa LLVM.DIDerivedType # subroutine - sroute = LLVM.subroutine_type!(dib, file, LLVM.Metadata[i64, i64]) + sroute = LLVM.subroutine_type!(dib, file, i64, LLVM.Metadata[i64]) @test sroute isa LLVM.DISubroutineType + # void return + @test LLVM.subroutine_type!(dib, file, nothing) isa LLVM.DISubroutineType # forward decl / replaceable composite @test LLVM.forward_decl!(dib, DW_TAG_structure_type, "Fwd", cu, file, 1) isa LLVM.DICompositeType @@ -189,7 +191,7 @@ end file, "LLVM.jl Tests") i64 = LLVM.basic_type!(dib, "Int64", 64, DW_ATE_signed) - stype = LLVM.subroutine_type!(dib, file, LLVM.Metadata[i64, i64, i64]) + stype = LLVM.subroutine_type!(dib, file, i64, LLVM.Metadata[i64, i64]) # subprogram sp = LLVM.subprogram!(dib, file, "add", file, 1, stype) @@ -241,7 +243,7 @@ end cu = LLVM.compile_unit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, file, "LLVM.jl Tests") i64 = LLVM.basic_type!(dib, "Int64", 64, DW_ATE_signed) - stype = LLVM.subroutine_type!(dib, file, LLVM.Metadata[i64, i64, i64]) + stype = LLVM.subroutine_type!(dib, file, i64, LLVM.Metadata[i64, i64]) sp = LLVM.subprogram!(dib, file, "add", file, 1, stype) ft = LLVM.FunctionType(LLVM.Int64Type(), [LLVM.Int64Type(), LLVM.Int64Type()]) From eaa69a4a846f7b2cf4cb5005fa203a0645eb9758 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 17:57:56 +0200 Subject: [PATCH 21/27] Require a Constant value in static_member_type!. `LLVMDIBuilderCreateStaticMemberType` unconditionally `cast`s its `ConstantVal` operand and aborts on null (unlike the C++ entry point, which calls `getConstantOrNull`). Make `constant_val` a required positional argument so the foot-gun isn't reachable from the default- argument path. --- src/debuginfo.jl | 14 ++++++++------ test/debuginfo.jl | 3 ++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/debuginfo.jl b/src/debuginfo.jl index 2f75fd27..c8323165 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -630,22 +630,24 @@ end """ static_member_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, - file::DIFile, line::Integer, type::DIType; + file::DIFile, line::Integer, type::DIType, + constant_val::Constant; flags=API.LLVMDIFlagZero, - constant_val=nothing, align_in_bits::Integer=0) -> DIDerivedType -Create a new static member of a composite type. +Create a new static member of a composite type. `constant_val` is required: +the underlying C entry point unconditionally `cast`s it and +crashes on null. """ function static_member_type!(builder::DIBuilder, scope::DIScope, name::AbstractString, - file::DIFile, line::Integer, type::DIType; + file::DIFile, line::Integer, type::DIType, + constant_val::Constant; flags=API.LLVMDIFlagZero, - constant_val=nothing, align_in_bits::Integer=0) DIDerivedType(API.LLVMDIBuilderCreateStaticMemberType( builder, scope, name, Csize_t(length(name)), file, Cuint(line), type, flags, - something(constant_val, C_NULL), UInt32(align_in_bits))) + constant_val, UInt32(align_in_bits))) end """ diff --git a/test/debuginfo.jl b/test/debuginfo.jl index 14391fda..fffc585d 100644 --- a/test/debuginfo.jl +++ b/test/debuginfo.jl @@ -162,7 +162,8 @@ end # bitfield + static + member-pointer + inheritance @test LLVM.bitfield_member_type!(dib, cu, "b", file, 1, 3, 0, 0, i64) isa LLVM.DIDerivedType - @test LLVM.static_member_type!(dib, cu, "s", file, 1, i64) isa LLVM.DIDerivedType + @test LLVM.static_member_type!(dib, cu, "s", file, 1, i64, + ConstantInt(LLVM.Int64Type(), 42)) isa LLVM.DIDerivedType @test LLVM.member_pointer_type!(dib, i64, st, 64) isa LLVM.DIDerivedType base = LLVM.class_type!(dib, cu, "Base", file, 1, 64, 64, 0, LLVM.Metadata[]) From eea18ba5a481e6d7ae8611229fb144f759487588 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 17:58:25 +0200 Subject: [PATCH 22/27] Require a scope when constructing a DILocation. Passing `nothing` (the old default) was silently forwarded as C_NULL, which LLVM segfaults on -- acknowledged by an `XXX:` comment in the previous implementation. Make the scope required and throw an `UndefRefError` from the explicit-`nothing` overload so the failure mode is a Julia exception instead of a native crash. The test that relied on the null-scope path is updated to attach a real subprogram scope via a small DIBuilder. --- src/debuginfo.jl | 19 ++++++++++++------- test/instructions.jl | 17 ++++++++++++----- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/debuginfo.jl b/src/debuginfo.jl index c8323165..924c5be6 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -90,18 +90,23 @@ end register(DILocation, API.LLVMDILocationMetadataKind) """ - DILocation([line::Int], [col::Int], [scope::Metadata], [inlined_at::Metadata]) + DILocation(line::Integer, col::Integer, scope::DIScope, + [inlined_at::DILocation]) -> DILocation -Creates a new DebugLocation that describes a source location. +Creates a new DebugLocation that describes a source location. A scope is +required: LLVM segfaults on a null scope. """ -function DILocation(line=0, col=0, scope=nothing, inlined_at=nothing) - # XXX: are null scopes valid? they crash LLVM: - # DILocation(Context(), 1, 2).scope - DILocation(API.LLVMDIBuilderCreateDebugLocation(context(), line, col, - something(scope, C_NULL), +function DILocation(line::Integer, col::Integer, scope::Metadata, + inlined_at::Union{DILocation,Nothing}=nothing) + # `scope` is typed as `Metadata` rather than `DIScope` because `DIScope` + # isn't defined yet at this point in the file. + DILocation(API.LLVMDIBuilderCreateDebugLocation(context(), line, col, scope, something(inlined_at, C_NULL))) end +DILocation(line::Integer, col::Integer, scope::Nothing, inlined_at=nothing) = + throw(UndefRefError()) + """ line(location::DILocation) diff --git a/test/instructions.jl b/test/instructions.jl index 3ce35f70..636b653a 100644 --- a/test/instructions.jl +++ b/test/instructions.jl @@ -15,11 +15,18 @@ @test position(builder) == entrybb @test debuglocation(builder) === nothing - loc = DILocation(1, 1) - debuglocation!(builder, loc) - @test debuglocation(builder) == loc - debuglocation!(builder) - @test debuglocation(builder) === nothing + LLVM.DIBuilder(mod) do dib + difile = LLVM.file!(dib, "test.jl", "/tmp") + LLVM.compile_unit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, + difile, "LLVM.jl Tests") + sp = LLVM.subprogram!(dib, difile, "SomeFunction", difile, 1, + LLVM.subroutine_type!(dib, difile, nothing)) + loc = DILocation(1, 1, sp) + debuglocation!(builder, loc) + @test debuglocation(builder) == loc + debuglocation!(builder) + @test debuglocation(builder) === nothing + end retinst1 = ret!(builder) @check_ir retinst1 "ret void" From e48ff95e783223b921e9ed7119e9659b2c7b8c61 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 17:59:35 +0200 Subject: [PATCH 23/27] Harden DIBuilder tests for assertion-enabled LLVM. Several testsets exercised patterns that LLVM silently tolerates in release builds but rejects under assertions: - `lexical_block!` and `label!` were called with a `DICompileUnit` scope. LLVM's `getNonCompileUnitScope` drops CU scopes to null on these entry points, then asserts on the null. Nest both under a real `DISubprogram` scope instead. - `replaceable_composite_type!` was created and never RAUW'd in the type-constructors sweep; finalize then asserts on the unresolved temporary. It remains exercised in the mutation testset, where it is properly replaced. Also add an `LLVM.verify(mod) === nothing` check after the end-to-end build in the instruction-level-insertion testset -- a stronger structural check than the existing `isa`/`occursin` assertions. --- test/debuginfo.jl | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/test/debuginfo.jl b/test/debuginfo.jl index fffc585d..9e60fc7c 100644 --- a/test/debuginfo.jl +++ b/test/debuginfo.jl @@ -58,13 +58,20 @@ end end @testset "DIBuilder: lexical blocks" begin + DW_ATE_signed = 0x05 @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin DIBuilder(mod) do dib file = LLVM.file!(dib, "test.jl", "/tmp") cu = LLVM.compile_unit!(dib, LLVM.API.LLVMDWARFSourceLanguageJulia, file, "LLVM.jl Tests") - lb = LLVM.lexical_block!(dib, cu, file, 3, 5) + # Lexical blocks must be nested in a local scope (a subprogram or + # another lexical block) -- LLVM drops a DICompileUnit scope to null. + i64 = LLVM.basic_type!(dib, "Int64", 64, DW_ATE_signed) + stype = LLVM.subroutine_type!(dib, file, i64) + sp = LLVM.subprogram!(dib, file, "f", file, 1, stype) + + lb = LLVM.lexical_block!(dib, sp, file, 3, 5) @test lb isa DILexicalBlock lbf = LLVM.lexical_block_file!(dib, lb, file, 0) @@ -77,9 +84,12 @@ end @test LLVM.scope(loc) == lb # inlined_at chain - outer = DILocation(5, 1, cu) + outer = DILocation(5, 1, sp) inner = DILocation(10, 20, lb, outer) @test LLVM.inlined_at(inner) == outer + + # DILocation requires a scope + @test_throws UndefRefError DILocation(1, 2, nothing) end end end @@ -175,9 +185,10 @@ end # void return @test LLVM.subroutine_type!(dib, file, nothing) isa LLVM.DISubroutineType - # forward decl / replaceable composite + # forward decl (a permanent declaration); the temporary + # `replaceable_composite_type!` is exercised in the mutation testset + # because it must be RAUW'd before finalize. @test LLVM.forward_decl!(dib, DW_TAG_structure_type, "Fwd", cu, file, 1) isa LLVM.DICompositeType - @test LLVM.replaceable_composite_type!(dib, DW_TAG_structure_type, "Rep", cu, file, 1) isa LLVM.DICompositeType end end end @@ -298,10 +309,15 @@ end else @test occursin("llvm.dbg.declare", ir) end + + # the resulting module must be structurally valid — `verify` checks + # that subprograms, scopes, locations and dbg records are well-formed + @test LLVM.verify(mod) === nothing end end @testset "DIBuilder: imported entities and macros" begin + DW_ATE_signed = 0x05 @dispose ctx=Context() mod=LLVM.Module("SomeModule") begin DIBuilder(mod) do dib file = LLVM.file!(dib, "test.jl", "/tmp") @@ -333,7 +349,11 @@ end @test m isa LLVM.DIMacro if LLVM.version() >= v"20" - lbl = LLVM.label!(dib, cu, "my_label", file, 5) + # labels live inside a subprogram, not at compile-unit scope + i64 = LLVM.basic_type!(dib, "Int64", 64, DW_ATE_signed) + stype = LLVM.subroutine_type!(dib, file, i64) + sp = LLVM.subprogram!(dib, file, "f", file, 1, stype) + lbl = LLVM.label!(dib, sp, "my_label", file, 5) @test lbl isa LLVM.DILabel end end From 42babb4a3be92969700a144a4fb99abb1ffbd006 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 19:08:41 +0200 Subject: [PATCH 24/27] Fix for LLVM <17. --- src/debuginfo.jl | 4 +++- test/debuginfo.jl | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/debuginfo.jl b/src/debuginfo.jl index 924c5be6..425201a1 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -410,12 +410,14 @@ Get the alignment in bits of the given type. """ align(typ::DIType) = Int(API.LLVMDITypeGetAlignInBits(typ)) +@static if version() >= v"17" """ tag(node::DINode) -Get the DWARF tag of the given node, or `0` if none. +Get the DWARF tag of the given node, or `0` if none. Requires LLVM 17+. """ tag(node::DINode) = Int(API.LLVMGetDINodeTag(node)) +end # basic types diff --git a/test/debuginfo.jl b/test/debuginfo.jl index 9e60fc7c..57a64d4c 100644 --- a/test/debuginfo.jl +++ b/test/debuginfo.jl @@ -111,7 +111,9 @@ end @test i64 isa LLVM.DIBasicType @test LLVM.name(i64) == "Int64" @test LLVM.align(i64) == 0 - @test LLVM.tag(i64) != 0 + if LLVM.version() >= v"17" + @test LLVM.tag(i64) != 0 + end @test LLVM.unspecified_type!(dib, "unspec") isa LLVM.DIBasicType @test LLVM.nullptr_type!(dib) isa LLVM.DIBasicType From bf7ecd6754034f26547ef4de37f2747d049eb564 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 19:37:07 +0200 Subject: [PATCH 25/27] Preserve input kind in artificial/object-pointer type clones. The underlying C entry points clone the input DIType with an extra flag set, so the return kind is always whatever kind was passed in, not specifically DIDerivedType. Wrap through Metadata(...)::DIType so a DIBasicType stays a DIBasicType. --- src/debuginfo.jl | 17 ++++++++++------- test/debuginfo.jl | 4 ++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/debuginfo.jl b/src/debuginfo.jl index 425201a1..65b7ebde 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -555,32 +555,35 @@ rvalue_reference_type!(builder::DIBuilder, type::DIType) = reference_type!(builder, _DW_TAG_rvalue_reference_type, type) """ - artificial_type!(builder::DIBuilder, type::DIType) -> DIDerivedType + artificial_type!(builder::DIBuilder, type::DIType) -> DIType Create a new artificial type (`DI_FLAG_ARTIFICIAL`), e.g. an implicit `this`. +The concrete subtype matches the input (e.g. a `DIBasicType` stays a +`DIBasicType`). """ artificial_type!(builder::DIBuilder, type::DIType) = - DIDerivedType(API.LLVMDIBuilderCreateArtificialType(builder, type)) + Metadata(API.LLVMDIBuilderCreateArtificialType(builder, type))::DIType @static if version() >= v"20" """ object_pointer_type!(builder::DIBuilder, type::DIType; - implicit::Bool=true) -> DIDerivedType + implicit::Bool=true) -> DIType Create a new type identifying an object pointer (`DI_FLAG_OBJECT_POINTER`). When `implicit` is `true` (the default, matching LLVM ≤ 19 behavior), also -sets `DI_FLAG_ARTIFICIAL`. +sets `DI_FLAG_ARTIFICIAL`. The concrete subtype matches the input. """ object_pointer_type!(builder::DIBuilder, type::DIType; implicit::Bool=true) = - DIDerivedType(API.LLVMDIBuilderCreateObjectPointerType(builder, type, implicit)) + Metadata(API.LLVMDIBuilderCreateObjectPointerType(builder, type, implicit))::DIType else """ - object_pointer_type!(builder::DIBuilder, type::DIType) -> DIDerivedType + object_pointer_type!(builder::DIBuilder, type::DIType) -> DIType Create a new type identifying an object pointer (`DI_FLAG_OBJECT_POINTER`). +The concrete subtype matches the input. """ object_pointer_type!(builder::DIBuilder, type::DIType) = - DIDerivedType(API.LLVMDIBuilderCreateObjectPointerType(builder, type)) + Metadata(API.LLVMDIBuilderCreateObjectPointerType(builder, type))::DIType end # @static """ diff --git a/test/debuginfo.jl b/test/debuginfo.jl index 57a64d4c..e7d1a60b 100644 --- a/test/debuginfo.jl +++ b/test/debuginfo.jl @@ -129,10 +129,10 @@ end @test cq isa LLVM.DIDerivedType at2 = LLVM.artificial_type!(dib, i64) - @test at2 isa LLVM.DIDerivedType + @test at2 isa LLVM.DIBasicType op = LLVM.object_pointer_type!(dib, i64) - @test op isa LLVM.DIDerivedType + @test op isa LLVM.DIBasicType ref = LLVM.reference_type!(dib, DW_TAG_reference_type, i64) @test ref isa LLVM.DIDerivedType From a64931aa1a588157529e664bc2bd21449de8f78b Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 19:37:15 +0200 Subject: [PATCH 26/27] Add DIObjCProperty and return it from objc_property!. LLVMDIBuilderCreateObjCProperty returns a DIObjCProperty (metadata kind 27), not a DIDerivedType. The old wrapper would trip typecheck and, without it, emit an unidentified metadata kind on the first use. --- src/debuginfo.jl | 15 +++++++++++++-- test/debuginfo.jl | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/debuginfo.jl b/src/debuginfo.jl index 65b7ebde..67060040 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -941,8 +941,19 @@ end # ObjC +export DIObjCProperty @public objc_ivar!, objc_property! +""" + DIObjCProperty + +An Objective-C `@property` descriptor. +""" +@checked struct DIObjCProperty <: DINode + ref::API.LLVMMetadataRef +end +register(DIObjCProperty, API.LLVMDIObjCPropertyMetadataKind) + """ objc_ivar!(builder::DIBuilder, name::AbstractString, file::DIFile, line::Integer, size_in_bits::Integer, align_in_bits::Integer, @@ -965,14 +976,14 @@ end """ objc_property!(builder::DIBuilder, name::AbstractString, file::DIFile, line::Integer, getter::AbstractString, setter::AbstractString, - attributes::Integer, type::DIType) -> DIDerivedType + attributes::Integer, type::DIType) -> DIObjCProperty Create a new Objective-C `@property` descriptor. """ function objc_property!(builder::DIBuilder, name::AbstractString, file::DIFile, line::Integer, getter::AbstractString, setter::AbstractString, attributes::Integer, type::DIType) - DIDerivedType(API.LLVMDIBuilderCreateObjCProperty( + DIObjCProperty(API.LLVMDIBuilderCreateObjCProperty( builder, name, Csize_t(length(name)), file, Cuint(line), getter, Csize_t(length(getter)), diff --git a/test/debuginfo.jl b/test/debuginfo.jl index e7d1a60b..ecaf9125 100644 --- a/test/debuginfo.jl +++ b/test/debuginfo.jl @@ -424,7 +424,7 @@ end # ObjC (not widely used from Julia but round-tripped) prop = LLVM.objc_property!(dib, "count", file, 1, "getCount", "setCount:", 0, i64) - @test prop isa LLVM.DIDerivedType + @test prop isa LLVM.DIObjCProperty ivar = LLVM.objc_ivar!(dib, "_count", file, 1, 64, 64, 0, i64, prop) @test ivar isa LLVM.DIDerivedType end From 7fc12248af2b9ab155fb2e4a4929aefa1841ba85 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Apr 2026 21:37:33 +0200 Subject: [PATCH 27/27] Lift docstrings out of @static version gates. Docstrings placed inside @static if branches aren't picked up by Documenter's checkdocs: the docstring ends up attached to a block expression rather than the function binding, so `@docs LLVM.foo!` reports "no docs found". Split every version-gated factory into an outer, unconditional docstring on the bare name and version-selected implementations below, and document the concrete DI*Type subtypes (which have no standalone docstrings because they're generated in an @eval loop) so cross-references to them resolve too. --- docs/src/lib/metadata.md | 5 ++ src/debuginfo.jl | 151 ++++++++++++++++++++++----------------- 2 files changed, 89 insertions(+), 67 deletions(-) diff --git a/docs/src/lib/metadata.md b/docs/src/lib/metadata.md index 0c89991c..6fcb1e8d 100644 --- a/docs/src/lib/metadata.md +++ b/docs/src/lib/metadata.md @@ -113,6 +113,10 @@ LLVM.namespace! ```@docs DIType +LLVM.DIBasicType +LLVM.DIDerivedType +LLVM.DICompositeType +LLVM.DISubroutineType name(::DIType) Base.sizeof(::DIType) offset(::DIType) @@ -182,6 +186,7 @@ LLVM.get_or_create_type_array! Objective-C: ```@docs +DIObjCProperty LLVM.objc_ivar! LLVM.objc_property! ``` diff --git a/src/debuginfo.jl b/src/debuginfo.jl index 67060040..5ca60d89 100644 --- a/src/debuginfo.jl +++ b/src/debuginfo.jl @@ -343,6 +343,41 @@ for typ in (:Basic, :Derived, :Composite, :Subroutine) end end +""" + DIBasicType <: DIType + +A primitive type (integer, floating-point, boolean, ...), built with +[`basic_type!`](@ref). +""" +DIBasicType + +""" + DIDerivedType <: DIType + +A type derived from another type by adding qualifiers, reference/pointer +indirection, a typedef name, or by describing a member/field. Built with +[`pointer_type!`](@ref), [`typedef_type!`](@ref), [`member_type!`](@ref), and +the other qualifier/member factories. +""" +DIDerivedType + +""" + DICompositeType <: DIType + +An aggregate type (struct, union, class, array, vector, enumeration, ...). +Built with [`struct_type!`](@ref), [`union_type!`](@ref), +[`array_type!`](@ref), etc. +""" +DICompositeType + +""" + DISubroutineType <: DIType + +A function/subroutine type, listing the return and parameter types. Built +with [`subroutine_type!`](@ref). +""" +DISubroutineType + """ DIEnumerator @@ -564,24 +599,21 @@ The concrete subtype matches the input (e.g. a `DIBasicType` stays a artificial_type!(builder::DIBuilder, type::DIType) = Metadata(API.LLVMDIBuilderCreateArtificialType(builder, type))::DIType -@static if version() >= v"20" """ object_pointer_type!(builder::DIBuilder, type::DIType; implicit::Bool=true) -> DIType Create a new type identifying an object pointer (`DI_FLAG_OBJECT_POINTER`). -When `implicit` is `true` (the default, matching LLVM ≤ 19 behavior), also -sets `DI_FLAG_ARTIFICIAL`. The concrete subtype matches the input. +The concrete subtype matches the input. On LLVM 20+ an `implicit::Bool` keyword +is accepted: when `true` (the default, matching LLVM ≤ 19 behavior) the clone +also sets `DI_FLAG_ARTIFICIAL`. """ +object_pointer_type! + +@static if version() >= v"20" object_pointer_type!(builder::DIBuilder, type::DIType; implicit::Bool=true) = Metadata(API.LLVMDIBuilderCreateObjectPointerType(builder, type, implicit))::DIType else -""" - object_pointer_type!(builder::DIBuilder, type::DIType) -> DIType - -Create a new type identifying an object pointer (`DI_FLAG_OBJECT_POINTER`). -The concrete subtype matches the input. -""" object_pointer_type!(builder::DIBuilder, type::DIType) = Metadata(API.LLVMDIBuilderCreateObjectPointerType(builder, type))::DIType end # @static @@ -1496,6 +1528,49 @@ end @public declare_before!, declare_at_end!, value_before!, value_at_end! +""" + declare_before!(builder::DIBuilder, storage::Value, var::DILocalVariable, + expr::DIExpression, debugloc::DILocation, + instr::Instruction) + +Insert a new dbg-declare describing `storage` as the runtime location of `var`, +immediately before `instr`. Returns a `DbgRecord` on LLVM ≥ 19, or the +legacy `llvm.dbg.declare` call [`Instruction`](@ref) on LLVM < 19. +""" +declare_before! + +""" + declare_at_end!(builder::DIBuilder, storage::Value, var::DILocalVariable, + expr::DIExpression, debugloc::DILocation, + block::BasicBlock) + +Insert a new dbg-declare at the end of `block`. Returns a `DbgRecord` +on LLVM ≥ 19, or the legacy `llvm.dbg.declare` call [`Instruction`](@ref) on +LLVM < 19. +""" +declare_at_end! + +""" + value_before!(builder::DIBuilder, val::Value, var::DILocalVariable, + expr::DIExpression, debugloc::DILocation, + instr::Instruction) + +Insert a new dbg-value describing `val` as the value of `var`, immediately +before `instr`. Returns a `DbgRecord` on LLVM ≥ 19, or the legacy +`llvm.dbg.value` call [`Instruction`](@ref) on LLVM < 19. +""" +value_before! + +""" + value_at_end!(builder::DIBuilder, val::Value, var::DILocalVariable, + expr::DIExpression, debugloc::DILocation, + block::BasicBlock) + +Insert a new dbg-value at the end of `block`. Returns a `DbgRecord` on +LLVM ≥ 19, or the legacy `llvm.dbg.value` call [`Instruction`](@ref) on LLVM < 19. +""" +value_at_end! + @static if version() >= v"19" export DbgRecord @@ -1520,51 +1595,21 @@ function Base.show(io::IO, record::DbgRecord) API.LLVMDisposeMessage(str_ptr) end -""" - declare_before!(builder::DIBuilder, storage::Value, var::DILocalVariable, - expr::DIExpression, debugloc::DILocation, - instr::Instruction) -> DbgRecord - -Insert a new `#dbg_declare` record describing `storage` as the runtime -location of `var`, immediately before `instr`. -""" declare_before!(builder::DIBuilder, storage::Value, var::DILocalVariable, expr::DIExpression, debugloc::DILocation, instr::Instruction) = DbgRecord(API.LLVMDIBuilderInsertDeclareRecordBefore( builder, storage, var, expr, debugloc, instr)) -""" - declare_at_end!(builder::DIBuilder, storage::Value, var::DILocalVariable, - expr::DIExpression, debugloc::DILocation, - block::BasicBlock) -> DbgRecord - -Insert a new `#dbg_declare` record at the end of `block`. -""" declare_at_end!(builder::DIBuilder, storage::Value, var::DILocalVariable, expr::DIExpression, debugloc::DILocation, block::BasicBlock) = DbgRecord(API.LLVMDIBuilderInsertDeclareRecordAtEnd( builder, storage, var, expr, debugloc, block)) -""" - value_before!(builder::DIBuilder, val::Value, var::DILocalVariable, - expr::DIExpression, debugloc::DILocation, - instr::Instruction) -> DbgRecord - -Insert a new `#dbg_value` record describing `val` as the value of `var`, -immediately before `instr`. -""" value_before!(builder::DIBuilder, val::Value, var::DILocalVariable, expr::DIExpression, debugloc::DILocation, instr::Instruction) = DbgRecord(API.LLVMDIBuilderInsertDbgValueRecordBefore( builder, val, var, expr, debugloc, instr)) -""" - value_at_end!(builder::DIBuilder, val::Value, var::DILocalVariable, - expr::DIExpression, debugloc::DILocation, - block::BasicBlock) -> DbgRecord - -Insert a new `#dbg_value` record at the end of `block`. -""" value_at_end!(builder::DIBuilder, val::Value, var::DILocalVariable, expr::DIExpression, debugloc::DILocation, block::BasicBlock) = DbgRecord(API.LLVMDIBuilderInsertDbgValueRecordAtEnd( @@ -1572,49 +1617,21 @@ value_at_end!(builder::DIBuilder, val::Value, var::DILocalVariable, else # LLVM < 19: legacy intrinsic-based insertion -""" - declare_before!(builder::DIBuilder, storage::Value, var::DILocalVariable, - expr::DIExpression, debugloc::DILocation, - instr::Instruction) -> Instruction - -Insert a new `llvm.dbg.declare` intrinsic call immediately before `instr`. -""" declare_before!(builder::DIBuilder, storage::Value, var::DILocalVariable, expr::DIExpression, debugloc::DILocation, instr::Instruction) = Instruction(API.LLVMDIBuilderInsertDeclareBefore( builder, storage, var, expr, debugloc, instr)) -""" - declare_at_end!(builder::DIBuilder, storage::Value, var::DILocalVariable, - expr::DIExpression, debugloc::DILocation, - block::BasicBlock) -> Instruction - -Insert a new `llvm.dbg.declare` intrinsic call at the end of `block`. -""" declare_at_end!(builder::DIBuilder, storage::Value, var::DILocalVariable, expr::DIExpression, debugloc::DILocation, block::BasicBlock) = Instruction(API.LLVMDIBuilderInsertDeclareAtEnd( builder, storage, var, expr, debugloc, block)) -""" - value_before!(builder::DIBuilder, val::Value, var::DILocalVariable, - expr::DIExpression, debugloc::DILocation, - instr::Instruction) -> Instruction - -Insert a new `llvm.dbg.value` intrinsic call immediately before `instr`. -""" value_before!(builder::DIBuilder, val::Value, var::DILocalVariable, expr::DIExpression, debugloc::DILocation, instr::Instruction) = Instruction(API.LLVMDIBuilderInsertDbgValueBefore( builder, val, var, expr, debugloc, instr)) -""" - value_at_end!(builder::DIBuilder, val::Value, var::DILocalVariable, - expr::DIExpression, debugloc::DILocation, - block::BasicBlock) -> Instruction - -Insert a new `llvm.dbg.value` intrinsic call at the end of `block`. -""" value_at_end!(builder::DIBuilder, val::Value, var::DILocalVariable, expr::DIExpression, debugloc::DILocation, block::BasicBlock) = Instruction(API.LLVMDIBuilderInsertDbgValueAtEnd(