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

Eugene Epshteyn via flang-commits flang-commits at lists.llvm.org
Mon Aug 17 20:55:58 PDT 2026


================
@@ -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);
+      }
+    }
----------------
eugeneepshteyn wrote:

(This comment was definitely generated by AI.)

This `else` branch routes every non-AIX target through a direct `fegetenv`/`fesetenv` on the 32-byte `__data` component, which silently drops two things the shared path below deliberately provides for status:

1. The SPARC runtime-sized heap mechanism added by #121949 and documented as a design contract in `flang/include/flang/Runtime/magic-numbers.h`: on Solaris/SPARC, `fenv_t` is far larger than 32 bytes (an array of exception-handler entries plus the FSR), so `fegetenv` now writes past the end of `__data`, silently corrupting the adjacent `__allocatable_data` descriptor and beyond. Compile-only reproducer on an x86 host with the Sparc target registered:

```
flang -fc1 -triple sparcv9-sun-solaris2.11 -emit-llvm status.f90 -o -
# before this PR: calls _FortranAGetStatusTypeSize + malloc
# after this PR:  fegetenv straight into the 32-byte global
```

Note that the *modes* path on the same triple still heap-allocates after this PR, so the two sibling intrinsics now disagree about the platform's contract.

2. The `__GLIBC_USE_IEC_60559_BFP_EXT` guard: on non-glibc-hosted builds, `ieee_get_status`/`ieee_set_status` previously produced a clean TODO error; now they lower unconditionally.

Suggested fix shape: make the new code an AIX-only early branch — `if (triple.isOSAIX()) { ...the new fegetenv/readflm code...; return; }` — and delete this non-AIX `else`, letting non-AIX status fall through to the pre-existing shared modes/status lowering (i.e. restore the `isModes ? ... : ...` ternaries below). That keeps the AIX fix fully intact and restores the SPARC heap path, the non-glibc TODO, and the compiler-side caller of `GetStatusTypeSize`.

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


More information about the flang-commits mailing list