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

Reply via email to