[clang] [clang][ms] Add support for optional function argument in #pragma init_seg (PR #203769)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Jun 14 07:00:42 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-codegen
Author: Tymofii (timofey256)
<details>
<summary>Changes</summary>
This PR adds support for function argument in #pragma init_seg as defined by the Microsoft docs[1]
Closes #<!-- -->148938
[1] https://learn.microsoft.com/en-us/cpp/preprocessor/init-seg
---
Full diff: https://github.com/llvm/llvm-project/pull/203769.diff
10 Files Affected:
- (modified) clang/include/clang/Basic/Attr.td (+6-2)
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+2)
- (modified) clang/include/clang/Sema/Sema.h (+10-4)
- (modified) clang/lib/CodeGen/MicrosoftCXXABI.cpp (+13)
- (modified) clang/lib/Parse/ParsePragma.cpp (+15-2)
- (modified) clang/lib/Sema/Sema.cpp (+2-2)
- (modified) clang/lib/Sema/SemaAttr.cpp (+33-3)
- (modified) clang/lib/Sema/SemaDecl.cpp (+4-3)
- (added) clang/test/CodeGenCXX/pragma-init_seg-func.cpp (+20)
- (modified) clang/test/SemaCXX/pragma-init_seg.cpp (+10-1)
``````````diff
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 1745c5a4f4c2a..0af0c02ceae66 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4584,12 +4584,16 @@ def MSVtorDisp : InheritableAttr {
def InitSeg : Attr {
let Spellings = [Pragma<"", "init_seg">];
- let Args = [StringArgument<"Section">];
+ let Args = [StringArgument<"Section">,
+ DeclArgument<Function, "Func", /*optional=*/1>];
let SemaHandler = 0;
let Documentation = [InitSegDocs];
let AdditionalMembers = [{
void printPrettyPragma(raw_ostream &OS, const PrintingPolicy &Policy) const {
- OS << " (" << getSection() << ')';
+ OS << " (" << getSection();
+ if (const auto *FD = getFunc())
+ OS << ", " << FD->getName();
+ OS << ')';
}
}];
}
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 8097800e6744a..a3bb0e9834bf3 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1216,6 +1216,8 @@ def err_pragma_alloc_text_c_linkage: Error<
"'#pragma alloc_text' is applicable only to functions with C linkage">;
def err_pragma_alloc_text_not_function: Error<
"'#pragma alloc_text' is applicable only to functions">;
+def err_pragma_init_seg_bad_func_type: Error<
+ "'#pragma init_seg' function %0 must have type 'int (*)(void (__cdecl *)(void))'">;
def warn_pragma_unused_undeclared_var : Warning<
"undeclared variable %0 used as an argument for '#pragma unused'">,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index b8d760e7e0975..02d2c4b076d36 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2107,9 +2107,13 @@ class Sema final : public SemaBase {
bool ShouldAct;
};
- /// Last section used with #pragma init_seg.
- StringLiteral *CurInitSeg;
- SourceLocation CurInitSegLoc;
+ /// Used for #pragma init_seg.
+ struct InitSegPragmaState {
+ SourceLocation PragmaLocation;
+ StringLiteral *Segment = nullptr;
+ FunctionDecl *Func = nullptr;
+ };
+ InitSegPragmaState CurInitSeg;
/// Sections used with #pragma alloc_text.
llvm::StringMap<std::tuple<StringRef, SourceLocation>> FunctionToSectionMap;
@@ -2280,8 +2284,10 @@ class Sema final : public SemaBase {
StringLiteral *SegmentName);
/// Called on well-formed \#pragma init_seg().
+ /// If a function name is provided (", func-name"), it is passed via Func.
void ActOnPragmaMSInitSeg(SourceLocation PragmaLocation,
- StringLiteral *SegmentName);
+ StringLiteral *SegmentName,
+ IdentifierLoc *Func = nullptr);
/// Called on well-formed \#pragma alloc_text().
void ActOnPragmaMSAllocText(
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index 40c7c00d85395..eecdd29847739 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -2436,6 +2436,19 @@ void MicrosoftCXXABI::registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D,
if (CGM.getLangOpts().HLSL)
return CGM.AddCXXDtorEntry(Dtor, Addr);
+ // Handle atexit overwrite by #pragma init_seg
+ if (auto *ISA = D.getAttr<InitSegAttr>()) {
+ if (const FunctionDecl *FD = ISA->getFunc()) {
+ llvm::Constant *dtorStub = CGF.createAtExitStub(D, Dtor, Addr);
+ llvm::FunctionType *ExitFnTy =
+ llvm::FunctionType::get(CGM.IntTy, dtorStub->getType(), false);
+ llvm::Constant *ExitFn = CGM.GetAddrOfFunction(FD, ExitFnTy);
+ CGF.EmitNounwindRuntimeCall(llvm::FunctionCallee(ExitFnTy, ExitFn),
+ dtorStub);
+ return;
+ }
+ }
+
// The default behavior is to use atexit.
CGF.registerGlobalDtorWithAtExit(D, Dtor, Addr);
}
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index 237874de9f8a3..d76ac21ada218 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -1246,6 +1246,7 @@ bool Parser::HandlePragmaMSInitSeg(StringRef PragmaName,
// Parse either the known section names or the string section name.
StringLiteral *SegmentName = nullptr;
+ IdentifierLoc *FuncId = nullptr;
if (Tok.isAnyIdentifier()) {
auto *II = Tok.getIdentifierInfo();
StringRef Section = llvm::StringSwitch<StringRef>(II->getName())
@@ -1276,7 +1277,19 @@ bool Parser::HandlePragmaMSInitSeg(StringRef PragmaName,
<< PragmaName;
return false;
}
- // FIXME: Add support for the '[, func-name]' part of the pragma.
+ }
+
+ // Parse optional ', func-name'.
+ if (Tok.is(tok::comma)) {
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::identifier)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+ << PragmaName;
+ return false;
+ }
+ FuncId = new (Actions.Context)
+ IdentifierLoc(Tok.getLocation(), Tok.getIdentifierInfo());
+ PP.Lex(Tok);
}
if (!SegmentName) {
@@ -1290,7 +1303,7 @@ bool Parser::HandlePragmaMSInitSeg(StringRef PragmaName,
PragmaName))
return false;
- Actions.ActOnPragmaMSInitSeg(PragmaLocation, SegmentName);
+ Actions.ActOnPragmaMSInitSeg(PragmaLocation, SegmentName, FuncId);
return true;
}
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 78fbc9e31842d..6b517ec1cc637 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -313,8 +313,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
AlignPackStack(AlignPackInfo(getLangOpts().XLPragmaPack)),
DataSegStack(nullptr), BSSSegStack(nullptr), ConstSegStack(nullptr),
CodeSegStack(nullptr), StrictGuardStackCheckStack(false),
- FpPragmaStack(FPOptionsOverride()), CurInitSeg(nullptr),
- VisContext(nullptr), PragmaAttributeCurrentTargetDecl(nullptr),
+ FpPragmaStack(FPOptionsOverride()), CurInitSeg(), VisContext(nullptr),
+ PragmaAttributeCurrentTargetDecl(nullptr),
StdCoroutineTraitsCache(nullptr), IdResolver(pp),
OriginalLexicalContext(nullptr), StdInitializerList(nullptr),
StdTypeIdentity(nullptr),
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index 67573c9f1c72a..85cf4526be3e1 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -929,12 +929,42 @@ void Sema::ActOnPragmaMSSection(SourceLocation PragmaLocation,
}
void Sema::ActOnPragmaMSInitSeg(SourceLocation PragmaLocation,
- StringLiteral *SegmentName) {
+ StringLiteral *SegmentName,
+ IdentifierLoc *Func) {
// There's no stack to maintain, so we just have a current section. When we
// see the default section, reset our current section back to null so we stop
// tacking on unnecessary attributes.
- CurInitSeg = SegmentName->getString() == ".CRT$XCU" ? nullptr : SegmentName;
- CurInitSegLoc = PragmaLocation;
+ CurInitSeg.Segment =
+ SegmentName->getString() == ".CRT$XCU" ? nullptr : SegmentName;
+ CurInitSeg.PragmaLocation = PragmaLocation;
+ CurInitSeg.Func = nullptr;
+
+ if (Func) {
+ DeclarationNameInfo NameInfo(Func->getIdentifierInfo(), Func->getLoc());
+ LookupResult R(*this, NameInfo, LookupOrdinaryName);
+ LookupName(R, TUScope);
+
+ FunctionDecl *FD = R.getAsSingle<FunctionDecl>();
+ if (!FD) {
+ Diag(Func->getLoc(), diag::err_undeclared_var_use)
+ << Func->getIdentifierInfo();
+ return;
+ }
+
+ // the required type is: int __cdecl myexit (void (__cdecl *pf)(void))
+ QualType FnTy = FD->getType();
+ const auto *Proto = FnTy->getAs<FunctionProtoType>();
+ bool Valid = Proto && Proto->getReturnType()->isIntegerType() &&
+ Proto->getNumParams() == 1 &&
+ Proto->getParamType(0)->isFunctionPointerType();
+ if (!Valid) {
+ Diag(Func->getLoc(), diag::err_pragma_init_seg_bad_func_type)
+ << FD->getDeclName();
+ return;
+ }
+
+ CurInitSeg.Func = FD;
+ }
}
void Sema::ActOnPragmaMSAllocText(
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index cddcf3a010279..905a65d9228c5 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -15193,9 +15193,10 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
// Apply the init_seg attribute if this has an initializer. If the
// initializer turns out to not be dynamic, we'll end up ignoring this
// attribute.
- if (CurInitSeg && var->getInit())
- var->addAttr(InitSegAttr::CreateImplicit(Context, CurInitSeg->getString(),
- CurInitSegLoc));
+ if (CurInitSeg.Segment && var->getInit())
+ var->addAttr(InitSegAttr::CreateImplicit(
+ Context, CurInitSeg.Segment->getString(), CurInitSeg.Func,
+ CurInitSeg.PragmaLocation));
}
// All the following checks are C++ only.
diff --git a/clang/test/CodeGenCXX/pragma-init_seg-func.cpp b/clang/test/CodeGenCXX/pragma-init_seg-func.cpp
new file mode 100644
index 0000000000000..a981cce752e20
--- /dev/null
+++ b/clang/test/CodeGenCXX/pragma-init_seg-func.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 %s -triple=i686-pc-win32 -fms-extensions -emit-llvm -o - | FileCheck %s
+
+int __cdecl myexit(void (__cdecl *pf)(void));
+
+struct S {
+ S();
+ ~S();
+};
+
+#pragma init_seg(".myseg", myexit)
+
+S s;
+
+// The initializer pointer is still placed in the custom section.
+// CHECK: @__cxx_init_fn_ptr = private constant ptr @"??__Es@@YAXXZ", section ".myseg"
+
+// The destructor registration calls myexit instead of atexit.
+// CHECK-LABEL: define {{.*}} @"??__Es@@YAXXZ"
+// CHECK: call i32 @"?myexit@@{{[^"]+}}"(
+// CHECK-NOT: call {{.*}} @atexit(
diff --git a/clang/test/SemaCXX/pragma-init_seg.cpp b/clang/test/SemaCXX/pragma-init_seg.cpp
index 1b22939f18e33..9124f9085603c 100644
--- a/clang/test/SemaCXX/pragma-init_seg.cpp
+++ b/clang/test/SemaCXX/pragma-init_seg.cpp
@@ -11,12 +11,21 @@
#pragma init_seg asdf // expected-warning {{missing '('}}
#pragma init_seg) // expected-warning {{missing '('}}
#pragma init_seg("a" "b") // no warning
-#pragma init_seg("a", "b") // expected-warning {{missing ')'}}
+#pragma init_seg("a", "b") // expected-warning {{expected identifier in '#pragma init_seg'}}
#pragma init_seg("a") asdf // expected-warning {{extra tokens at end of '#pragma init_seg'}}
#pragma init_seg("\x") // expected-error {{\x used with no following hex digits}}
#pragma init_seg("a" L"b") // expected-warning {{expected non-wide string literal in '#pragma init_seg'}}
#pragma init_seg(compiler)
+
+int __cdecl myexit(void (__cdecl *pf)(void));
+#pragma init_seg(".myseg", myexit)
+
+int bad_func(int x);
+#pragma init_seg(".myseg2", bad_func) // expected-error {{'#pragma init_seg' function 'bad_func' must have type 'int (*)(void (__cdecl *)(void))'}}
+
+#pragma init_seg(".myseg3", no_such_func) // expected-error {{use of undeclared identifier 'no_such_func'}}
+
#else
#pragma init_seg(compiler) // expected-warning {{'#pragma init_seg' is only supported when targeting a Microsoft environment}}
#endif
``````````
</details>
https://github.com/llvm/llvm-project/pull/203769
More information about the cfe-commits
mailing list