https://github.com/Himadhith created 
https://github.com/llvm/llvm-project/pull/209705

AIX fopen(3) does not atomically handle the C11/C23 'x' mode.
Redirect fopen(path, "...x...") to a helper using open(O_CREAT|
O_EXCL)+fdopen. Remove XFAIL: LIBCXX-AIX-FIXME from fstream tests.

>From 0e35816ce0a42f3e497f97f791c7dca3c23ca758 Mon Sep 17 00:00:00 2001
From: himadhith <[email protected]>
Date: Wed, 15 Jul 2026 14:41:42 +0530
Subject: [PATCH] [Clang][AIX] Fix fopen 'x' mode via
 open(O_CREAT|O_EXCL)+fdopen

AIX fopen(3) does not atomically handle the C11/C23 'x' mode.
Redirect fopen(path, "...x...") to a helper using open(O_CREAT|
O_EXCL)+fdopen. Remove XFAIL: LIBCXX-AIX-FIXME from fstream tests.
---
 clang/lib/CodeGen/CGExpr.cpp                  |  21 +++
 clang/lib/CodeGen/CodeGenModule.cpp           | 176 ++++++++++++++++++
 clang/lib/CodeGen/CodeGenModule.h             |   4 +
 .../filebuf.members/open_pointer.pass.cpp     |   2 -
 .../fstreams/fstream.cons/pointer.pass.cpp    |   2 -
 .../fstream.members/open_pointer.pass.cpp     |   2 -
 .../fstreams/ofstream.cons/pointer.pass.cpp   |   2 -
 .../ofstream.members/open_pointer.pass.cpp    |   2 -
 8 files changed, 201 insertions(+), 10 deletions(-)

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 6a0d127fb451b..7f42ac03bfa98 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -6517,6 +6517,27 @@ RValue CodeGenFunction::EmitCallExpr(const CallExpr *E,
     return EmitCXXPseudoDestructorExpr(callee.getPseudoDestructorExpr());
   }
 
+  // On AIX, redirect fopen(path, "...x...") to a helper that uses
+  // open(O_CREAT|O_EXCL)+fdopen, which handles exclusive creation atomically.
+  if (CGM.getTriple().isOSAIX()) {
+    if (const FunctionDecl *FD = E->getDirectCallee()) {
+      if (FD->getDeclName().isIdentifier() &&
+          FD->getName() == "fopen" && E->getNumArgs() == 2) {
+        const Expr *ModeArg = E->getArg(1)->IgnoreImpCasts();
+        if (const StringLiteral *SL = dyn_cast<StringLiteral>(ModeArg)) {
+          if (SL->getString().contains('x')) {
+            llvm::Function *Helper = CGM.getOrCreateAIXFOpenExclusiveHelper();
+            llvm::Value *PathVal = EmitScalarExpr(E->getArg(0));
+            llvm::Value *ModeVal = EmitScalarExpr(E->getArg(1));
+            llvm::CallInst *CI = Builder.CreateCall(Helper, {PathVal, 
ModeVal});
+            CI->setCallingConv(Helper->getCallingConv());
+            return RValue::get(CI);
+          }
+        }
+      }
+    }
+  }
+
   return EmitCall(E->getCallee()->getType(), callee, E, ReturnValue,
                   /*Chain=*/nullptr, CallOrInvoke);
 }
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index e155fdd752d7f..4d3449d2476f6 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -57,8 +57,12 @@
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/BinaryFormat/ELF.h"
 #include "llvm/IR/AttributeMask.h"
+#include "llvm/IR/BasicBlock.h"
 #include "llvm/IR/CallingConv.h"
+#include "llvm/IR/Constants.h"
 #include "llvm/IR/DataLayout.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Module.h"
@@ -8855,3 +8859,175 @@ void 
CodeGenModule::requireVectorDestructorDefinition(const CXXRecordDecl *RD) {
   // even if destructor is only declared.
   addDeferredDeclToEmit(VectorDtorGD);
 }
