From e77a85615ee9d775b397cc877b16eed91e1ad2e1 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh Date: Tue, 10 Jun 2025 14:06:53 -0700 Subject: [PATCH] [clang] "modular_format" attribute for functions using format strings This provides a C language version of the new IR modular-format attribute. This, in concert with the format attribute, allows a library function to declare that a modular version of its implementation is available. See issue #146159 for context. --- clang/include/clang/Basic/Attr.td | 11 +++++++++++ clang/include/clang/Basic/AttrDocs.td | 25 +++++++++++++++++++++++++ clang/lib/CodeGen/CGCall.cpp | 12 ++++++++++++ clang/lib/Sema/SemaDeclAttr.cpp | 27 +++++++++++++++++++++++++++ 4 files changed, 75 insertions(+) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 27fea7dea0a5e..bed878a10424c 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -5182,3 +5182,14 @@ def NonString : InheritableAttr { let Subjects = SubjectList<[Var, Field]>; let Documentation = [NonStringDocs]; } + +def ModularFormat : InheritableAttr { + let Spellings = [Clang<"modular_format">]; + let Args = [ + IdentifierArgument<"ModularImplFn">, + StringArgument<"ImplName">, + VariadicStringArgument<"Aspects"> + ]; + let Subjects = SubjectList<[Function]>; + let Documentation = [ModularFormatDocs]; +} diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 43442f177ab7b..3c325ce2462cb 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -9427,3 +9427,28 @@ diagnostics with code like: __attribute__((nonstring)) char NotAStr[3] = "foo"; // Not diagnosed }]; } + +def ModularFormatDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``modular_format`` attribute can be applied to a function that bears the +``format`` attribute to indicate that the implementation is modular on the +format string argument. When the format argument for a given call is constant, +the compiler may redirect the call to the symbol given as the first argument to +the attribute (the modular implementation function). + +The second argument is a implementation name, and the remaining arguments are +aspects of the format string for the compiler to report. If the compiler does +not understand a aspect, it must summarily report that the format string has +that aspect. + +The compiler reports an aspect by issing a relocation for the symbol +`_``. This arranges for code and data needed to support the +aspect of the implementation to be brought into the link to satisfy weak +references in the modular implemenation function. + +The following aspects are currently supported: + +- ``float``: The call has a floating point argument + }]; +} diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index a06455d25b1ef..9e8929b5a56ae 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2569,6 +2569,18 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, if (TargetDecl->hasAttr()) FuncAttrs.addAttribute("aarch64_pstate_sm_body"); + + if (auto *ModularFormat = TargetDecl->getAttr()) { + // TODO: Error checking + FormatAttr *Format = TargetDecl->getAttr(); + std::string FormatIdx = std::to_string(Format->getFormatIdx()); + std::string FirstArg = std::to_string(Format->getFirstArg()); + SmallVector Args = { + FormatIdx, FirstArg, ModularFormat->getModularImplFn()->getName(), + ModularFormat->getImplName()}; + llvm::append_range(Args, ModularFormat->aspects()); + FuncAttrs.addAttribute("modular-format", llvm::join(Args, ",")); + } } // Attach "no-builtins" attributes to: diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index eba29e609cb05..b70ffd7c35f7b 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6897,6 +6897,29 @@ static void handleVTablePointerAuthentication(Sema &S, Decl *D, CustomDiscriminationValue)); } +static void handleModularFormat(Sema &S, Decl *D, const ParsedAttr &AL) { + StringRef ImplName; + if (!S.checkStringLiteralArgumentAttr(AL, 1, ImplName)) + return; + SmallVector Aspects; + for (unsigned I = 2, E = AL.getNumArgs(); I != E; ++I) { + StringRef Aspect; + if (!S.checkStringLiteralArgumentAttr(AL, I, Aspect)) + return; + Aspects.push_back(Aspect); + } + + // Store aspects sorted and without duplicates. + llvm::sort(Aspects); + Aspects.erase(llvm::unique(Aspects), Aspects.end()); + + // TODO: Type checking on identifier + // TODO: Merge attributes + D->addAttr(::new (S.Context) ModularFormatAttr( + S.Context, AL, AL.getArgAsIdent(0)->getIdentifierInfo(), ImplName, + Aspects.data(), Aspects.size())); +} + //===----------------------------------------------------------------------===// // Top Level Sema Entry Points //===----------------------------------------------------------------------===// @@ -7821,6 +7844,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_VTablePointerAuthentication: handleVTablePointerAuthentication(S, D, AL); break; + + case ParsedAttr::AT_ModularFormat: + handleModularFormat(S, D, AL); + break; } }