[clang] [CIR] Add long double format module attribute (PR #234275)

via cfe-commits cfe-commits at lists.llvm.org
Wed Oct 21 09:47:08 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Mariya Podchishchaeva (Fznamznon)

<details>
<summary>Changes</summary>

The attribute allows to preserve information about `-mlong-double-*` and other long double length controlling options used to create serialized CIR and maps directly to long-double-size metadata. Clang won't emit the attribute if no `long_double` type is present in the module.

Assisted-by: claude

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


11 Files Affected:

- (modified) clang/include/clang/CIR/Dialect/IR/CIRAttrs.td (+29) 
- (modified) clang/include/clang/CIR/Dialect/IR/CIRDialect.td (+1) 
- (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+19) 
- (modified) clang/lib/CIR/CodeGen/CIRGenTypes.cpp (+1) 
- (modified) clang/lib/CIR/CodeGen/CIRGenTypes.h (+4) 
- (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+61) 
- (modified) clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.cpp (+27) 
- (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+13) 
- (added) clang/test/CIR/CodeGen/long-double-format-attr.c (+40) 
- (added) clang/test/CIR/IR/invalid-long-double-format.cir (+42) 
- (added) clang/test/CIR/Lowering/long-double-format-attr.cir (+7) 


``````````diff
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index b3cc021989fde..9ab592437a5e3 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -2672,6 +2672,35 @@ def CIR_LoweringLangOptionsAttr
   let canHaveIllegalCXXABIType = 0;
 }
 
+//===----------------------------------------------------------------------===//
+// LongDoubleFormatAttr
+//===----------------------------------------------------------------------===//
+
+def CIR_LongDoubleFormat : CIR_I32Enum<"LongDoubleFormat",
+    "long double format", [
+  I32EnumCase<"IEEEsingle", 0, "float">,
+  I32EnumCase<"IEEEdouble", 1, "double">,
+  I32EnumCase<"X87DoubleExtended", 2, "x86_fp80">,
+  I32EnumCase<"IEEEquad", 3, "fp128">,
+  I32EnumCase<"PPCDoubleDouble", 4, "ppc_fp128">
+]>;
+
+def CIR_LongDoubleFormatAttr
+    : CIR_EnumAttr<CIR_LongDoubleFormat, "long_double_format"> {
+  let summary = "Module long double format";
+  let description = [{
+    Records the floating-point format used for `long double` in the module.
+    The values of the attribute mirror possible values of `long-double-type`
+    LLVM IR metadata.
+
+    Example:
+    ```
+    module attributes {
+      cir.long_double_format = #cir.long_double_format<x86_fp80>} {}
+    ```
+  }];
+}
+
 //===----------------------------------------------------------------------===//
 // AnnotationAttr
 //===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
