[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:53:06 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir
Author: RattataKing (RattataKing)
<details>
<summary>Changes</summary>
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).
---
Full diff: https://github.com/llvm/llvm-project/pull/179770.diff
4 Files Affected:
- (modified) mlir/include/mlir-c/Support.h (+26)
- (modified) mlir/include/mlir/Bindings/Python/NanobindUtils.h (+18-8)
- (modified) mlir/include/mlir/CAPI/Support.h (+2)
- (modified) mlir/lib/CAPI/IR/Support.cpp (+38)
``````````diff
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.
//===----------------------------------------------------------------------===//
``````````
</details>
https://github.com/llvm/llvm-project/pull/179770
More information about the Mlir-commits
mailing list