+
+// Stable open(2) flag values from AIX <fcntl.h>.
+static constexpr unsigned AIX_O_RDONLY  = 0x0;
+static constexpr unsigned AIX_O_WRONLY  = 0x1;
+static constexpr unsigned AIX_O_RDWR    = 0x2;
+static constexpr unsigned AIX_O_ACCMODE = 0x3;
+static constexpr unsigned AIX_O_APPEND  = 0x8;
+static constexpr unsigned AIX_O_CREAT   = 0x100;
+static constexpr unsigned AIX_O_TRUNC   = 0x200;
+static constexpr unsigned AIX_O_EXCL    = 0x400;
+
+// On AIX, fopen(3) does not atomically implement the C11/C23 'x' exclusive
+// mode. Lazily synthesises __aix_fopen_exclusive(path, mode) which uses
+// open(O_CREAT|O_EXCL)+fdopen so that exclusive creation is truly atomic.
+llvm::Function *CodeGenModule::getOrCreateAIXFOpenExclusiveHelper() {
+  assert(getTriple().isOSAIX() &&
+         "AIX fopen exclusive helper is only needed on AIX");
+
+  constexpr llvm::StringLiteral HelperName("__aix_fopen_exclusive");
+  llvm::Module &M = getModule();
+
+  if (llvm::Function *F = M.getFunction(HelperName))
+    return F;
+
+  llvm::LLVMContext &Ctx = getLLVMContext();
+  llvm::PointerType *PtrTy   = llvm::PointerType::getUnqual(Ctx);
+  llvm::IntegerType *Int32Ty = llvm::Type::getInt32Ty(Ctx);
+  llvm::IntegerType *Int8Ty  = llvm::Type::getInt8Ty(Ctx);
+  llvm::ArrayType   *Arr8Ty  = llvm::ArrayType::get(Int8Ty, 8);
+
+  llvm::Value *Zero32     = llvm::ConstantInt::get(Int32Ty, 0);
+  llvm::Value *One32      = llvm::ConstantInt::get(Int32Ty, 1);
+  llvm::Value *MinusOne32 = llvm::ConstantInt::getSigned(Int32Ty, -1);
+  llvm::Value *ZeroI8     = llvm::ConstantInt::get(Int8Ty, 0);
+  llvm::Value *NullPtr    = llvm::ConstantPointerNull::get(PtrTy);
+  llvm::Value *CreatExclFlags =
+      llvm::ConstantInt::get(Int32Ty, AIX_O_CREAT | AIX_O_EXCL);
+
+  llvm::FunctionType *FnTy =
+      llvm::FunctionType::get(PtrTy, {PtrTy, PtrTy}, /*isVarArg=*/false);
+  llvm::Function *Fn = llvm::Function::Create(
+      FnTy, llvm::GlobalValue::LinkOnceODRLinkage, HelperName, &M);
+  Fn->setVisibility(llvm::GlobalValue::HiddenVisibility);
+  Fn->addFnAttr(llvm::Attribute::NoUnwind);
+  Fn->setDSOLocal(true);
+
+  llvm::Argument *ArgPath = Fn->getArg(0);
+  llvm::Argument *ArgMode = Fn->getArg(1);
+  ArgPath->setName("path");
+  ArgMode->setName("mode");
+
+  llvm::FunctionCallee OpenFn = M.getOrInsertFunction(
+      "open",
+      llvm::FunctionType::get(Int32Ty, {PtrTy, Int32Ty}, /*isVarArg=*/true));
+  llvm::FunctionCallee FdopenFn = M.getOrInsertFunction(
+      "fdopen",
+      llvm::FunctionType::get(PtrTy, {Int32Ty, PtrTy}, /*isVarArg=*/false));
+  llvm::FunctionCallee CloseFn = M.getOrInsertFunction(
+      "close",
+      llvm::FunctionType::get(Int32Ty, {Int32Ty}, /*isVarArg=*/false));
+
+  using BB = llvm::BasicBlock;
+  llvm::IRBuilder<> B(Ctx);
+
+  BB *EntryBB      = BB::Create(Ctx, "entry",      Fn);
+  BB *OpenCallBB   = BB::Create(Ctx, "open.call",  Fn);
+  BB *OpenOkBB     = BB::Create(Ctx, "open.ok",    Fn);
+  BB *OpenFailBB   = BB::Create(Ctx, "open.fail",  Fn);
+  BB *FdopenOkBB   = BB::Create(Ctx, "fdopen.ok",  Fn);
+  BB *FdopenFailBB = BB::Create(Ctx, "fdopen.fail",Fn);
+  BB *RetBB        = BB::Create(Ctx, "ret",        Fn);
+
+  B.SetInsertPoint(EntryBB);
+
+  llvm::AllocaInst *BmodeBuf = B.CreateAlloca(Arr8Ty, nullptr, "bmode");
+  BmodeBuf->setAlignment(llvm::Align(1));
+
+  // Derive open(2) access flags from mode[0]: 'w'→O_WRONLY|O_TRUNC,
+  // 'a'→O_WRONLY|O_APPEND, otherwise O_RDONLY.
+  llvm::Value *Mode0 = B.CreateLoad(Int8Ty, ArgMode, "mode0");
+  llvm::Value *AccFlags = B.CreateSelect(
+      B.CreateICmpEQ(Mode0, llvm::ConstantInt::get(Int8Ty, 'w'), "is.w"),
+      llvm::ConstantInt::get(Int32Ty, AIX_O_WRONLY | AIX_O_TRUNC),
+      B.CreateSelect(
+          B.CreateICmpEQ(Mode0, llvm::ConstantInt::get(Int8Ty, 'a'), "is.a"),
+          llvm::ConstantInt::get(Int32Ty, AIX_O_WRONLY | AIX_O_APPEND),
+          llvm::ConstantInt::get(Int32Ty, AIX_O_RDONLY)),
+      "acc.flags");
+
+  // If '+' appears anywhere in mode, upgrade to O_RDWR (unrolled 6 iters).
+  llvm::Value *HasPlus = llvm::ConstantInt::get(llvm::Type::getInt1Ty(Ctx), 0);
+  for (int i = 0; i < 6; ++i) {
+    llvm::Value *Ch = B.CreateLoad(Int8Ty,
+        B.CreateConstGEP1_64(Int8Ty, ArgMode, i, ("mp" + 
llvm::Twine(i)).str()),
+        ("mc" + llvm::Twine(i)).str());
+    HasPlus = B.CreateOr(HasPlus,
+        B.CreateICmpEQ(Ch, llvm::ConstantInt::get(Int8Ty, '+'),
+            ("eq.plus" + llvm::Twine(i)).str()),
+        ("has.plus" + llvm::Twine(i)).str());
+  }
+  llvm::Value *AccMask = llvm::ConstantInt::get(Int32Ty, ~AIX_O_ACCMODE);
+  llvm::Value *AccFlagsFinal = B.CreateSelect(HasPlus,
+      B.CreateOr(B.CreateAnd(AccFlags, AccMask),
+                 llvm::ConstantInt::get(Int32Ty, AIX_O_RDWR)),
+      AccFlags, "acc.final");
+
+  llvm::Value *OpenFlags =
+      B.CreateOr(AccFlagsFinal, CreatExclFlags, "open.flags");
+
+  // Build a mode string without 'x' in BmodeBuf (unrolled 6 iters).
+  llvm::AllocaInst *WIdxAlloca = B.CreateAlloca(Int32Ty, nullptr, "widx");
+  B.CreateStore(Zero32, WIdxAlloca);
+  llvm::Value *XChar = llvm::ConstantInt::get(Int8Ty, 'x');
+  for (int i = 0; i < 6; ++i) {
+    llvm::Value *Ch = B.CreateLoad(Int8Ty,
+        B.CreateConstGEP1_64(Int8Ty, ArgMode, i, ("sp" + 
llvm::Twine(i)).str()),
+        ("ch" + llvm::Twine(i)).str());
+    BB *DoStore   = BB::Create(Ctx, ("store" + llvm::Twine(i)).str(), Fn);
+    BB *AfterIter = BB::Create(Ctx, ("after" + llvm::Twine(i)).str(), Fn);
+    B.CreateCondBr(
+        B.CreateOr(B.CreateICmpEQ(Ch, XChar), B.CreateICmpEQ(Ch, ZeroI8),
+            ("skip" + llvm::Twine(i)).str()),
+        AfterIter, DoStore);
+    B.SetInsertPoint(DoStore);
+    llvm::Value *WIdx = B.CreateLoad(Int32Ty, WIdxAlloca, "wi");
+    B.CreateStore(Ch, B.CreateGEP(Arr8Ty, BmodeBuf,
+        {Zero32, WIdx}, ("dp" + llvm::Twine(i)).str()));
+    B.CreateStore(B.CreateAdd(WIdx, One32), WIdxAlloca);
+    B.CreateBr(AfterIter);
+    B.SetInsertPoint(AfterIter);
+  }
+  llvm::Value *WIdx = B.CreateLoad(Int32Ty, WIdxAlloca, "wi.fin");
+  B.CreateStore(ZeroI8,
+      B.CreateGEP(Arr8Ty, BmodeBuf, {Zero32, WIdx}, "nuldst"));
+  llvm::Value *BmodePtr =
+      B.CreateConstGEP2_32(Arr8Ty, BmodeBuf, 0, 0, "bmode.ptr");
+  B.CreateBr(OpenCallBB);
+
+  B.SetInsertPoint(OpenCallBB);
+  llvm::Value *Mode0666 = llvm::ConstantInt::get(Int32Ty, 0666);
+  llvm::CallInst *FdVal =
+      B.CreateCall(OpenFn, {ArgPath, OpenFlags, Mode0666}, "fd");
+  FdVal->setDoesNotThrow();
+  B.CreateCondBr(B.CreateICmpEQ(FdVal, MinusOne32, "open.fail.cmp"),
+                 OpenFailBB, OpenOkBB);
+
+  B.SetInsertPoint(OpenFailBB);
+  B.CreateBr(RetBB);
+
+  B.SetInsertPoint(OpenOkBB);
+  llvm::CallInst *FpVal =
+      B.CreateCall(FdopenFn, {FdVal, BmodePtr}, "fp");
+  FpVal->setDoesNotThrow();
+  B.CreateCondBr(B.CreateICmpEQ(FpVal, NullPtr, "fdopen.fail.cmp"),
+                 FdopenFailBB, FdopenOkBB);
+
+  B.SetInsertPoint(FdopenFailBB);
+  B.CreateCall(CloseFn, {FdVal})->setDoesNotThrow();
+  B.CreateBr(RetBB);
+
+  B.SetInsertPoint(FdopenOkBB);
+  B.CreateBr(RetBB);
+
+  B.SetInsertPoint(RetBB);
+  llvm::PHINode *Ret = B.CreatePHI(PtrTy, 3, "retval");
+  Ret->addIncoming(NullPtr, OpenFailBB);
+  Ret->addIncoming(NullPtr, FdopenFailBB);
+  Ret->addIncoming(FpVal,   FdopenOkBB);
+  B.CreateRet(Ret);
+
+  return Fn;
+}
diff --git a/clang/lib/CodeGen/CodeGenModule.h 
b/clang/lib/CodeGen/CodeGenModule.h
index b1e70a7c347db..4578ae2257a01 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1414,6 +1414,10 @@ class CodeGenModule : public CodeGenTypeCache {
   llvm::Constant *CreateRuntimeVariable(llvm::Type *Ty,
                                         StringRef Name);
 
+  // On AIX, fopen(3) with 'x' mode is not atomic. Lazily emits a helper
+  // __aix_fopen_exclusive that uses open(O_CREAT|O_EXCL)+fdopen instead.
+  llvm::Function *getOrCreateAIXFOpenExclusiveHelper();
+
   ///@name Custom Blocks Runtime Interfaces
   ///@{
 
diff --git 
a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
 
b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 9f617dc1e5a89..ade3affe03821 100644
--- 
a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ 
b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -13,8 +13,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib 
(a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && 
using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
diff --git 
a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
 
b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index 2e0ebcd684d79..3db29707c27e7 100644
--- 
a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ 
b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib 
(a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && 
using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 
diff --git 
a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
 
b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 0d83d681b1dfc..a54444f4b2f53 100644
--- 
a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ 
b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib 
(a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && 
using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
diff --git 
a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
 
b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index fbb03f1e85841..3c4a06c2efa21 100644
--- 
a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ 
b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib 
(a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && 
using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include <ios>
diff --git 
a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
 
b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 73a474277a933..6446ca16cb409 100644
--- 
a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ 
b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib 
(a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && 
using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to