[llvm] [llvm-c] Add LLVMParseIRInContext2 (PR #174085)
Tamir Duberstein via llvm-commits
llvm-commits at lists.llvm.org
Wed Dec 31 03:42:35 PST 2025
https://github.com/tamird created https://github.com/llvm/llvm-project/pull/174085
This new function is the same as LLVMParseIRInContext except it doesn't
take ownership of the memory buffer. This fixes a wart that has been in
place since 5ebb7b311223bcd21d2b3d25413d1edacefcc63d changed the
underlying internal API to avoid taking ownership.
Reduce nesting in the implementation of LLVMParseIRInContext (now
LLVMParseIRInContext2) as well.
Update examples, OCaml bindings, and tests including plugging some
pre-existing memory leaks.
>From 1981d40e192b8f872c868885ea10bd48308b92a5 Mon Sep 17 00:00:00 2001
From: Tamir Duberstein <tamird at gmail.com>
Date: Wed, 31 Dec 2025 05:56:45 -0500
Subject: [PATCH] [llvm-c] Add LLVMParseIRInContext2
This new function is the same as LLVMParseIRInContext except it doesn't
take ownership of the memory buffer. This fixes a wart that has been in
place since 5ebb7b311223bcd21d2b3d25413d1edacefcc63d changed the
underlying internal API to avoid taking ownership.
Reduce nesting in the implementation of LLVMParseIRInContext (now
LLVMParseIRInContext2) as well.
Update examples, OCaml bindings, and tests including plugging some
pre-existing memory leaks.
---
.../ocaml/bitreader/llvm_bitreader.mli | 15 +++++++--
llvm/bindings/ocaml/irreader/irreader_ocaml.c | 5 ++-
.../bindings/ocaml/irreader/llvm_irreader.mli | 7 ++++-
.../OrcV2CBindingsLazy/OrcV2CBindingsLazy.c | 17 +++++-----
.../OrcV2CBindingsVeryLazy.c | 12 ++++---
llvm/include/llvm-c/IRReader.h | 16 ++++++++++
llvm/lib/IRReader/IRReader.cpp | 30 ++++++++++--------
llvm/test/Bindings/OCaml/bitreader.ml | 14 ++++-----
llvm/test/Bindings/OCaml/irreader.ml | 31 ++++++++++++++-----
9 files changed, 102 insertions(+), 45 deletions(-)
diff --git a/llvm/bindings/ocaml/bitreader/llvm_bitreader.mli b/llvm/bindings/ocaml/bitreader/llvm_bitreader.mli
index def8b84fe9e62..9133c3993c1cc 100644
--- a/llvm/bindings/ocaml/bitreader/llvm_bitreader.mli
+++ b/llvm/bindings/ocaml/bitreader/llvm_bitreader.mli
@@ -16,11 +16,22 @@ exception Error of string
(** [get_module context mb] reads the bitcode for a new module [m] from the
memory buffer [mb] in the context [context]. Returns [m] if successful, or
raises [Error msg] otherwise, where [msg] is a description of the error
- encountered. See the function [llvm::getBitcodeModule]. *)
+ encountered.
+
+ If parsing succeeds, ownership of [mb] is transferred to the returned
+ module (for lazy deserialization); the caller must not dispose [mb]. If
+ parsing fails, ownership is retained by the caller, which must dispose it.
+
+ See the function [llvm::getBitcodeModule]. *)
val get_module : Llvm.llcontext -> Llvm.llmemorybuffer -> Llvm.llmodule
(** [parse_bitcode context mb] parses the bitcode for a new module [m] from the
memory buffer [mb] in the context [context]. Returns [m] if successful, or
raises [Error msg] otherwise, where [msg] is a description of the error
- encountered. See the function [llvm::ParseBitcodeFile]. *)
+ encountered.
+
+ This function does not take ownership of [mb]; the caller should dispose it
+ (see {!Llvm.MemoryBuffer.dispose}) when it is no longer needed.
+
+ See the function [llvm::ParseBitcodeFile]. *)
val parse_bitcode : Llvm.llcontext -> Llvm.llmemorybuffer -> Llvm.llmodule
diff --git a/llvm/bindings/ocaml/irreader/irreader_ocaml.c b/llvm/bindings/ocaml/irreader/irreader_ocaml.c
index 708d6fb5b0b79..594f490a49a95 100644
--- a/llvm/bindings/ocaml/irreader/irreader_ocaml.c
+++ b/llvm/bindings/ocaml/irreader/irreader_ocaml.c
@@ -24,12 +24,11 @@ void llvm_raise(value Prototype, char *Message);
/* Llvm.llcontext -> Llvm.llmemorybuffer -> Llvm.llmodule */
value llvm_parse_ir(value C, value MemBuf) {
CAMLparam0();
- CAMLlocal2(Variant, MessageVal);
LLVMModuleRef M;
char *Message;
- if (LLVMParseIRInContext(Context_val(C), MemoryBuffer_val(MemBuf), &M,
- &Message))
+ if (LLVMParseIRInContext2(Context_val(C), MemoryBuffer_val(MemBuf), &M,
+ &Message))
llvm_raise(*caml_named_value("Llvm_irreader.Error"), Message);
CAMLreturn(to_val(M));
diff --git a/llvm/bindings/ocaml/irreader/llvm_irreader.mli b/llvm/bindings/ocaml/irreader/llvm_irreader.mli
index bdb7d040845ab..5c0f0614c43c1 100644
--- a/llvm/bindings/ocaml/irreader/llvm_irreader.mli
+++ b/llvm/bindings/ocaml/irreader/llvm_irreader.mli
@@ -16,5 +16,10 @@ exception Error of string
(** [parse_ir context mb] parses the IR for a new module [m] from the
memory buffer [mb] in the context [context]. Returns [m] if successful, or
raises [Error msg] otherwise, where [msg] is a description of the error
- encountered. See the function [llvm::ParseIR]. *)
+ encountered.
+
+ This function does not take ownership of [mb]; the caller should dispose it
+ (see {!Llvm.MemoryBuffer.dispose}) when it is no longer needed.
+
+ See the function [llvm::ParseIR]. *)
val parse_ir : Llvm.llcontext -> Llvm.llmemorybuffer -> Llvm.llmodule
diff --git a/llvm/examples/OrcV2Examples/OrcV2CBindingsLazy/OrcV2CBindingsLazy.c b/llvm/examples/OrcV2Examples/OrcV2CBindingsLazy/OrcV2CBindingsLazy.c
index 9c31f93899201..ea753c6fcc224 100644
--- a/llvm/examples/OrcV2Examples/OrcV2CBindingsLazy/OrcV2CBindingsLazy.c
+++ b/llvm/examples/OrcV2Examples/OrcV2CBindingsLazy/OrcV2CBindingsLazy.c
@@ -71,16 +71,19 @@ LLVMErrorRef parseExampleModule(const char *Source, size_t Len,
// Create an LLVMContext for the Module.
LLVMContextRef Ctx = LLVMContextCreate();
- // Wrap Source in a MemoryBuffer
- LLVMMemoryBufferRef MB =
- LLVMCreateMemoryBufferWithMemoryRange(Source, Len, Name, 0);
-
// Parse the LLVM module.
LLVMModuleRef M;
char *ErrMsg;
- if (LLVMParseIRInContext(Ctx, MB, &M, &ErrMsg)) {
- return LLVMCreateStringError(ErrMsg);
- // TODO: LLVMDisposeMessage(ErrMsg);
+ // Wrap Source in a MemoryBuffer.
+ LLVMMemoryBufferRef MB =
+ LLVMCreateMemoryBufferWithMemoryRange(Source, Len, Name, 0);
+ LLVMBool Ret = LLVMParseIRInContext2(Ctx, MB, &M, &ErrMsg);
+ LLVMDisposeMemoryBuffer(MB);
+
+ if (Ret) {
+ LLVMErrorRef Err = LLVMCreateStringError(ErrMsg);
+ LLVMDisposeMessage(ErrMsg);
+ return Err;
}
// Create a new ThreadSafeContext to hold the context.
diff --git a/llvm/examples/OrcV2Examples/OrcV2CBindingsVeryLazy/OrcV2CBindingsVeryLazy.c b/llvm/examples/OrcV2Examples/OrcV2CBindingsVeryLazy/OrcV2CBindingsVeryLazy.c
index c63a72f1470ff..50272a48925b4 100644
--- a/llvm/examples/OrcV2Examples/OrcV2CBindingsVeryLazy/OrcV2CBindingsVeryLazy.c
+++ b/llvm/examples/OrcV2Examples/OrcV2CBindingsVeryLazy/OrcV2CBindingsVeryLazy.c
@@ -77,14 +77,16 @@ LLVMErrorRef parseExampleModule(const char *Source, size_t Len,
// Create an LLVMContext.
LLVMContextRef Ctx = LLVMContextCreate();
- // Wrap Source in a MemoryBuffer
- LLVMMemoryBufferRef MB =
- LLVMCreateMemoryBufferWithMemoryRange(Source, Len, Name, 1);
-
// Parse the LLVM module.
LLVMModuleRef M;
char *ErrMsg;
- if (LLVMParseIRInContext(Ctx, MB, &M, &ErrMsg)) {
+ // Wrap Source in a MemoryBuffer.
+ LLVMMemoryBufferRef MB =
+ LLVMCreateMemoryBufferWithMemoryRange(Source, Len, Name, 0);
+ LLVMBool Ret = LLVMParseIRInContext2(Ctx, MB, &M, &ErrMsg);
+ LLVMDisposeMemoryBuffer(MB);
+
+ if (Ret) {
LLVMErrorRef Err = LLVMCreateStringError(ErrMsg);
LLVMDisposeMessage(ErrMsg);
return Err;
diff --git a/llvm/include/llvm-c/IRReader.h b/llvm/include/llvm-c/IRReader.h
index 9e5615b5be596..b60ff11bc00c2 100644
--- a/llvm/include/llvm-c/IRReader.h
+++ b/llvm/include/llvm-c/IRReader.h
@@ -33,6 +33,8 @@ LLVM_C_EXTERN_C_BEGIN
* Optionally returns a human-readable description of any errors that
* occurred during parsing IR. OutMessage must be disposed with
* LLVMDisposeMessage.
+ * Takes ownership of the memory buffer.
+ * This is deprecated. Use LLVMParseIRInContext2 instead.
*
* @see llvm::ParseIR()
*/
@@ -40,6 +42,20 @@ LLVM_C_ABI LLVMBool LLVMParseIRInContext(LLVMContextRef ContextRef,
LLVMMemoryBufferRef MemBuf,
LLVMModuleRef *OutM,
char **OutMessage);
+/**
+ * Read LLVM IR from a memory buffer and convert it into an in-memory Module
+ * object. Returns 0 on success.
+ * Optionally returns a human-readable description of any errors that
+ * occurred during parsing IR. OutMessage must be disposed with
+ * LLVMDisposeMessage.
+ * Does not take ownership of the memory buffer.
+ *
+ * @see llvm::ParseIR()
+ */
+LLVM_C_ABI LLVMBool LLVMParseIRInContext2(LLVMContextRef ContextRef,
+ LLVMMemoryBufferRef MemBuf,
+ LLVMModuleRef *OutM,
+ char **OutMessage);
/**
* @}
diff --git a/llvm/lib/IRReader/IRReader.cpp b/llvm/lib/IRReader/IRReader.cpp
index c16871f081d1d..29bf18c01fd7c 100644
--- a/llvm/lib/IRReader/IRReader.cpp
+++ b/llvm/lib/IRReader/IRReader.cpp
@@ -17,6 +17,7 @@
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
+#include <cstring>
#include <optional>
#include <system_error>
@@ -117,23 +118,26 @@ std::unique_ptr<Module> llvm::parseIRFile(StringRef Filename, SMDiagnostic &Err,
LLVMBool LLVMParseIRInContext(LLVMContextRef ContextRef,
LLVMMemoryBufferRef MemBuf, LLVMModuleRef *OutM,
char **OutMessage) {
- SMDiagnostic Diag;
-
std::unique_ptr<MemoryBuffer> MB(unwrap(MemBuf));
- *OutM =
- wrap(parseIR(MB->getMemBufferRef(), Diag, *unwrap(ContextRef)).release());
+ return LLVMParseIRInContext2(ContextRef, wrap(MB.get()), OutM, OutMessage);
+}
- if(!*OutM) {
- if (OutMessage) {
- std::string buf;
- raw_string_ostream os(buf);
+LLVMBool LLVMParseIRInContext2(LLVMContextRef ContextRef,
+ LLVMMemoryBufferRef MemBuf, LLVMModuleRef *OutM,
+ char **OutMessage) {
+ SMDiagnostic Diag;
- Diag.print(nullptr, os, false);
+ *OutM = wrap(parseIR(*unwrap(MemBuf), Diag, *unwrap(ContextRef)).release());
- *OutMessage = strdup(buf.c_str());
- }
- return 1;
+ if (*OutM)
+ return 0;
+
+ if (OutMessage) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ Diag.print(nullptr, OS, /*ShowColors=*/false);
+ *OutMessage = strdup(Buf.c_str());
}
- return 0;
+ return 1;
}
diff --git a/llvm/test/Bindings/OCaml/bitreader.ml b/llvm/test/Bindings/OCaml/bitreader.ml
index 2638ca9d8c769..e2c87d9816a34 100644
--- a/llvm/test/Bindings/OCaml/bitreader.ml
+++ b/llvm/test/Bindings/OCaml/bitreader.ml
@@ -29,13 +29,13 @@ let _ =
(* parse_bitcode *)
begin
let mb = Llvm.MemoryBuffer.of_file fn in
- begin try
- let m = Llvm_bitreader.parse_bitcode context mb in
- Llvm.dispose_module m
- with x ->
- Llvm.MemoryBuffer.dispose mb;
- raise x
- end
+ let m =
+ try Llvm_bitreader.parse_bitcode context mb with x ->
+ Llvm.MemoryBuffer.dispose mb;
+ raise x
+ in
+ Llvm.MemoryBuffer.dispose mb;
+ Llvm.dispose_module m
end;
(* MemoryBuffer.of_file *)
diff --git a/llvm/test/Bindings/OCaml/irreader.ml b/llvm/test/Bindings/OCaml/irreader.ml
index 7d8e4a97e38dc..7ef871146d1dd 100644
--- a/llvm/test/Bindings/OCaml/irreader.ml
+++ b/llvm/test/Bindings/OCaml/irreader.ml
@@ -35,21 +35,38 @@ let insist cond =
let test_irreader () =
begin
let buf = MemoryBuffer.of_string "@foo = global i32 42" in
- let m = parse_ir context buf in
- match lookup_global "foo" m with
- | Some foo ->
- insist ((global_initializer foo) = (Some (const_int (i32_type context) 42)))
- | None ->
- failwith "global"
+ let m =
+ try parse_ir context buf with x ->
+ MemoryBuffer.dispose buf;
+ raise x
+ in
+ MemoryBuffer.dispose buf;
+ try
+ match lookup_global "foo" m with
+ | Some foo ->
+ insist ((global_initializer foo) =
+ (Some (const_int (i32_type context) 42)))
+ | None ->
+ failwith "global";
+ dispose_module m
+ with x ->
+ dispose_module m;
+ raise x
end;
begin
let buf = MemoryBuffer.of_string "@foo = global garble" in
try
- ignore (parse_ir context buf);
+ let m = parse_ir context buf in
+ dispose_module m;
+ MemoryBuffer.dispose buf;
failwith "parsed"
with Llvm_irreader.Error _ ->
+ MemoryBuffer.dispose buf;
()
+ | x ->
+ MemoryBuffer.dispose buf;
+ raise x
end
More information about the llvm-commits
mailing list