[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