[flang-commits] [flang] [llvm] [flang][PPC] Implement ieee_set_status and ieee_get_status for AIX (PR #215618)

via flang-commits flang-commits at lists.llvm.org
Tue Aug 11 10:55:28 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-fir-hlfir

Author: Kelvin Li (kkwli)

<details>
<summary>Changes</summary>

This patch implements the ieee_set_status and ieee_get_status for AIX PPC. 
On AIX, the trap-enable bits are needed to have the correct round-trip.

Assisted-by: IBM Bob

---
Full diff: https://github.com/llvm/llvm-project/pull/215618.diff


6 Files Affected:

- (modified) flang-rt/lib/runtime/exceptions.cpp (+7-1) 
- (modified) flang-rt/unittests/Runtime/Exceptions.cpp (+16) 
- (modified) flang/include/flang/Optimizer/Builder/LowLevelIntrinsics.h (+6) 
- (modified) flang/lib/Optimizer/Builder/IntrinsicCall.cpp (+74-8) 
- (modified) flang/lib/Optimizer/Builder/LowLevelIntrinsics.cpp (+15) 
- (added) flang/test/Lower/Intrinsics/ieee_status.f90 (+43) 


``````````diff
diff --git a/flang-rt/lib/runtime/exceptions.cpp b/flang-rt/lib/runtime/exceptions.cpp
index 5fb83f0375227..33cb9945c863a 100644
--- a/flang-rt/lib/runtime/exceptions.cpp
+++ b/flang-rt/lib/runtime/exceptions.cpp
@@ -231,7 +231,13 @@ size_t RTNAME(GetModesTypeSize)(void) {
 #endif
 }
 size_t RTNAME(GetStatusTypeSize)(void) {
-  return sizeof(fenv_t); // byte size of ieee_status_type data
+  // byte size of ieee_status_type data
+#if defined(_AIX)
+  // the raw FPSCR double is needed for trap-enable bit round-trip
+  return sizeof(fenv_t) + sizeof(double);
+#else
+  return sizeof(fenv_t);
+#endif
 }
 
 } // extern "C"
diff --git a/flang-rt/unittests/Runtime/Exceptions.cpp b/flang-rt/unittests/Runtime/Exceptions.cpp
index d6514ea9faae9..e2f247c1cb62d 100644
--- a/flang-rt/unittests/Runtime/Exceptions.cpp
+++ b/flang-rt/unittests/Runtime/Exceptions.cpp
@@ -142,3 +142,19 @@ TEST(Exceptions, ClearOneLeavesOthersAlone) {
   GTEST_SKIP() << "FE_OVERFLOW and FE_INVALID required for this test";
 #endif
 }
+
+TEST(Exceptions, GetStatusTypeSizeMatchesPlatformLayout) {
+  const std::size_t sz{RTNAME(GetStatusTypeSize)()};
+#if defined(_AIX)
+  EXPECT_EQ(sz, sizeof(std::fenv_t) + sizeof(double))
+      << "expected sizeof(fenv_t)+sizeof(double)="
+      << sizeof(std::fenv_t) + sizeof(double);
+#else
+  EXPECT_EQ(sz, sizeof(std::fenv_t))
+      << "expected sizeof(fenv_t)=" << sizeof(std::fenv_t);
+#endif
+  // The size must fit in ieee_status_type.__data as integer(4) with
+  // extent _FORTRAN_RUNTIME_IEEE_FENV_T_EXTENT.
+  EXPECT_LE(sz, 32u)
+      << "GetStatusTypeSize exceeds the 32-byte ieee_status_type.__data field";
+}
diff --git a/flang/include/flang/Optimizer/Builder/LowLevelIntrinsics.h b/flang/include/flang/Optimizer/Builder/LowLevelIntrinsics.h
index de945a2ceb47d..be742fa57e7a5 100644
--- a/flang/include/flang/Optimizer/Builder/LowLevelIntrinsics.h
+++ b/flang/include/flang/Optimizer/Builder/LowLevelIntrinsics.h
@@ -30,6 +30,12 @@ mlir::func::FuncOp getLlvmGetRounding(FirOpBuilder &builder);
 /// Get the `llvm.set.rounding` intrinsic.
 mlir::func::FuncOp getLlvmSetRounding(FirOpBuilder &builder);
 
