[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