https://github.com/adams381 updated https://github.com/llvm/llvm-project/pull/230697
>From cfc534a2edf0723f35da481f58020625c1945206 Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Fri, 9 Oct 2026 15:43:45 -0700 Subject: [PATCH 1/5] [CIR] Emit the frame-pointer function attribute and module flag CIR now honors -mframe-pointer= the way classic CodeGen does. Functions and the module carry a new #cir.frame_pointer attribute, which lowers to the "frame-pointer" function attribute and module flag. Assisted-by: Cursor / claude-opus-5.5 --- .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 45 +++++++++ .../clang/CIR/Dialect/IR/CIRDialect.td | 2 + clang/include/clang/CIR/Dialect/IR/CIROps.td | 3 +- clang/lib/CIR/CodeGen/CIRGenCall.cpp | 23 ++++- clang/lib/CIR/CodeGen/CIRGenModule.cpp | 6 ++ clang/lib/CIR/CodeGen/CIRGenModule.h | 6 ++ clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 39 ++++++-- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 25 +++++ .../Lowering/DirectToLLVM/LowerToLLVMIR.cpp | 22 +++++ clang/test/CIR/CodeGen/frame-pointer.cpp | 93 ++++++++++++++++++ clang/test/CIR/IR/func-attrs.cir | 31 ++++++ clang/test/CIR/IR/invalid-func-attr.cir | 19 ++++ .../CIR/IR/invalid-module-frame-pointer.cir | 19 ++++ clang/test/CIR/Lowering/frame-pointer.cir | 96 +++++++++++++++++++ 14 files changed, 418 insertions(+), 11 deletions(-) create mode 100644 clang/test/CIR/CodeGen/frame-pointer.cpp create mode 100644 clang/test/CIR/IR/invalid-module-frame-pointer.cir create mode 100644 clang/test/CIR/Lowering/frame-pointer.cir diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index eb4cf65b919c49..e9eac89b34ec5f 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -1454,6 +1454,51 @@ def CIR_UnwindTableKindAttr : CIR_EnumAttr<CIR_UnwindTableKind, "uwtable"> { }]; } +//===----------------------------------------------------------------------===// +// FramePointerKind +//===----------------------------------------------------------------------===// +def CIR_FramePointerKind : CIR_I32Enum<"FramePointerKind", + "Frame Pointer Kind", [ + I32EnumCase<"None", 0, "none">, + I32EnumCase<"NonLeaf", 1, "non_leaf">, + I32EnumCase<"All", 2, "all">, + I32EnumCase<"Reserved", 3, "reserved">, + I32EnumCase<"NonLeafNoReserve", 4, "non_leaf_no_reserve"> +]>; + +def CIR_FramePointerKindAttr + : CIR_EnumAttr<CIR_FramePointerKind, "frame_pointer"> { + let summary = "Encodes the frame pointer kind for a function or a module."; + + let description = [{ + This mirrors the FramePointerKind enum of the LLVM dialect. On a + `cir.func` it is the `frame_pointer` attribute and lowers to the + `frame-pointer` function attribute in LLVM IR. As the value of the + module attribute `cir.frame_pointer` it lowers to the `frame-pointer` + module flag, which LLVM gives to the functions it synthesizes. A + function or module without the attribute behaves as `none`. + + `none` lets the frame pointer be eliminated. `all` keeps the frame + pointer, and `non_leaf` keeps it if the function calls other functions. + `reserved` lets the frame pointer be eliminated but keeps its register + from being used for anything else, so the register either points to a + valid frame record for the current function or is left unmodified. + `non_leaf_no_reserve` is `non_leaf`, except that a leaf function may use + the frame pointer register as a general-purpose register. + + Example: + + ``` + module attributes {cir.frame_pointer = #cir.frame_pointer<all>} { + cir.func private @f() attributes {frame_pointer = #cir.frame_pointer<all>} + cir.func private @g() attributes { + frame_pointer = #cir.frame_pointer<non_leaf> + } + } + ``` + }]; +} + //===----------------------------------------------------------------------===// // GloblCtorAttr //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td index 2086646949a116..c8cf2870a7eb5c 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td @@ -52,6 +52,7 @@ def CIR_Dialect : Dialect { static llvm::StringRef getNoReturnAttrName() { return "noreturn"; } static llvm::StringRef getMemoryEffectsAttrName() { return "memory_effects"; } static llvm::StringRef getUwtableAttrName() { return "uwtable"; } + static llvm::StringRef getFramePointerAttrName() { return "frame_pointer"; } static llvm::StringRef getReturnsTwiceAttrName() { return "returns_twice"; } static llvm::StringRef getColdAttrName() { return "cold"; } static llvm::StringRef getHotAttrName() { return "hot"; } @@ -67,6 +68,7 @@ def CIR_Dialect : Dialect { static llvm::StringRef getNoUnwindAttrName() { return "nounwind"; } static llvm::StringRef getWillReturnAttrName() { return "willreturn"; } static llvm::StringRef getModuleLevelAsmAttrName() { return "cir.module_asm"; } + static llvm::StringRef getModuleFramePointerAttrName() { return "cir.frame_pointer"; } static llvm::StringRef getGlobalCtorsAttrName() { return "cir.global_ctors"; } static llvm::StringRef getGlobalDtorsAttrName() { return "cir.global_dtors"; } static llvm::StringRef getOffloadContainerAttrName() { return "cir.offload.container"; } diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 53f693a7863eee..4c26dcf630ecc9 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -4362,7 +4362,8 @@ def CIR_FuncOp : CIR_Op<"func", [ CIR_OptionalPriorityAttr:$global_dtor_priority, OptionalAttr<CIR_FuncInfoAttr>:$func_info, OptionalAttr<CIR_AnnotationArrayAttr>:$annotations, - OptionalAttr<CIR_UnwindTableKindAttr>:$uwtable + OptionalAttr<CIR_UnwindTableKindAttr>:$uwtable, + OptionalAttr<CIR_FramePointerKindAttr>:$frame_pointer ); let regions = (region AnyRegion:$body); diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index ec4dde920a2707..6499f3dcceab5c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -204,6 +204,23 @@ getZeroCallUsedRegsKindStr(llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind k) { llvm_unreachable("Unknown kind?"); } +std::optional<cir::FramePointerKind> +CIRGenModule::getFramePointerKind(CodeGenOptions::FramePointerKind kind) { + switch (kind) { + case CodeGenOptions::FramePointerKind::None: + return std::nullopt; + case CodeGenOptions::FramePointerKind::Reserved: + return cir::FramePointerKind::Reserved; + case CodeGenOptions::FramePointerKind::NonLeafNoReserve: + return cir::FramePointerKind::NonLeafNoReserve; + case CodeGenOptions::FramePointerKind::NonLeaf: + return cir::FramePointerKind::NonLeaf; + case CodeGenOptions::FramePointerKind::All: + return cir::FramePointerKind::All; + } + llvm_unreachable("Unknown frame pointer kind"); +} + /// Add default attributes to a function, which have merge semantics under /// -mlink-builtin-bitcode and should not simply overwrite any existing /// attributes in the linked library. @@ -235,7 +252,11 @@ static void addTrivialDefaultFunctionAttributes( attrs.set(cir::CIRDialect::getTrapFuncNameAttrName(), mlir::StringAttr::get(mlirCtx, codeGenOpts.TrapFuncName)); } else { - // TODO(cir): Set frame pointer attribute here. + if (std::optional<cir::FramePointerKind> kind = + CIRGenModule::getFramePointerKind(codeGenOpts.getFramePointer())) + attrs.set(cir::CIRDialect::getFramePointerAttrName(), + cir::FramePointerKindAttr::get(mlirCtx, *kind)); + // TODO(cir): a number of other attribute 1-offs based on codegen/lang opts // should be done here: less-recise-fpmad null-pointer-is-valid // no-trapping-math diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 1f80f4e0ebf085..f4074aecb051ca 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -3923,6 +3923,12 @@ void CIRGenModule::release() { emitGlobalAnnotations(); + if (std::optional<cir::FramePointerKind> kind = + getFramePointerKind(codeGenOpts.getFramePointer())) + theModule->setAttr( + cir::CIRDialect::getModuleFramePointerAttrName(), + cir::FramePointerKindAttr::get(&getMLIRContext(), *kind)); + if (!recordLayoutEntries.empty()) theModule->setAttr( cir::CIRDialect::getRecordLayoutsAttrName(), diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 05ba8b5ee212e0..ed5b7409eaa122 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -385,6 +385,12 @@ class CIRGenModule : public CIRGenTypeCache { bool attrOnCallSite, mlir::NamedAttrList &attrs); + /// Returns the frame pointer kind to record on functions and on the module, + /// or std::nullopt when \p kind is CodeGenOptions::FramePointerKind::None, + /// the default, which is left implicit. + static std::optional<cir::FramePointerKind> + getFramePointerKind(CodeGenOptions::FramePointerKind kind); + /// Will return a global variable of the given type. If a variable with a /// different type already exists then a new variable with the right type /// will be created and all uses of the old variable will be replaced with a diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 8a62ae541b4b6e..96c6c05e0a11af 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -272,6 +272,16 @@ cir::CIRDialect::verifyOperationAttribute(mlir::Operation *op, << "' attribute to be attached to '" << mlir::ModuleOp::getOperationName() << "'"; + if (attrName == getModuleFramePointerAttrName()) { + if (!mlir::isa<mlir::ModuleOp>(op)) + return op->emitOpError() << "expects '" << getModuleFramePointerAttrName() + << "' attribute to be attached to '" + << mlir::ModuleOp::getOperationName() << "'"; + if (!mlir::isa<cir::FramePointerKindAttr>(attr.getValue())) + return op->emitOpError() << "expects '" << getModuleFramePointerAttrName() + << "' to be a #cir.frame_pointer attribute"; + } + // LoweringPrepare uses this attribute directly as the fatbin global's // initializer, so it must be a valid #cir.const_array payload for its type. if (attrName == getCUDADeviceBinaryAttrName()) { @@ -1371,9 +1381,10 @@ parseTryCallDestinations(mlir::OpAsmParser &parser, return mlir::success(); } -/// Reject an effect attribute of the wrong kind in an explicit attribute -/// dictionary. Where these are declared they are stored as properties, so a -/// value of the wrong kind would be dropped without a diagnostic. +/// Reject an attribute in an explicit attribute dictionary whose value is not +/// the attribute class expected for its name. Where an op declares the +/// attribute it is stored as a property, so a value of the wrong class would +/// be dropped without a diagnostic. static ParseResult checkEffectAttrKinds(mlir::OpAsmParser &parser, llvm::SMLoc loc, const mlir::NamedAttrList &attrs) { @@ -1390,6 +1401,13 @@ static ParseResult checkEffectAttrKinds(mlir::OpAsmParser &parser, << CIRDialect::getUwtableAttrName() << "' must be a #cir.uwtable attribute"; + if (mlir::Attribute framePointer = + attrs.get(CIRDialect::getFramePointerAttrName())) + if (!mlir::isa<cir::FramePointerKindAttr>(framePointer)) + return parser.emitError(loc, "attribute '") + << CIRDialect::getFramePointerAttrName() + << "' must be a #cir.frame_pointer attribute"; + for (llvm::StringRef name : {CIRDialect::getNoUnwindAttrName(), CIRDialect::getWillReturnAttrName(), CIRDialect::getMustProgressAttrName()}) @@ -3098,11 +3116,13 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) { return failure(); // Every other declared attribute has dedicated syntax above, so - // memory_effects and uwtable is the only one the explicit list may carry. - // Without the exception cir.func could not parse back what it prints. + // memory_effects, uwtable and frame_pointer are the only ones the explicit + // list may carry. Without these exceptions cir.func could not parse back + // what it prints. for (StringRef disallowed : cir::FuncOp::getAttributeNames()) { if (disallowed == CIRDialect::getMemoryEffectsAttrName() || - disallowed == CIRDialect::getUwtableAttrName()) + disallowed == CIRDialect::getUwtableAttrName() || + disallowed == CIRDialect::getFramePointerAttrName()) continue; if (parsedAttrs.get(disallowed)) return parser.emitError(loc, "attribute '") @@ -3295,12 +3315,13 @@ void cir::FuncOp::print(OpAsmPrinter &p) { } // Every declared attribute is printed by the syntax above, except - // memory_effects and uwtable, which have none and so must reach the - // dictionary. + // memory_effects, uwtable and frame_pointer, which have none and so must + // reach the dictionary. llvm::SmallVector<llvm::StringRef> elidedAttrs; for (llvm::StringRef name : cir::FuncOp::getAttributeNames()) if (name != CIRDialect::getMemoryEffectsAttrName() && - name != CIRDialect::getUwtableAttrName()) + name != CIRDialect::getUwtableAttrName() && + name != CIRDialect::getFramePointerAttrName()) elidedAttrs.push_back(name); function_interface_impl::printFunctionAttributes(p, *this, elidedAttrs); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index d919ed2e98a5b7..11679fbe530917 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -305,6 +305,26 @@ convertUWTableKind(cir::UnwindTableKind kind) { llvm_unreachable("Unknown CIR unwind table kind"); } +static mlir::LLVM::framePointerKind::FramePointerKind +convertFramePointerKind(cir::FramePointerKind kind) { + using CIR = cir::FramePointerKind; + using LLVM = mlir::LLVM::framePointerKind::FramePointerKind; + + switch (kind) { + case CIR::None: + return LLVM::None; + case CIR::NonLeaf: + return LLVM::NonLeaf; + case CIR::All: + return LLVM::All; + case CIR::Reserved: + return LLVM::Reserved; + case CIR::NonLeafNoReserve: + return LLVM::NonLeafNoReserve; + } + llvm_unreachable("Unknown CIR frame pointer kind"); +} + mlir::LogicalResult CIRToLLVMCopyOpLowering::matchAndRewrite( cir::CopyOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { @@ -2933,6 +2953,11 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite( fn.setUwtableKindAttr(mlir::LLVM::UWTableKindAttr::get( fn.getContext(), convertUWTableKind(*uwtableKind))); + if (std::optional<cir::FramePointerKind> framePointerKind = + op.getFramePointer()) + fn.setFramePointerAttr(mlir::LLVM::FramePointerKindAttr::get( + fn.getContext(), convertFramePointerKind(*framePointerKind))); + // Function attributes with no dedicated field on the LLVM dialect's // LLVMFuncOp are routed through the `passthrough` array. The MLIR LLVM IR // translator forwards `passthrough` entries to LLVM IR as function diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp index 213f80f0defb01..71f59bafdb7c92 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp @@ -27,6 +27,23 @@ using namespace llvm; namespace cir { namespace direct { +static llvm::FramePointerKind +convertFramePointerKind(cir::FramePointerKind kind) { + switch (kind) { + case cir::FramePointerKind::None: + return llvm::FramePointerKind::None; + case cir::FramePointerKind::NonLeaf: + return llvm::FramePointerKind::NonLeaf; + case cir::FramePointerKind::All: + return llvm::FramePointerKind::All; + case cir::FramePointerKind::Reserved: + return llvm::FramePointerKind::Reserved; + case cir::FramePointerKind::NonLeafNoReserve: + return llvm::FramePointerKind::NonLeafNoReserve; + } + llvm_unreachable("Unknown CIR frame pointer kind"); +} + /// Implementation of the dialect interface that converts CIR attributes to LLVM /// IR metadata. class CIRDialectLLVMIRTranslationInterface @@ -135,6 +152,11 @@ class CIRDialectLLVMIRTranslationInterface llvm::Module *llvmModule = moduleTranslation.getLLVMModule(); llvm::LLVMContext &llvmContext = llvmModule->getContext(); + if (attribute.getName() == cir::CIRDialect::getModuleFramePointerAttrName()) + llvmModule->setFramePointer(convertFramePointerKind( + mlir::cast<cir::FramePointerKindAttr>(attribute.getValue()) + .getValue())); + if (attribute.getName() == "cir.amdhsa_code_object_version") { if (auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>(attribute.getValue())) { diff --git a/clang/test/CIR/CodeGen/frame-pointer.cpp b/clang/test/CIR/CodeGen/frame-pointer.cpp new file mode 100644 index 00000000000000..a99ac0c20e309b --- /dev/null +++ b/clang/test/CIR/CodeGen/frame-pointer.cpp @@ -0,0 +1,93 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t-default.cir +// RUN: FileCheck --input-file=%t-default.cir %s --check-prefix=CIR-NONE +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-default-cir.ll +// RUN: FileCheck --input-file=%t-default-cir.ll %s --check-prefix=LLVM-NONE +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t-default.ll +// RUN: FileCheck --input-file=%t-default.ll %s --check-prefix=LLVM-NONE + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mframe-pointer=none -fclangir -emit-cir %s -o %t-none.cir +// RUN: FileCheck --input-file=%t-none.cir %s --check-prefix=CIR-NONE +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mframe-pointer=none -fclangir -emit-llvm %s -o %t-none-cir.ll +// RUN: FileCheck --input-file=%t-none-cir.ll %s --check-prefix=LLVM-NONE +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mframe-pointer=none -emit-llvm %s -o %t-none.ll +// RUN: FileCheck --input-file=%t-none.ll %s --check-prefix=LLVM-NONE + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mframe-pointer=non-leaf -fclangir -emit-cir %s -o %t-non-leaf.cir +// RUN: FileCheck --input-file=%t-non-leaf.cir %s --check-prefix=CIR-NON-LEAF +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mframe-pointer=non-leaf -fclangir -emit-llvm %s -o %t-non-leaf-cir.ll +// RUN: FileCheck --input-file=%t-non-leaf-cir.ll %s --check-prefix=LLVM-NON-LEAF +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mframe-pointer=non-leaf -emit-llvm %s -o %t-non-leaf.ll +// RUN: FileCheck --input-file=%t-non-leaf.ll %s --check-prefix=LLVM-NON-LEAF + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mframe-pointer=all -fclangir -emit-cir %s -o %t-all.cir +// RUN: FileCheck --input-file=%t-all.cir %s --check-prefix=CIR-ALL +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mframe-pointer=all -fclangir -emit-llvm %s -o %t-all-cir.ll +// RUN: FileCheck --input-file=%t-all-cir.ll %s --check-prefix=LLVM-ALL +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mframe-pointer=all -emit-llvm %s -o %t-all.ll +// RUN: FileCheck --input-file=%t-all.ll %s --check-prefix=LLVM-ALL + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mframe-pointer=reserved -fclangir -emit-cir %s -o %t-reserved.cir +// RUN: FileCheck --input-file=%t-reserved.cir %s --check-prefix=CIR-RESERVED +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mframe-pointer=reserved -fclangir -emit-llvm %s -o %t-reserved-cir.ll +// RUN: FileCheck --input-file=%t-reserved-cir.ll %s --check-prefix=LLVM-RESERVED +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mframe-pointer=reserved -emit-llvm %s -o %t-reserved.ll +// RUN: FileCheck --input-file=%t-reserved.ll %s --check-prefix=LLVM-RESERVED + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mframe-pointer=non-leaf-no-reserve -fclangir -emit-cir %s -o %t-nlnr.cir +// RUN: FileCheck --input-file=%t-nlnr.cir %s --check-prefix=CIR-NLNR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mframe-pointer=non-leaf-no-reserve -fclangir -emit-llvm %s -o %t-nlnr-cir.ll +// RUN: FileCheck --input-file=%t-nlnr-cir.ll %s --check-prefix=LLVM-NLNR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mframe-pointer=non-leaf-no-reserve -emit-llvm %s -o %t-nlnr.ll +// RUN: FileCheck --input-file=%t-nlnr.ll %s --check-prefix=LLVM-NLNR + +void declaration(); +void definition() { declaration(); } + +// CIR-NONE-NOT: frame_pointer +// CIR-NONE: cir.func {{.*}}@_Z10definitionv() +// CIR-NONE-NOT: frame_pointer + +// CIR-NON-LEAF: module {{.*}}attributes {{.*}}cir.frame_pointer = #cir.frame_pointer<non_leaf> +// CIR-NON-LEAF: cir.func {{.*}}@_Z10definitionv() attributes {{.*}}frame_pointer = #cir.frame_pointer<non_leaf> +// CIR-NON-LEAF: cir.func private @_Z11declarationv() attributes {{.*}}frame_pointer = #cir.frame_pointer<non_leaf> + +// CIR-ALL: module {{.*}}attributes {{.*}}cir.frame_pointer = #cir.frame_pointer<all> +// CIR-ALL: cir.func {{.*}}@_Z10definitionv() attributes {{.*}}frame_pointer = #cir.frame_pointer<all> +// CIR-ALL: cir.call @_Z11declarationv() : () -> () loc( +// CIR-ALL: cir.func private @_Z11declarationv() attributes {{.*}}frame_pointer = #cir.frame_pointer<all> + +// CIR-RESERVED: module {{.*}}attributes {{.*}}cir.frame_pointer = #cir.frame_pointer<reserved> +// CIR-RESERVED: cir.func {{.*}}@_Z10definitionv() attributes {{.*}}frame_pointer = #cir.frame_pointer<reserved> +// CIR-RESERVED: cir.func private @_Z11declarationv() attributes {{.*}}frame_pointer = #cir.frame_pointer<reserved> + +// CIR-NLNR: module {{.*}}attributes {{.*}}cir.frame_pointer = #cir.frame_pointer<non_leaf_no_reserve> +// CIR-NLNR: cir.func {{.*}}@_Z10definitionv() attributes {{.*}}frame_pointer = #cir.frame_pointer<non_leaf_no_reserve> +// CIR-NLNR: cir.func private @_Z11declarationv() attributes {{.*}}frame_pointer = #cir.frame_pointer<non_leaf_no_reserve> + +// LLVM-NONE: define {{.*}}void @_Z10definitionv() +// LLVM-NONE-NOT: "frame-pointer" + +// LLVM-NON-LEAF: define {{.*}}void @_Z10definitionv() #[[DEF_ATTR:[0-9]+]] +// LLVM-NON-LEAF: declare void @_Z11declarationv() #[[DECL_ATTR:[0-9]+]] +// LLVM-NON-LEAF: attributes #[[DEF_ATTR]] = {{.*}}"frame-pointer"="non-leaf" +// LLVM-NON-LEAF: attributes #[[DECL_ATTR]] = {{.*}}"frame-pointer"="non-leaf" +// LLVM-NON-LEAF: !{i32 7, !"frame-pointer", i32 1} + +// LLVM-ALL: define {{.*}}void @_Z10definitionv() #[[DEF_ATTR:[0-9]+]] +// LLVM-ALL: call void @_Z11declarationv(){{$}} +// LLVM-ALL: declare void @_Z11declarationv() #[[DECL_ATTR:[0-9]+]] +// LLVM-ALL: attributes #[[DEF_ATTR]] = {{.*}}"frame-pointer"="all" +// LLVM-ALL: attributes #[[DECL_ATTR]] = {{.*}}"frame-pointer"="all" +// LLVM-ALL: !{i32 7, !"frame-pointer", i32 2} + +// LLVM-RESERVED: define {{.*}}void @_Z10definitionv() #[[DEF_ATTR:[0-9]+]] +// LLVM-RESERVED: declare void @_Z11declarationv() #[[DECL_ATTR:[0-9]+]] +// LLVM-RESERVED: attributes #[[DEF_ATTR]] = {{.*}}"frame-pointer"="reserved" +// LLVM-RESERVED: attributes #[[DECL_ATTR]] = {{.*}}"frame-pointer"="reserved" +// LLVM-RESERVED: !{i32 7, !"frame-pointer", i32 3} + +// LLVM-NLNR: define {{.*}}void @_Z10definitionv() #[[DEF_ATTR:[0-9]+]] +// LLVM-NLNR: declare void @_Z11declarationv() #[[DECL_ATTR:[0-9]+]] +// LLVM-NLNR: attributes #[[DEF_ATTR]] = {{.*}}"frame-pointer"="non-leaf-no-reserve" +// LLVM-NLNR: attributes #[[DECL_ATTR]] = {{.*}}"frame-pointer"="non-leaf-no-reserve" +// LLVM-NLNR: !{i32 7, !"frame-pointer", i32 4} diff --git a/clang/test/CIR/IR/func-attrs.cir b/clang/test/CIR/IR/func-attrs.cir index fd51e1cee6f4ae..aa781f6e8f604f 100644 --- a/clang/test/CIR/IR/func-attrs.cir +++ b/clang/test/CIR/IR/func-attrs.cir @@ -126,3 +126,34 @@ cir.func no_inline dso_local @uwtableWithOthers() attributes {mustprogress, uwta // CHECK: cir.func no_inline dso_local @uwtableWithOthers() attributes {mustprogress, uwtable = #cir.uwtable<async>} { // CHECK: cir.return // CHECK: } + +cir.func @noneFramePointer() attributes {frame_pointer = #cir.frame_pointer<none>} { + cir.return +} +// CHECK: cir.func @noneFramePointer() attributes {frame_pointer = #cir.frame_pointer<none>} { +// CHECK: cir.return +// CHECK: } + +cir.func @allFramePointer() attributes {frame_pointer = #cir.frame_pointer<all>} { + cir.return +} +// CHECK: cir.func @allFramePointer() attributes {frame_pointer = #cir.frame_pointer<all>} { +// CHECK: cir.return +// CHECK: } + +cir.func @nonLeafNoReserveFramePointer() attributes {frame_pointer = #cir.frame_pointer<non_leaf_no_reserve>} { + cir.return +} +// CHECK: cir.func @nonLeafNoReserveFramePointer() attributes {frame_pointer = #cir.frame_pointer<non_leaf_no_reserve>} { +// CHECK: cir.return +// CHECK: } + +cir.func private @framePointerDecl() attributes {frame_pointer = #cir.frame_pointer<non_leaf>} +// CHECK: cir.func private @framePointerDecl() attributes {frame_pointer = #cir.frame_pointer<non_leaf>} + +cir.func no_inline dso_local @framePointerWithOthers() attributes {mustprogress, frame_pointer = #cir.frame_pointer<reserved>, uwtable = #cir.uwtable<async>} { + cir.return +} +// CHECK: cir.func no_inline dso_local @framePointerWithOthers() attributes {mustprogress, frame_pointer = #cir.frame_pointer<reserved>, uwtable = #cir.uwtable<async>} { +// CHECK: cir.return +// CHECK: } diff --git a/clang/test/CIR/IR/invalid-func-attr.cir b/clang/test/CIR/IR/invalid-func-attr.cir index 81054bda03db98..ac1ea2bc64c96d 100644 --- a/clang/test/CIR/IR/invalid-func-attr.cir +++ b/clang/test/CIR/IR/invalid-func-attr.cir @@ -37,3 +37,22 @@ module { cir.return } } + +// ----- + +module { + // expected-error@+1 {{attribute 'frame_pointer' must be a #cir.frame_pointer attribute}} + cir.func @badFramePointerKind() attributes {frame_pointer = "all"} { + cir.return + } +} + +// ----- + +module { + // expected-error@+2 {{expected one of [none, non_leaf, all, reserved, non_leaf_no_reserve] for Frame Pointer Kind, got: bogus}} + // expected-error@+1 {{failed to parse CIR_FramePointerKindAttr parameter 'value' which is to be a `::cir::FramePointerKind`}} + cir.func @badFramePointerSpelling() attributes {frame_pointer = #cir.frame_pointer<bogus>} { + cir.return + } +} diff --git a/clang/test/CIR/IR/invalid-module-frame-pointer.cir b/clang/test/CIR/IR/invalid-module-frame-pointer.cir new file mode 100644 index 00000000000000..9b5bc18fa18374 --- /dev/null +++ b/clang/test/CIR/IR/invalid-module-frame-pointer.cir @@ -0,0 +1,19 @@ +// RUN: cir-opt %s -verify-diagnostics -split-input-file + +module attributes {cir.frame_pointer = #cir.frame_pointer<all>} { +} + +// ----- + +// expected-error@+1 {{expects 'cir.frame_pointer' to be a #cir.frame_pointer attribute}} +module attributes {cir.frame_pointer = 2 : i32} { +} + +// ----- + +module { + // expected-error@+1 {{expects 'cir.frame_pointer' attribute to be attached to 'builtin.module'}} + cir.func @f() attributes {cir.frame_pointer = #cir.frame_pointer<all>} { + cir.return + } +} diff --git a/clang/test/CIR/Lowering/frame-pointer.cir b/clang/test/CIR/Lowering/frame-pointer.cir new file mode 100644 index 00000000000000..ca3431094212f7 --- /dev/null +++ b/clang/test/CIR/Lowering/frame-pointer.cir @@ -0,0 +1,96 @@ +// RUN: cir-opt %s -cir-to-llvm --split-input-file -o - | FileCheck %s -check-prefix=MLIR +// RUN: cir-translate %s -cir-to-llvmir --disable-cc-lowering --split-input-file | FileCheck %s -check-prefix=LLVM + +module attributes {cir.triple = "x86_64-unknown-linux-gnu"} { + cir.func @fp_none() attributes {frame_pointer = #cir.frame_pointer<none>} { + cir.return + } + cir.func @fp_non_leaf() attributes {frame_pointer = #cir.frame_pointer<non_leaf>} { + cir.return + } + cir.func @fp_all() attributes {frame_pointer = #cir.frame_pointer<all>} { + cir.return + } + cir.func @fp_reserved() attributes {frame_pointer = #cir.frame_pointer<reserved>} { + cir.return + } + cir.func @fp_non_leaf_no_reserve() attributes {frame_pointer = #cir.frame_pointer<non_leaf_no_reserve>} { + cir.return + } + cir.func private @fp_decl() attributes {frame_pointer = #cir.frame_pointer<all>} + cir.func @fp_absent() { + cir.return + } +} + +// MLIR: llvm.func @fp_none() attributes {frame_pointer = #llvm.framePointerKind<none> +// MLIR: llvm.func @fp_non_leaf() attributes {frame_pointer = #llvm.framePointerKind<"non-leaf"> +// MLIR: llvm.func @fp_all() attributes {frame_pointer = #llvm.framePointerKind<all> +// MLIR: llvm.func @fp_reserved() attributes {frame_pointer = #llvm.framePointerKind<reserved> +// MLIR: llvm.func @fp_non_leaf_no_reserve() attributes {frame_pointer = #llvm.framePointerKind<"non-leaf-no-reserve"> +// MLIR: llvm.func @fp_decl() attributes {frame_pointer = #llvm.framePointerKind<all> +// MLIR: llvm.func @fp_absent() +// MLIR-NOT: frame_pointer +// MLIR: module attributes {cir.frame_pointer = #cir.frame_pointer<none> +// MLIR: module attributes {cir.frame_pointer = #cir.frame_pointer<non_leaf> +// MLIR: module attributes {cir.frame_pointer = #cir.frame_pointer<all> +// MLIR: module attributes {cir.frame_pointer = #cir.frame_pointer<reserved> +// MLIR: module attributes {cir.frame_pointer = #cir.frame_pointer<non_leaf_no_reserve> + +// LLVM: define void @fp_none() #[[NONE:[0-9]+]] +// LLVM: define void @fp_non_leaf() #[[NON_LEAF:[0-9]+]] +// LLVM: define void @fp_all() #[[ALL:[0-9]+]] +// LLVM: define void @fp_reserved() #[[RESERVED:[0-9]+]] +// LLVM: define void @fp_non_leaf_no_reserve() #[[NLNR:[0-9]+]] +// LLVM: declare void @fp_decl() #[[ALL]] +// LLVM: define void @fp_absent() { +// LLVM: attributes #[[NONE]] = { "frame-pointer"="none" } +// LLVM: attributes #[[NON_LEAF]] = { "frame-pointer"="non-leaf" } +// LLVM: attributes #[[ALL]] = { "frame-pointer"="all" } +// LLVM: attributes #[[RESERVED]] = { "frame-pointer"="reserved" } +// LLVM: attributes #[[NLNR]] = { "frame-pointer"="non-leaf-no-reserve" } +// LLVM-NOT: !"frame-pointer" +// LLVM: ; ModuleID + +// ----- + +module attributes {cir.triple = "x86_64-unknown-linux-gnu", cir.frame_pointer = #cir.frame_pointer<none>} { +} + +// LLVM: !{i32 7, !"frame-pointer", i32 0} +// LLVM-NOT: !"frame-pointer" +// LLVM: ; ModuleID + +// ----- + +module attributes {cir.triple = "x86_64-unknown-linux-gnu", cir.frame_pointer = #cir.frame_pointer<non_leaf>} { +} + +// LLVM: !{i32 7, !"frame-pointer", i32 1} +// LLVM-NOT: !"frame-pointer" +// LLVM: ; ModuleID + +// ----- + +module attributes {cir.triple = "x86_64-unknown-linux-gnu", cir.frame_pointer = #cir.frame_pointer<all>} { +} + +// LLVM: !{i32 7, !"frame-pointer", i32 2} +// LLVM-NOT: !"frame-pointer" +// LLVM: ; ModuleID + +// ----- + +module attributes {cir.triple = "x86_64-unknown-linux-gnu", cir.frame_pointer = #cir.frame_pointer<reserved>} { +} + +// LLVM: !{i32 7, !"frame-pointer", i32 3} +// LLVM-NOT: !"frame-pointer" +// LLVM: ; ModuleID + +// ----- + +module attributes {cir.triple = "x86_64-unknown-linux-gnu", cir.frame_pointer = #cir.frame_pointer<non_leaf_no_reserve>} { +} + +// LLVM: !{i32 7, !"frame-pointer", i32 4} >From 1164fc45242eaba0f8dc3fcdf3d0ec9664915b53 Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Mon, 12 Oct 2026 09:17:12 -0700 Subject: [PATCH 2/5] [CIR] Address reviewer feedback Assisted-by: Cursor / claude-opus-5.5 --- .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 38 +++++++++++-------- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 13 +++++++ .../Lowering/DirectToLLVM/LowerToLLVMIR.cpp | 22 ----------- clang/test/CIR/Lowering/frame-pointer.cir | 6 +++ 4 files changed, 41 insertions(+), 38 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index e9eac89b34ec5f..ff66c4db41c43e 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -1471,29 +1471,35 @@ def CIR_FramePointerKindAttr let summary = "Encodes the frame pointer kind for a function or a module."; let description = [{ - This mirrors the FramePointerKind enum of the LLVM dialect. On a - `cir.func` it is the `frame_pointer` attribute and lowers to the - `frame-pointer` function attribute in LLVM IR. As the value of the - module attribute `cir.frame_pointer` it lowers to the `frame-pointer` - module flag, which LLVM gives to the functions it synthesizes. A + A frame pointer is a register that holds the address of the current + function's frame record, so that debuggers, profilers and stack + unwinders can walk the call stack by following the chain of frame + records. Eliminating it frees the register for other uses. The frame + pointer kind says which functions must keep one: + + - `none`: the frame pointer may be eliminated and its register used for + other purposes. + - `non_leaf`: functions that call other functions keep the frame + pointer. A leaf function may eliminate it, but may not use its + register for anything else. + - `all`: every function keeps the frame pointer. + - `reserved`: the frame pointer may be eliminated, but its register is + not used for anything else, so it either points to a valid frame + record for the current function or is left unmodified. + - `non_leaf_no_reserve`: like `non_leaf`, except that a leaf function + may use the frame pointer register as a general-purpose register. + + The kind is a minimum: the code generator may keep a frame pointer that + the kind allows it to eliminate. On a function the attribute applies + to that function. On the module it applies to the functions LLVM + creates after CIR is lowered, such as sanitizer constructors. A function or module without the attribute behaves as `none`. - `none` lets the frame pointer be eliminated. `all` keeps the frame - pointer, and `non_leaf` keeps it if the function calls other functions. - `reserved` lets the frame pointer be eliminated but keeps its register - from being used for anything else, so the register either points to a - valid frame record for the current function or is left unmodified. - `non_leaf_no_reserve` is `non_leaf`, except that a leaf function may use - the frame pointer register as a general-purpose register. - Example: ``` module attributes {cir.frame_pointer = #cir.frame_pointer<all>} { cir.func private @f() attributes {frame_pointer = #cir.frame_pointer<all>} - cir.func private @g() attributes { - frame_pointer = #cir.frame_pointer<non_leaf> - } } ``` }]; diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 11679fbe530917..719c364533195c 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -4564,6 +4564,19 @@ void ConvertCIRToLLVMPass::processCIRAttrs(mlir::ModuleOp module) { module->setAttr(mlir::LLVM::LLVMDialect::getModuleLevelAsmAttrName(), asmAttr); + if (auto framePointerAttr = module->getAttrOfType<cir::FramePointerKindAttr>( + cir::CIRDialect::getModuleFramePointerAttrName())) { + mlir::OpBuilder builder(module.getContext()); + builder.setInsertionPointToEnd(module.getBody()); + auto flag = mlir::LLVM::ModuleFlagAttr::get( + module.getContext(), mlir::LLVM::ModFlagBehavior::Max, + builder.getStringAttr("frame-pointer"), + builder.getI32IntegerAttr(static_cast<int32_t>( + convertFramePointerKind(framePointerAttr.getValue())))); + mlir::LLVM::ModuleFlagsOp::create(builder, module.getLoc(), + builder.getArrayAttr({flag})); + } + lowerOpenCLModuleMetadataAttrs(module); } diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp index 71f59bafdb7c92..213f80f0defb01 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp @@ -27,23 +27,6 @@ using namespace llvm; namespace cir { namespace direct { -static llvm::FramePointerKind -convertFramePointerKind(cir::FramePointerKind kind) { - switch (kind) { - case cir::FramePointerKind::None: - return llvm::FramePointerKind::None; - case cir::FramePointerKind::NonLeaf: - return llvm::FramePointerKind::NonLeaf; - case cir::FramePointerKind::All: - return llvm::FramePointerKind::All; - case cir::FramePointerKind::Reserved: - return llvm::FramePointerKind::Reserved; - case cir::FramePointerKind::NonLeafNoReserve: - return llvm::FramePointerKind::NonLeafNoReserve; - } - llvm_unreachable("Unknown CIR frame pointer kind"); -} - /// Implementation of the dialect interface that converts CIR attributes to LLVM /// IR metadata. class CIRDialectLLVMIRTranslationInterface @@ -152,11 +135,6 @@ class CIRDialectLLVMIRTranslationInterface llvm::Module *llvmModule = moduleTranslation.getLLVMModule(); llvm::LLVMContext &llvmContext = llvmModule->getContext(); - if (attribute.getName() == cir::CIRDialect::getModuleFramePointerAttrName()) - llvmModule->setFramePointer(convertFramePointerKind( - mlir::cast<cir::FramePointerKindAttr>(attribute.getValue()) - .getValue())); - if (attribute.getName() == "cir.amdhsa_code_object_version") { if (auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>(attribute.getValue())) { diff --git a/clang/test/CIR/Lowering/frame-pointer.cir b/clang/test/CIR/Lowering/frame-pointer.cir index ca3431094212f7..d95a53e946fe5c 100644 --- a/clang/test/CIR/Lowering/frame-pointer.cir +++ b/clang/test/CIR/Lowering/frame-pointer.cir @@ -31,11 +31,17 @@ module attributes {cir.triple = "x86_64-unknown-linux-gnu"} { // MLIR: llvm.func @fp_decl() attributes {frame_pointer = #llvm.framePointerKind<all> // MLIR: llvm.func @fp_absent() // MLIR-NOT: frame_pointer +// MLIR-NOT: module_flags // MLIR: module attributes {cir.frame_pointer = #cir.frame_pointer<none> +// MLIR-NEXT: llvm.module_flags [#llvm.mlir.module_flag<max, "frame-pointer", 0 : i32>] // MLIR: module attributes {cir.frame_pointer = #cir.frame_pointer<non_leaf> +// MLIR-NEXT: llvm.module_flags [#llvm.mlir.module_flag<max, "frame-pointer", 1 : i32>] // MLIR: module attributes {cir.frame_pointer = #cir.frame_pointer<all> +// MLIR-NEXT: llvm.module_flags [#llvm.mlir.module_flag<max, "frame-pointer", 2 : i32>] // MLIR: module attributes {cir.frame_pointer = #cir.frame_pointer<reserved> +// MLIR-NEXT: llvm.module_flags [#llvm.mlir.module_flag<max, "frame-pointer", 3 : i32>] // MLIR: module attributes {cir.frame_pointer = #cir.frame_pointer<non_leaf_no_reserve> +// MLIR-NEXT: llvm.module_flags [#llvm.mlir.module_flag<max, "frame-pointer", 4 : i32>] // LLVM: define void @fp_none() #[[NONE:[0-9]+]] // LLVM: define void @fp_non_leaf() #[[NON_LEAF:[0-9]+]] >From bcd3e6f5ab17bc8b5d1bd7040cf61efdd938888c Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Tue, 13 Oct 2026 07:56:03 -0700 Subject: [PATCH 3/5] [CIR] Address reviewer feedback Assisted-by: Cursor / claude-opus-5.5 --- .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index ff66c4db41c43e..83fb881e76b652 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -1471,29 +1471,26 @@ def CIR_FramePointerKindAttr let summary = "Encodes the frame pointer kind for a function or a module."; let description = [{ - A frame pointer is a register that holds the address of the current - function's frame record, so that debuggers, profilers and stack - unwinders can walk the call stack by following the chain of frame - records. Eliminating it frees the register for other uses. The frame - pointer kind says which functions must keep one: - - - `none`: the frame pointer may be eliminated and its register used for - other purposes. - - `non_leaf`: functions that call other functions keep the frame - pointer. A leaf function may eliminate it, but may not use its - register for anything else. - - `all`: every function keeps the frame pointer. - - `reserved`: the frame pointer may be eliminated, but its register is - not used for anything else, so it either points to a valid frame - record for the current function or is left unmodified. - - `non_leaf_no_reserve`: like `non_leaf`, except that a leaf function - may use the frame pointer register as a general-purpose register. - - The kind is a minimum: the code generator may keep a frame pointer that - the kind allows it to eliminate. On a function the attribute applies - to that function. On the module it applies to the functions LLVM - creates after CIR is lowered, such as sanitizer constructors. A - function or module without the attribute behaves as `none`. + Mirrors the `frame-pointer` function attribute and module flag of LLVM + IR (https://llvm.org/docs/LangRef.html#function-attributes), which tell + the code generator whether a function should keep its frame pointer. The + code generator may keep a frame pointer even when the kind says it can + be eliminated. + + - `none`: the frame pointer can be eliminated, and its register can be + used for other purposes. + - `reserved`: the frame pointer register must either be updated to point + to a valid frame record for the current function, or not be modified. + - `non_leaf`: the frame pointer should be kept if the function calls + other functions. + - `non_leaf_no_reserve`: like `non_leaf`, but a leaf function may use + the frame pointer register for other purposes. + - `all`: the frame pointer should be kept. + + On a function, the attribute applies to that function. On the module, + it becomes the `frame-pointer` module flag, which LLVM applies to the + functions it creates after CIR is lowered, such as sanitizer + constructors. Without the attribute, the kind is `none`. Example: >From 9116751c78658a71b788c5491702d964931178bc Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Tue, 13 Oct 2026 10:45:26 -0500 Subject: [PATCH 4/5] Update clang/include/clang/CIR/Dialect/IR/CIRAttrs.td Co-authored-by: Henrich Lauko <[email protected]> --- clang/include/clang/CIR/Dialect/IR/CIRAttrs.td | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 83fb881e76b652..526b108b3438fb 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -1458,7 +1458,7 @@ def CIR_UnwindTableKindAttr : CIR_EnumAttr<CIR_UnwindTableKind, "uwtable"> { // FramePointerKind //===----------------------------------------------------------------------===// def CIR_FramePointerKind : CIR_I32Enum<"FramePointerKind", - "Frame Pointer Kind", [ + "frame pointer kind", [ I32EnumCase<"None", 0, "none">, I32EnumCase<"NonLeaf", 1, "non_leaf">, I32EnumCase<"All", 2, "all">, >From 54143aeb03f35a02d25847a260f01057c4da79d8 Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Tue, 13 Oct 2026 10:46:00 -0500 Subject: [PATCH 5/5] Update clang/include/clang/CIR/Dialect/IR/CIRAttrs.td Co-authored-by: Henrich Lauko <[email protected]> --- clang/include/clang/CIR/Dialect/IR/CIRAttrs.td | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 526b108b3438fb..51c07103884f10 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -1457,6 +1457,7 @@ def CIR_UnwindTableKindAttr : CIR_EnumAttr<CIR_UnwindTableKind, "uwtable"> { //===----------------------------------------------------------------------===// // FramePointerKind //===----------------------------------------------------------------------===// + def CIR_FramePointerKind : CIR_I32Enum<"FramePointerKind", "frame pointer kind", [ I32EnumCase<"None", 0, "none">, _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