+/// Get the `llvm.ppc.readflm` intrinsic (reads FPSCR, returns f64).
+mlir::func::FuncOp getLlvmPpcReadflm(FirOpBuilder &builder);
+
+/// Get the `llvm.ppc.setflm` intrinsic (sets FPSCR from f64).
+mlir::func::FuncOp getLlvmPpcSetflm(FirOpBuilder &builder);
+
 /// Get the `llvm.init.trampoline` intrinsic.
 mlir::func::FuncOp getLlvmInitTrampoline(FirOpBuilder &builder);
 
diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index f349d41888780..c6f0d72625c0a 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -5210,11 +5210,80 @@ template <bool isGet, bool isModes>
 void IntrinsicLibrary::genIeeeGetOrSetModesOrStatus(
     llvm::ArrayRef<fir::ExtendedValue> args) {
   assert(args.size() == 1);
+  if constexpr (!isModes) {
+    llvm::Triple triple = fir::getTargetTriple(builder.getModule());
+    mlir::Type i32Ty = builder.getIntegerType(32);
+    mlir::Type i32PtrTy = builder.getRefType(i32Ty);
+    if (triple.isOSAIX()) {
+      // On AIX, fegetenv/fesetenv does not round-trip the FPSCR trap-enable
+      // bits [7:3].
+      //
+      // ieee_status_type.__data layout:
+      //   bytes  [0, 20) - fenv_t saved by fegetenv / restored by fesetenv
+      //   bytes [20, 28) - raw FPSCR double from mffs (trap-enable bits [7:3])
+      static constexpr int kAIXFenvTSize = 20; // sizeof(fenv_t) on AIX
+      mlir::Type i8Ty = builder.getIntegerType(8);
+      mlir::Type f64Ty = builder.getF64Type();
+      mlir::Type idxTy = builder.getIndexType();
+      mlir::Type i8SeqTy =
+          fir::SequenceType::get({fir::SequenceType::getUnknownExtent()}, i8Ty);
+      mlir::Type i8SeqPtrTy = builder.getRefType(i8SeqTy);
+      mlir::Type f64PtrTy = builder.getRefType(f64Ty);
+
+      // Cast __data base pointer to !fir.ref<!fir.array<?xi8>> for GEP
+      mlir::Value base = fir::ConvertOp::create(builder, loc, i8SeqPtrTy,
+                                                fir::getBase(args[0]));
+
+      // fenv_t pointer: byte offset 0, cast to !fir.ref<i32> for fe[gs]etenv
+      mlir::Value fenvIdx = builder.createIntegerConstant(loc, idxTy, 0);
+      mlir::Value fenvGep = fir::CoordinateOp::create(
+          builder, loc, builder.getRefType(i8Ty), base, fenvIdx);
+      mlir::Value fenvPtr =
+          fir::ConvertOp::create(builder, loc, i32PtrTy, fenvGep);
+
+      // Raw FPSCR double pointer: byte offset kAIXFenvTSize (20), cast to f64
+      mlir::Value fpIdx =
+          builder.createIntegerConstant(loc, idxTy, kAIXFenvTSize);
+      mlir::Value fpGep = fir::CoordinateOp::create(
+          builder, loc, builder.getRefType(i8Ty), base, fpIdx);
+      mlir::Value fpPtr = fir::ConvertOp::create(builder, loc, f64PtrTy, fpGep);
+
+      if constexpr (isGet) {
+        mlir::func::FuncOp readFlm = fir::factory::getLlvmPpcReadflm(builder);
+        // Save the floating-point environment
+        genRuntimeCall("fegetenv", i32Ty, fenvPtr);
+        // Save the raw FPSCR so that the exception-enable (trap-enable) bits
+        // [7:3] are preserved. On AIX, these bits are not restored by fesetenv.
+        mlir::Value fpscr =
+            fir::CallOp::create(builder, loc, readFlm).getResult(0);
+        // Store the raw FPSCR double at offset kAIXFenvTSize
+        fir::StoreOp::create(builder, loc, fpscr, fpPtr);
+      } else {
+        mlir::func::FuncOp setFlm = fir::factory::getLlvmPpcSetflm(builder);
+        // Restore the floating-point environment
+        genRuntimeCall("fesetenv", i32Ty, fenvPtr);
+        // Load the raw FPSCR double from offset kAIXFenvTSize
+        mlir::Value fpscr = fir::LoadOp::create(builder, loc, fpPtr);
+        // Restore the FPSCR exception-enable (trap-enable) bits [7:3], which
+        // are not restored by fesetenv on AIX.
+        fir::CallOp::create(builder, loc, setFlm, fpscr);
+      }
+    } else {
+      mlir::Value addr =
+          fir::ConvertOp::create(builder, loc, i32PtrTy, fir::getBase(args[0]));
+      if constexpr (isGet) {
+        genRuntimeCall("fegetenv", i32Ty, addr);
+      } else {
+        genRuntimeCall("fesetenv", i32Ty, addr);
+      }
+    }
+    return;
+  }
+
+// isModes is true
 #ifndef __GLIBC_USE_IEC_60559_BFP_EXT // only use of "#include <cfenv>"
   // No definitions of fegetmode, fesetmode
-  llvm::StringRef func = isModes
-                             ? (isGet ? "ieee_get_modes" : "ieee_set_modes")
-                             : (isGet ? "ieee_get_status" : "ieee_set_status");
+  llvm::StringRef func = isGet ? "ieee_get_modes" : "ieee_set_modes";
   TODO(loc, "intrinsic module procedure: " + func);
 #else
   mlir::Type i32Ty = builder.getIntegerType(32);
@@ -5238,9 +5307,7 @@ void IntrinsicLibrary::genIeeeGetOrSetModesOrStatus(
     builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
     fir::ResultOp::create(builder, loc, addr);
     builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
-    mlir::Value byteSize =
-        isModes ? fir::runtime::genGetModesTypeSize(builder, loc)
-                : fir::runtime::genGetStatusTypeSize(builder, loc);
+    mlir::Value byteSize = fir::runtime::genGetModesTypeSize(builder, loc);
     byteSize = builder.createConvert(loc, builder.getIndexType(), byteSize);
     addr = fir::AllocMemOp::create(builder, loc, extractSequenceType(heapTy),
                                    /*typeparams=*/mlir::ValueRange(), byteSize);
@@ -5255,8 +5322,7 @@ void IntrinsicLibrary::genIeeeGetOrSetModesOrStatus(
     // Place floating point environment data in __data storage.
     addr = fir::ConvertOp::create(builder, loc, ptrTy, getBase(args[0]));
   }
-  llvm::StringRef func = isModes ? (isGet ? "fegetmode" : "fesetmode")
-                                 : (isGet ? "fegetenv" : "fesetenv");
+  llvm::StringRef func = isGet ? "fegetmode" : "fesetmode";
   genRuntimeCall(func, i32Ty, addr);
 #endif
 }
diff --git a/flang/lib/Optimizer/Builder/LowLevelIntrinsics.cpp b/flang/lib/Optimizer/Builder/LowLevelIntrinsics.cpp
index 8f8ceef2480a6..a8f8269faa539 100644
--- a/flang/lib/Optimizer/Builder/LowLevelIntrinsics.cpp
+++ b/flang/lib/Optimizer/Builder/LowLevelIntrinsics.cpp
@@ -37,6 +37,21 @@ fir::factory::getLlvmSetRounding(fir::FirOpBuilder &builder) {
                                 funcTy);
 }
 