index 53e4730e95d97..ad4f8182904fd 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
@@ -120,6 +120,7 @@ def CIR_Dialect : Dialect {
     // LangOptions facts that post-CIRGen lowering consumes, serialized by
     // CIRGen so a reloaded .cir module is self-describing without a live
     // clang::LangOptions. See #cir.lowering_lang_options.
+    static llvm::StringRef getLongDoubleFormatAttrName() { return "cir.long_double_format"; }
     static llvm::StringRef getLoweringLangOptionsAttrName() { return "cir.lowering_lang_options"; }
     // Platform SDK version from -target-sdk-version, as printed by
     // llvm::VersionTuple (e.g. "12.3"). Absent when none was given.
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 6142ee62f3be1..74609be3dffeb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -4027,6 +4027,25 @@ void CIRGenModule::release() {
         cir::CIRDialect::getModuleFramePointerAttrName(),
         cir::FramePointerKindAttr::get(&getMLIRContext(), *kind));
 
+  if (getTypes().isLongDoubleReferenced()) {
+    const llvm::fltSemantics *flt = &getTarget().getLongDoubleFormat();
+    std::optional<cir::LongDoubleFormat> format;
+    if (flt == &llvm::APFloat::IEEEquad())
+      format = cir::LongDoubleFormat::IEEEquad;
+    else if (flt == &llvm::APFloat::IEEEdouble())
+      format = cir::LongDoubleFormat::IEEEdouble;
+    else if (flt == &llvm::APFloat::PPCDoubleDouble())
+      format = cir::LongDoubleFormat::PPCDoubleDouble;
+    else if (flt == &llvm::APFloat::x87DoubleExtended())
+      format = cir::LongDoubleFormat::X87DoubleExtended;
+    else if (flt == &llvm::APFloat::IEEEsingle())
+      format = cir::LongDoubleFormat::IEEEsingle;
+    if (format)
+      theModule->setAttr(
+          cir::CIRDialect::getLongDoubleFormatAttrName(),
+          cir::LongDoubleFormatAttr::get(&getMLIRContext(), *format));
+  }
+
   if (!recordLayoutEntries.empty())
     theModule->setAttr(
         cir::CIRDialect::getRecordLayoutsAttrName(),
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index e1da57c9a38db..8eaecbad8b1f1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -486,6 +486,7 @@ mlir::Type CIRGenTypes::convertType(QualType type) {
       resultType = cgm.doubleTy;
       break;
     case BuiltinType::LongDouble:
+      longDoubleReferenced = true;
       resultType =
           builder.getLongDoubleTy(astContext.getFloatTypeSemantics(type));
       break;
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h
index 852e1f4ab6699..d243817d9ca77 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.h
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h
@@ -74,6 +74,9 @@ class CIRGenTypes {
 
   llvm::SmallVector<const clang::RecordDecl *, 8> deferredRecords;
 
+  /// True if any instance of long double types are used.
+  bool longDoubleReferenced = false;
+
   /// Cache of record type keys known to be safe to convert (i.e.,
   /// isSafeToConvert returned true). Cleared whenever recordsBeingLaidOut
   /// changes, since the safety result depends on which records are currently
@@ -92,6 +95,7 @@ class CIRGenTypes {
 
   /// Utility to check whether a function type can be converted to a CIR type
   /// (i.e. doesn't depend on an incomplete tag type).
+  bool isLongDoubleReferenced() const { return longDoubleReferenced; }
   bool isFuncTypeConvertible(const clang::FunctionType *ft);
   bool isFuncParamTypeConvertible(clang::QualType type);
 
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index b5ee092283075..88a6a50b76502 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -281,6 +281,43 @@ bool cir::CIRDialect::isPureCIRType(mlir::Type ty) const {
   return result;
 }
 
+/// Returns true if \p triple can use \p format for long double: either it is
+/// the target default, or the target lets the user select it (-mlong-double-64,
+/// -mlong-double-80, -mlong-double-128, -mabi=ieeelongdouble).
+static bool isLongDoubleFormatSupported(const llvm::Triple &triple,
+                                        cir::LongDoubleFormat format) {
+  auto toFormat = [](llvm::LongDoubleFormat f) {
+    switch (f) {
+    case llvm::LongDoubleFormat::IEEEsingle:
+      return cir::LongDoubleFormat::IEEEsingle;
+    case llvm::LongDoubleFormat::IEEEdouble:
+      return cir::LongDoubleFormat::IEEEdouble;
+    case llvm::LongDoubleFormat::X87DoubleExtended:
+      return cir::LongDoubleFormat::X87DoubleExtended;
+    case llvm::LongDoubleFormat::IEEEquad:
+      return cir::LongDoubleFormat::IEEEquad;
+    case llvm::LongDoubleFormat::PPCDoubleDouble:
+      return cir::LongDoubleFormat::PPCDoubleDouble;
+    }
+    llvm_unreachable("unknown long double format");
+  };
+  if (format == toFormat(triple.getDefaultLongDoubleFormat()))
+    return true;
+
+  switch (format) {
+  case cir::LongDoubleFormat::IEEEdouble:
+  case cir::LongDoubleFormat::IEEEquad:
+    return triple.isX86() || triple.isPPC();
+  case cir::LongDoubleFormat::X87DoubleExtended:
+    return triple.isX86();
+  case cir::LongDoubleFormat::PPCDoubleDouble:
+    return triple.isPPC();
+  case cir::LongDoubleFormat::IEEEsingle:
+    return false;
+  }
+  llvm_unreachable("unknown long double format");
+}
+
 LogicalResult
 cir::CIRDialect::verifyOperationAttribute(mlir::Operation *op,
                                           mlir::NamedAttribute attr) {
@@ -313,6 +350,30 @@ cir::CIRDialect::verifyOperationAttribute(mlir::Operation *op,
     return success();
   }
 
+  if (attrName == getLongDoubleFormatAttrName()) {
+    if (!mlir::isa<mlir::ModuleOp>(op))
+      return op->emitOpError() << "expects '" << getLongDoubleFormatAttrName()
+                               << "' attribute to be attached to '"
+                               << mlir::ModuleOp::getOperationName() << "'";
+    auto format = mlir::dyn_cast<cir::LongDoubleFormatAttr>(attr.getValue());
+    if (!format)
+      return op->emitOpError()
+             << "expects '" << getLongDoubleFormatAttrName()
+             << "' to be a #cir.long_double_format attribute";
+
+    auto triple = op->getAttrOfType<mlir::StringAttr>(getTripleAttrName());
+    if (!triple)
+      return success();
+
+    llvm::Triple tripleVal(triple.getValue());
+    if (!isLongDoubleFormatSupported(tripleVal, format.getValue()))
+      return op->emitOpError()
+             << "long double format '"
+             << cir::stringifyLongDoubleFormat(format.getValue())
+             << "' is not supported on target '" << triple.getValue() << "'";
+    return success();
+  }
+
   if (attrName == getOffloadContainerAttrName()) {
     if (!mlir::isa<mlir::UnitAttr>(attr.getValue()))
       return op->emitOpError() << "expects '" << getOffloadContainerAttrName()
diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.cpp
index 2fda1c996ad5c..4e058513b1786 100644
--- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.cpp
@@ -115,6 +115,28 @@ std::unique_ptr<LowerModule> createLowerModule(mlir::ModuleOp module) {
     langOpts.setClangABICompat(static_cast<clang::LangOptions::ClangABI>(
         loweringLangOpts.getClangAbiCompat()));
   }
+  if (auto ldFormat = mlir::dyn_cast_if_present<cir::LongDoubleFormatAttr>(
+          module->getAttr(cir::CIRDialect::getLongDoubleFormatAttrName()))) {
+    switch (ldFormat.getValue()) {
+    case cir::LongDoubleFormat::IEEEsingle:
+      // There is no frontend option to force 32 bit long double, so let it
+      // derive from the target.
+      break;
+    case cir::LongDoubleFormat::IEEEdouble:
+      langOpts.LongDoubleSize = 64;
+      break;
+    case cir::LongDoubleFormat::X87DoubleExtended:
+      langOpts.LongDoubleSize = 80;
+      break;
+    case cir::LongDoubleFormat::IEEEquad:
+      langOpts.LongDoubleSize = 128;
+      break;
+    case cir::LongDoubleFormat::PPCDoubleDouble:
+      // TargetInfo::adjust would turn 128 into IEEE quad, so leave it unset to
+      // keep the target's own ppc_fp128 format.
+      break;
+    }
+  }
   // Some langopts are still not lowered.
   assert(!cir::MissingFeatures::lowerModuleLangOpts());
 
@@ -133,6 +155,11 @@ std::unique_ptr<LowerModule> createLowerModule(mlir::ModuleOp module) {
     codeGenOpts.OptimizeSize = optInfo.getSize();
   }
 
+  clang::DiagnosticOptions diagOpts;
+  clang::DiagnosticsEngine diags(clang::DiagnosticIDs::create(), diagOpts,
+                                 new clang::IgnoringDiagConsumer());
+  targetInfo->adjust(diags, langOpts, /*Aux=*/nullptr);
+
   return std::make_unique<LowerModule>(std::move(langOpts),
                                        std::move(codeGenOpts), module,
                                        std::move(targetInfo));
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 417d629be68b9..8c2dc1e40e0f0 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -4809,6 +4809,19 @@ void ConvertCIRToLLVMPass::processCIRAttrs(mlir::ModuleOp module) {
                                       builder.getArrayAttr({flag}));
   }
 
+  if (auto ldFormat = module->getAttrOfType<cir::LongDoubleFormatAttr>(
+          cir::CIRDialect::getLongDoubleFormatAttrName())) {
+    mlir::OpBuilder builder(module.getContext());
+    builder.setInsertionPointToEnd(module.getBody());
+    auto flag = mlir::LLVM::ModuleFlagAttr::get(
+        module.getContext(), mlir::LLVM::ModFlagBehavior::Error,
+        builder.getStringAttr("long-double-type"),
+        builder.getStringAttr(cir::stringifyLongDoubleFormat(
+            ldFormat.getValue())));
+    mlir::LLVM::ModuleFlagsOp::create(builder, module.getLoc(),
+                                      builder.getArrayAttr({flag}));
+  }
+
   lowerOpenCLModuleMetadataAttrs(module);
 }
 
diff --git a/clang/test/CIR/CodeGen/long-double-format-attr.c b/clang/test/CIR/CodeGen/long-double-format-attr.c
new file mode 100644
index 0000000000000..48d3324da0283
--- /dev/null
+++ b/clang/test/CIR/CodeGen/long-double-format-attr.c
@@ -0,0 +1,40 @@
+// This test mirrors CodeGen/long-double-module-flag.c
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -fclangir -emit-cir -o - | FileCheck %s --check-prefix=X86FP80-CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -DNOLDBL %s -emit-cir -o - | FileCheck %s --check-prefix=NOLDBL-CIR
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -fclangir -emit-llvm -o - | FileCheck %s --check-prefix=X86FP80
+// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu %s -fclangir -emit-llvm -o - | FileCheck %s --check-prefix=FP128
+
+// FIXME: enable when arm abi is supported
+// RUN-disabled: %clang_cc1 -triple arm-none-eabi %s -fclangir -emit-llvm -o - | FileCheck %s --check-prefix=DOUBLE
+// FIXME: enable when PPC double-double format for long double is done
+// RUN-disabled: %clang_cc1 -triple powerpc64le-unknown-linux-gnu %s -fclangir -emit-llvm -o - | FileCheck %s --check-prefix=PPCFP128
+
+// RUN: %clang_cc1 -triple powerpc64le-unknown-linux-gnu %s -fclangir -emit-llvm -mabi=ieeelongdouble -o - | FileCheck %s --check-prefix=FP128
+// RUN: %clang_cc1 -triple powerpc64le-unknown-linux-gnu %s -fclangir -emit-llvm -mlong-double-64 -o - | FileCheck %s --check-prefix=DOUBLE
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -DNOLDBL %s -emit-llvm -o - | FileCheck %s --check-prefix=NOLDBL
+
+// classic CG runs.
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -o - | FileCheck %s --check-prefix=X86FP80
+// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu %s -emit-llvm -o - | FileCheck %s --check-prefix=FP128
+// RUN: %clang_cc1 -triple arm-none-eabi %s -emit-llvm -o - | FileCheck %s --check-prefix=DOUBLE
+// RUN: %clang_cc1 -triple powerpc64le-unknown-linux-gnu %s -emit-llvm -o - | FileCheck %s --check-prefix=PPCFP128
+// RUN: %clang_cc1 -triple powerpc64le-unknown-linux-gnu %s -emit-llvm -mabi=ieeelongdouble -o - | FileCheck %s --check-prefix=FP128
+// RUN: %clang_cc1 -triple powerpc64le-unknown-linux-gnu %s -emit-llvm -mlong-double-64 -o - | FileCheck %s --check-prefix=DOUBLE
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -DNOLDBL %s -emit-llvm -o - | FileCheck %s --check-prefix=NOLDBL
+
+#ifndef NOLDBL
+long double foo(long double a, long double b) {
+  return a + b;
+}
+#endif
+
+int bar() { return 1; }
+
+// X86FP80-CIR: cir.long_double_format = #cir.long_double_format<x86_fp80>
+// X86FP80: ![[#]] = !{i32 1, !"long-double-type", !"x86_fp80"}
+// FP128: ![[#]] = !{i32 1, !"long-double-type", !"fp128"}
+// DOUBLE: ![[#]] = !{i32 1, !"long-double-type", !"double"}
+// PPCFP128: ![[#]] = !{i32 1, !"long-double-type", !"ppc_fp128"}
+// NOLDBL-NOT: !"long-double-type"
+// NOLDBL-CIR-NOT: cir.long_double_format
diff --git a/clang/test/CIR/IR/invalid-long-double-format.cir b/clang/test/CIR/IR/invalid-long-double-format.cir
new file mode 100644
index 0000000000000..ba7fafeae7ad4
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-long-double-format.cir
@@ -0,0 +1,42 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+// expected-error at +1 {{long double format 'float' is not supported on target 'x86_64-unknown-linux-gnu'}}
+module attributes {cir.triple = "x86_64-unknown-linux-gnu",
+    cir.long_double_format = #cir.long_double_format<float>} {
+}
+
+// -----
+
+// expected-error at +1 {{long double format 'ppc_fp128' is not supported on target 'x86_64-unknown-linux-gnu'}}
+module attributes {cir.triple = "x86_64-unknown-linux-gnu",
+    cir.long_double_format = #cir.long_double_format<ppc_fp128>} {
+}
+
+// -----
+
+// expected-error at +1 {{long double format 'x86_fp80' is not supported on target 'aarch64-unknown-linux-gnu'}}
+module attributes {cir.triple = "aarch64-unknown-linux-gnu",
+    cir.long_double_format = #cir.long_double_format<x86_fp80>} {
+}
+
+// -----
+
+module {
+  // expected-error at +1 {{expects 'cir.long_double_format' attribute to be attached to 'builtin.module'}}
+  cir.func @f() attributes {cir.long_double_format = #cir.long_double_format<double>} {
+    cir.return
+  }
+}
+
+// -----
+
+// Valid: PPC can use IEEE quad (-mabi=ieeelongdouble) and ppc_fp128.
+module attributes {cir.triple = "powerpc64le-unknown-linux-gnu",
+    cir.long_double_format = #cir.long_double_format<fp128>} {
+}
+
+// -----
+
+module attributes {cir.triple = "powerpc64le-unknown-linux-gnu",
+    cir.long_double_format = #cir.long_double_format<ppc_fp128>} {
+}
diff --git a/clang/test/CIR/Lowering/long-double-format-attr.cir b/clang/test/CIR/Lowering/long-double-format-attr.cir
new file mode 100644
index 0000000000000..eaf6e9e374539
--- /dev/null
+++ b/clang/test/CIR/Lowering/long-double-format-attr.cir
@@ -0,0 +1,7 @@
+// RUN: cir-translate %s --cir-to-llvmir -o - | FileCheck %s --check-prefix=LLVM
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu",
+    cir.long_double_format = #cir.long_double_format<double>} {
+}
+
+// LLVM: !{i32 1, !"long-double-type", !"double"}

``````````

</details>


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


More information about the cfe-commits mailing list