[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