+mlir::func::FuncOp fir::factory::getLlvmPpcReadflm(fir::FirOpBuilder &builder) {
+  auto f64Ty = builder.getF64Type();
+  auto funcTy = mlir::FunctionType::get(builder.getContext(), {}, {f64Ty});
+  return builder.createFunction(builder.getUnknownLoc(), "llvm.ppc.readflm",
+                                funcTy);
+}
+
+mlir::func::FuncOp fir::factory::getLlvmPpcSetflm(fir::FirOpBuilder &builder) {
+  auto f64Ty = builder.getF64Type();
+  auto funcTy =
+      mlir::FunctionType::get(builder.getContext(), {f64Ty}, {f64Ty});
+  return builder.createFunction(builder.getUnknownLoc(), "llvm.ppc.setflm",
+                                funcTy);
+}
+
 mlir::func::FuncOp
 fir::factory::getLlvmInitTrampoline(fir::FirOpBuilder &builder) {
   auto ptrTy = builder.getRefType(builder.getIntegerType(8));
diff --git a/flang/test/Lower/Intrinsics/ieee_status.f90 b/flang/test/Lower/Intrinsics/ieee_status.f90
new file mode 100644
index 0000000000000..058d1f30f5ce7
--- /dev/null
+++ b/flang/test/Lower/Intrinsics/ieee_status.f90
@@ -0,0 +1,43 @@
+! Tests HLFIR code generation for ieee_get_status / ieee_set_status on PPC targets.
+!
+! REQUIRES: powerpc-registered-target
+! RUN: %flang_fc1 -triple powerpc64-ibm-aix -emit-hlfir -o - %s | FileCheck %s --check-prefix=CHECK-AIX
+! RUN: %flang_fc1 -triple powerpc64le-unknown-linux-gnu -emit-hlfir -o - %s | FileCheck %s --check-prefix=CHECK-LNX
+
+program test
+  use ieee_arithmetic
+  type(ieee_status_type) :: stat
+! LABEL:   func.func @_QQmain
+
+  call ieee_get_status(stat)
+
+! CHECK-AIX: %[[UUDAT:.*]] = fir.convert {{.*}} : ({{.*}}) -> !fir.ref<!fir.array<?xi8>>
+! CHECK-AIX: %c0 = arith.constant 0 : index
+! CHECK-AIX: %[[UUDAT0:.*]] = fir.coordinate_of %[[UUDAT]], %c0 : (!fir.ref<!fir.array<?xi8>>, index) -> !fir.ref<i8>
+! CHECK-AIX: %[[FENV:.*]] = fir.convert %[[UUDAT0]] : (!fir.ref<i8>) -> !fir.ref<i32>
+! CHECK-AIX: %c20 = arith.constant 20 : index
+! CHECK-AIX: %[[UUDAT1:.*]] = fir.coordinate_of %[[UUDAT]], %c20 : (!fir.ref<!fir.array<?xi8>>, index) -> !fir.ref<i8>
+! CHECK-AIX: %[[UUDAT1F:.*]] = fir.convert %[[UUDAT1]] : (!fir.ref<i8>) -> !fir.ref<f64>
+! CHECK-AIX: {{.*}} = fir.call @fegetenv(%[[FENV]]) {{.*}} : (!fir.ref<i32>) -> i32
+! CHECK-AIX: %[[FPS:.*]] = fir.call @llvm.ppc.readflm() {{.*}} : () -> f64
+! CHECK-AIX: fir.store %[[FPS]] to %[[UUDAT1F]] : !fir.ref<f64>
+
+! CHECK-LNX: %[[UUDAT:.*]] = fir.convert {{.*}} : ({{.*}}) -> !fir.ref<i32>
+! CHECK-LNX: {{.*}} = fir.call @fegetenv(%[[UUDAT]]) {{.*}} : (!fir.ref<i32>) -> i32
+
+  call ieee_set_status(stat)
+
+! CHECK-AIX: %[[UUDAT:.*]] = fir.convert {{.*}} : ({{.*}}) -> !fir.ref<!fir.array<?xi8>>
+! CHECK-AIX: %c0_0 = arith.constant 0 : index
+! CHECK-AIX: %[[UUDAT0:.*]] = fir.coordinate_of %[[UUDAT]], %c0_0 : (!fir.ref<!fir.array<?xi8>>, index) -> !fir.ref<i8>
+! CHECK-AIX: %[[FENV:.*]] = fir.convert %[[UUDAT0]] : (!fir.ref<i8>) -> !fir.ref<i32>
+! CHECK-AIX: %c20_1 = arith.constant 20 : index
+! CHECK-AIX: %[[UUDAT1:.*]] = fir.coordinate_of %[[UUDAT]], %c20_1 : (!fir.ref<!fir.array<?xi8>>, index) -> !fir.ref<i8>
+! CHECK-AIX: %[[UUDAT1F:.*]] = fir.convert %[[UUDAT1]] : (!fir.ref<i8>) -> !fir.ref<f64>
+! CHECK-AIX: {{.*}} = fir.call @fesetenv(%[[FENV]]) {{.*}} : (!fir.ref<i32>) -> i32
+! CHECK-AIX: %[[FPS:.*]] = fir.load %[[UUDAT1F]] : !fir.ref<f64>
+! CHECK-AIX: fir.call @llvm.ppc.setflm(%[[FPS]]) {{.*}} : (f64) -> f64
+
+! CHECK-LNX: %[[UUDAT:.*]] = fir.convert {{.*}} : ({{.*}}) -> !fir.ref<i32>
+! CHECK-LNX: {{.*}} = fir.call @fesetenv(%[[UUDAT]]) {{.*}} : (!fir.ref<i32>) -> i32
+end program

``````````

</details>


https://github.com/llvm/llvm-project/pull/215618


More information about the flang-commits mailing list