[Mlir-commits] [mlir] [MLIR][Python] Add llvm raw fd ostream c api (PR #179770)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Wed Feb 4 11:52:31 PST 2026
https://github.com/RattataKing created https://github.com/llvm/llvm-project/pull/179770
This PR adds a C API `MlirLlvmRawFdOstream` for llvm::raw_fd_ostream, which cannot be safely replaced by std::ofstream on Windows.
`llvm::raw_fd_ostream` configures Win32 file sharing flags, allowing other handles (e.g. Python temp file handles) to coexist, see details [here](https://llvm.org/doxygen/Windows_2Path_8inc_source.html#l1281).
>From 00b037fa74fbb916397d28d508e3bcdbb67b4d13 Mon Sep 17 00:00:00 2001
From: Amily Wu <amilywu2 at amd.com>
Date: Wed, 4 Feb 2026 19:35:42 +0000
Subject: [PATCH] Add llvm raw fd ostream c api
---
mlir/include/mlir-c/Support.h | 26 +++++++++++++
.../mlir/Bindings/Python/NanobindUtils.h | 26 +++++++++----
mlir/include/mlir/CAPI/Support.h | 2 +
mlir/lib/CAPI/IR/Support.cpp | 38 +++++++++++++++++++
4 files changed, 84 insertions(+), 8 deletions(-)
diff --git a/mlir/include/mlir-c/Support.h b/mlir/include/mlir-c/Support.h
index 6abd8894227c3..3e1baa1cfbc63 100644
--- a/mlir/include/mlir-c/Support.h
+++ b/mlir/include/mlir-c/Support.h
@@ -60,6 +60,9 @@ extern "C" {
/// Re-export llvm::ThreadPool so as to avoid including the LLVM C API directly.
DEFINE_C_API_STRUCT(MlirLlvmThreadPool, void);
+/// Re-export llvm::raw_fd_ostream so as to avoid including the LLVM C API
+/// directly.
+DEFINE_C_API_STRUCT(MlirLlvmRawFdOstream, void);
DEFINE_C_API_STRUCT(MlirTypeID, const void);
DEFINE_C_API_STRUCT(MlirTypeIDAllocator, void);
@@ -153,6 +156,29 @@ MLIR_CAPI_EXPORTED MlirLlvmThreadPool mlirLlvmThreadPoolCreate(void);
/// Destroy an LLVM thread pool.
MLIR_CAPI_EXPORTED void mlirLlvmThreadPoolDestroy(MlirLlvmThreadPool pool);
+//===----------------------------------------------------------------------===//
+// MlirLlvmRawFdOstream.
+//===----------------------------------------------------------------------===//
+
+/// Create a raw_fd_ostream for the given path. This wrapper is needed because
+/// std::ostream does not provide the file sharing semantics required on
+/// Windows. On failure, returns a null stream and invokes the optional error
+/// callback with the error message.
+MLIR_CAPI_EXPORTED MlirLlvmRawFdOstream mlirLlvmRawFdOstreamCreate(
+ const char *path, bool binary, MlirStringCallback errorCallback,
+ void *userData);
+
+/// Write a string to a raw_fd_ostream created with mlirLlvmRawFdOstreamCreate.
+MLIR_CAPI_EXPORTED void mlirLlvmRawFdOstreamWrite(
+ MlirLlvmRawFdOstream stream, MlirStringRef string);
+
+/// Checks if a raw_fd_ostream is null.
+MLIR_CAPI_EXPORTED bool mlirLlvmRawFdOstreamIsNull(MlirLlvmRawFdOstream stream);
+
+/// Destroy a raw_fd_ostream created with mlirLlvmRawFdOstreamCreate.
+MLIR_CAPI_EXPORTED void
+mlirLlvmRawFdOstreamDestroy(MlirLlvmRawFdOstream stream);
+
//===----------------------------------------------------------------------===//
// TypeID API.
//===----------------------------------------------------------------------===//
diff --git a/mlir/include/mlir/Bindings/Python/NanobindUtils.h b/mlir/include/mlir/Bindings/Python/NanobindUtils.h
index 9b41f1d8c3642..b5576b1f55eb2 100644
--- a/mlir/include/mlir/Bindings/Python/NanobindUtils.h
+++ b/mlir/include/mlir/Bindings/Python/NanobindUtils.h
@@ -16,7 +16,6 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/DataTypes.h"
-#include "llvm/Support/raw_ostream.h"
#include <fstream>
#include <sstream>
@@ -165,18 +164,29 @@ class PyFileAccumulator {
: binary(binary) {
std::string filePath;
if (nanobind::try_cast<std::string>(fileOrStringObject, filePath)) {
- std::error_code ec;
- writeTarget.emplace<llvm::raw_fd_ostream>(filePath, ec);
- if (ec) {
+ std::string errorMessage;
+ auto errorCallback = [](MlirStringRef message, void *userData) {
+ auto *storage = static_cast<std::string *>(userData);
+ storage->assign(message.data, message.length);
+ };
+ MlirLlvmRawFdOstream stream = mlirLlvmRawFdOstreamCreate(
+ filePath.c_str(), binary, errorCallback, &errorMessage);
+ if (mlirLlvmRawFdOstreamIsNull(stream)) {
throw nanobind::value_error(
- (std::string("Unable to open file for writing: ") + ec.message())
+ (std::string("Unable to open file for writing: ") + errorMessage)
.c_str());
}
+ writeTarget.emplace<MlirLlvmRawFdOstream>(stream);
} else {
writeTarget.emplace<nanobind::object>(fileOrStringObject.attr("write"));
}
}
+ ~PyFileAccumulator() {
+ if (writeTarget.index() == 1)
+ mlirLlvmRawFdOstreamDestroy(std::get<MlirLlvmRawFdOstream>(writeTarget));
+ }
+
MlirStringCallback getCallback() {
return writeTarget.index() == 0 ? getPyWriteCallback()
: getOstreamCallback();
@@ -204,12 +214,12 @@ class PyFileAccumulator {
MlirStringCallback getOstreamCallback() {
return [](MlirStringRef part, void *userData) {
PyFileAccumulator *accum = static_cast<PyFileAccumulator *>(userData);
- std::get<llvm::raw_fd_ostream>(accum->writeTarget)
- .write(part.data, part.length);
+ mlirLlvmRawFdOstreamWrite(
+ std::get<MlirLlvmRawFdOstream>(accum->writeTarget), part);
};
}
- std::variant<nanobind::object, llvm::raw_fd_ostream> writeTarget;
+ std::variant<nanobind::object, MlirLlvmRawFdOstream> writeTarget;
bool binary;
};
diff --git a/mlir/include/mlir/CAPI/Support.h b/mlir/include/mlir/CAPI/Support.h
index 89a460375f936..59d505dca955a 100644
--- a/mlir/include/mlir/CAPI/Support.h
+++ b/mlir/include/mlir/CAPI/Support.h
@@ -23,6 +23,7 @@
namespace llvm {
class ThreadPoolInterface;
+class raw_fd_ostream;
} // namespace llvm
/// Converts a StringRef into its MLIR C API equivalent.
@@ -46,6 +47,7 @@ inline llvm::LogicalResult unwrap(MlirLogicalResult res) {
}
DEFINE_C_API_PTR_METHODS(MlirLlvmThreadPool, llvm::ThreadPoolInterface)
+DEFINE_C_API_PTR_METHODS(MlirLlvmRawFdOstream, llvm::raw_fd_ostream)
DEFINE_C_API_METHODS(MlirTypeID, mlir::TypeID)
DEFINE_C_API_PTR_METHODS(MlirTypeIDAllocator, mlir::TypeIDAllocator)
diff --git a/mlir/lib/CAPI/IR/Support.cpp b/mlir/lib/CAPI/IR/Support.cpp
index 3311131fc2bc8..0e8eceed09349 100644
--- a/mlir/lib/CAPI/IR/Support.cpp
+++ b/mlir/lib/CAPI/IR/Support.cpp
@@ -8,9 +8,12 @@
#include "mlir/CAPI/Support.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/raw_ostream.h"
#include <cstring>
+#include <string>
MlirStringRef mlirStringRefCreateFromCString(const char *str) {
return mlirStringRefCreate(str, strlen(str));
@@ -32,6 +35,41 @@ void mlirLlvmThreadPoolDestroy(MlirLlvmThreadPool threadPool) {
delete unwrap(threadPool);
}
+//===----------------------------------------------------------------------===//
+// LLVM raw_fd_ostream API.
+//===----------------------------------------------------------------------===//
+
+MlirLlvmRawFdOstream mlirLlvmRawFdOstreamCreate(
+ const char *path, bool binary, MlirStringCallback errorCallback,
+ void *userData) {
+ std::error_code ec;
+ auto flags = binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_Text;
+ auto *stream = new llvm::raw_fd_ostream(path, ec, flags);
+ if (ec) {
+ delete stream;
+ if (errorCallback) {
+ std::string message = ec.message();
+ errorCallback(mlirStringRefCreate(message.data(), message.size()),
+ userData);
+ }
+ return wrap(static_cast<llvm::raw_fd_ostream *>(nullptr));
+ }
+ return wrap(stream);
+}
+
+void mlirLlvmRawFdOstreamWrite(MlirLlvmRawFdOstream stream,
+ MlirStringRef string) {
+ unwrap(stream)->write(string.data, string.length);
+}
+
+bool mlirLlvmRawFdOstreamIsNull(MlirLlvmRawFdOstream stream) {
+ return !stream.ptr;
+}
+
+void mlirLlvmRawFdOstreamDestroy(MlirLlvmRawFdOstream stream) {
+ delete unwrap(stream);
+}
+
//===----------------------------------------------------------------------===//
// TypeID API.
//===----------------------------------------------------------------------===//
More information about the Mlir-commits
mailing list