llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang 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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
