[PATCH] -fms-extensions: Implement half of #pragma init_seg

Aaron Ballman aaron at aaronballman.com
Mon Jul 21 08:09:31 PDT 2014


On Thu, Jul 17, 2014 at 9:09 PM, Reid Kleckner <rnk at google.com> wrote:
> - Make pragma parsing more robust
>
> http://reviews.llvm.org/D4549
>
> Files:
>   include/clang/Basic/Attr.td
>   include/clang/Basic/AttrDocs.td
>   include/clang/Basic/DiagnosticParseKinds.td
>   include/clang/Sema/Sema.h
>   lib/CodeGen/CGDeclCXX.cpp
>   lib/CodeGen/CodeGenModule.h
>   lib/Parse/ParsePragma.cpp
>   lib/Sema/Sema.cpp
>   lib/Sema/SemaAttr.cpp
>   lib/Sema/SemaDecl.cpp
>   test/CodeGenCXX/pragma-init_seg.cpp
>   test/SemaCXX/pragma-init_seg.cpp

Some incredibly tiny nits below, but LGTM with those changes.

> Index: include/clang/Basic/Attr.td
> ===================================================================
> --- include/clang/Basic/Attr.td
> +++ include/clang/Basic/Attr.td
> @@ -1767,6 +1767,18 @@
>    let Documentation = [Undocumented];
>  }
>
> +def InitSeg : Attr {
> +  let Spellings = [Pragma<"", "init_seg">];
> +  let Args = [StringArgument<"Section">];
> +  let SemaHandler = 0;
> +  let Documentation = [InitSegDocs];
> +  let AdditionalMembers = [{
> +  void printPrettyPragma(raw_ostream &OS, const PrintingPolicy &Policy) const {
> +    OS << '(' << getSection() << ')';
> +  }
> +  }];
> +}
> +
>  def Unaligned : IgnoredAttr {
>    let Spellings = [Keyword<"__unaligned">];
>  }
> @@ -1809,7 +1821,7 @@
>    }
>
>    void printPrettyPragma(raw_ostream &OS, const PrintingPolicy &Policy) const {
> -    OS << getOptionName(option) << "(";
> +    OS << getOptionName(option) << '(';
>      if (option == VectorizeWidth || option == InterleaveCount ||
>          option == UnrollCount)
>        OS << value;
> Index: include/clang/Basic/AttrDocs.td
> ===================================================================
> --- include/clang/Basic/AttrDocs.td
> +++ include/clang/Basic/AttrDocs.td
> @@ -36,6 +36,22 @@
>    let Heading = "section (gnu::section, __declspec(allocate))";
>  }
>
> +def InitSegDocs : Documentation {
> +  let Category = DocCatVariable;
> +  let Content = [{
> +The attribute applied by ``pragma init_seg()`` controls the section into
> +which global initialization function pointers are emitted.  It is only
> +available with ``-fms-extensions``.  Typically, this function pointer is
> +emitted into ``.CRT$XCU`` on Windows.  The user can change the order of
> +initialization by using a different section name with the same
> +``.CRT$XC`` prefix and a suffix that sorts lexicographically before or
> +after the standard ``.CRT$XCU`` sections.  See the init_seg_
> +documentation on MSDN for more information.
> +
> +.. _init_seg: http://msdn.microsoft.com/en-us/library/7977wcck(v=vs.110).aspx
> +  }];
> +}
> +
>  def TLSModelDocs : Documentation {
>    let Category = DocCatVariable;
>    let Content = [{
> Index: include/clang/Basic/DiagnosticParseKinds.td
> ===================================================================
> --- include/clang/Basic/DiagnosticParseKinds.td
> +++ include/clang/Basic/DiagnosticParseKinds.td
> @@ -800,6 +800,9 @@
>  def warn_pragma_expected_section_label_or_name : Warning<
>    "expected a stack label or a string literal for the section name in '#pragma %0' - ignored">,
>    InGroup<IgnoredPragmas>;
> +def warn_pragma_expected_init_seg : Warning<
> +  "expected 'compiler', 'lib', 'user', or a string literal for the section name in '#pragma %0' - ignored">,
> +  InGroup<IgnoredPragmas>;
>  def warn_pragma_expected_integer : Warning<
>    "expected integer between %0 and %1 inclusive in '#pragma %2' - ignored">,
>    InGroup<IgnoredPragmas>;
> Index: include/clang/Sema/Sema.h
> ===================================================================
> --- include/clang/Sema/Sema.h
> +++ include/clang/Sema/Sema.h
> @@ -330,6 +330,10 @@
>    PragmaStack<StringLiteral *> ConstSegStack;
>    PragmaStack<StringLiteral *> CodeSegStack;
>
> +  /// Last section used with #pragma init_seg.
> +  StringLiteral *CurInitSeg;
> +  SourceLocation CurInitSegLoc;
> +
>    /// VisContext - Manages the stack for \#pragma GCC visibility.
>    void *VisContext; // Really a "PragmaVisStack*"
>
> @@ -7179,6 +7183,10 @@
>    void ActOnPragmaMSSection(SourceLocation PragmaLocation,
>                              int SectionFlags, StringLiteral *SegmentName);
>
> +  /// \brief Called on well-formed \#pragma init_seg().
> +  void ActOnPragmaMSInitSeg(SourceLocation PragmaLocation,
> +                            StringLiteral *SegmentName);
> +
>    /// ActOnPragmaDetectMismatch - Call on well-formed \#pragma detect_mismatch
>    void ActOnPragmaDetectMismatch(StringRef Name, StringRef Value);
>
> Index: lib/CodeGen/CGDeclCXX.cpp
> ===================================================================
> --- lib/CodeGen/CGDeclCXX.cpp
> +++ lib/CodeGen/CGDeclCXX.cpp
> @@ -257,6 +257,32 @@
>    return Fn;
>  }
>
> +/// Create a global pointer to a function that will initialize a global
> +/// variable.  The user has requested that this pointer be emitted in a specific
> +/// section.
> +void CodeGenModule::EmitPointerToInitFunc(const VarDecl *D,
> +                                          llvm::GlobalVariable *GV,
> +                                          llvm::Function *InitFunc,
> +                                          InitSegAttr *ISA) {
> +  llvm::GlobalVariable *PtrArray = new llvm::GlobalVariable(
> +      TheModule, InitFunc->getType(), /*isConstant=*/true,
> +      llvm::GlobalValue::PrivateLinkage, InitFunc, "__cxx_init_fn_ptr");
> +  PtrArray->setSection(ISA->getSection());
> +  addUsedGlobal(PtrArray);
> +
> +  // If the GV is already in a comdat group, then we have to join it.
> +  llvm::Comdat *C = GV->getComdat();
> +
> +  // LinkOnce and Weak linkage are lowered down to a single-member comdat group.
> +  // Make an explicit group so we can join it.
> +  if (!C && (GV->hasWeakLinkage() || GV->hasLinkOnceLinkage())) {
> +    C = TheModule.getOrInsertComdat(GV->getName());
> +    GV->setComdat(C);
> +  }
> +  if (C)
> +    PtrArray->setComdat(C);
> +}
> +
>  void
>  CodeGenModule::EmitCXXGlobalVarDeclInitFunc(const VarDecl *D,
>                                              llvm::GlobalVariable *Addr,
> @@ -272,18 +298,21 @@
>    llvm::Function *Fn =
>        CreateGlobalInitOrDestructFunction(*this, FTy, FnName.str());
>
> +  auto *ISA = D->getAttr<InitSegAttr>();
>    CodeGenFunction(*this).GenerateCXXGlobalVarDeclInitFunc(Fn, D, Addr,
>                                                            PerformInit);
> -
>    if (D->getTLSKind()) {
>      // FIXME: Should we support init_priority for thread_local?
>      // FIXME: Ideally, initialization of instantiated thread_local static data
>      // members of class templates should not trigger initialization of other
>      // entities in the TU.
>      // FIXME: We only need to register one __cxa_thread_atexit function for the
>      // entire TU.
>      CXXThreadLocalInits.push_back(Fn);
> -  } else if (const InitPriorityAttr *IPA = D->getAttr<InitPriorityAttr>()) {
> +  } else if (PerformInit && ISA) {
> +    EmitPointerToInitFunc(D, Addr, Fn, ISA);
> +    DelayedCXXInitPosition.erase(D);
> +  } else if (auto *IPA = D->getAttr<InitPriorityAttr>()) {
>      OrderGlobalInits Key(IPA->getPriority(), PrioritizedCXXGlobalInits.size());
>      PrioritizedCXXGlobalInits.push_back(std::make_pair(Key, Fn));
>      DelayedCXXInitPosition.erase(D);
> Index: lib/CodeGen/CodeGenModule.h
> ===================================================================
> --- lib/CodeGen/CodeGenModule.h
> +++ lib/CodeGen/CodeGenModule.h
> @@ -1099,6 +1099,9 @@
>                                      llvm::GlobalVariable *Addr,
>                                      bool PerformInit);
>
> +  void EmitPointerToInitFunc(const VarDecl *VD, llvm::GlobalVariable *Addr,
> +                             llvm::Function *InitFunc, InitSegAttr *ISA);
> +
>    // FIXME: Hardcoding priority here is gross.
>    void AddGlobalCtor(llvm::Function *Ctor, int Priority = 65535,
>                       llvm::Constant *AssociatedData = 0);
> Index: lib/Parse/ParsePragma.cpp
> ===================================================================
> --- lib/Parse/ParsePragma.cpp
> +++ lib/Parse/ParsePragma.cpp
> @@ -642,10 +642,66 @@
>
>  bool Parser::HandlePragmaMSInitSeg(StringRef PragmaName,
>                                     SourceLocation PragmaLocation) {
> -  PP.Diag(PragmaLocation,
> -          PP.getDiagnostics().getCustomDiagID(
> -              DiagnosticsEngine::Error, "'#pragma init_seg' not implemented."));
> -  return false;
> +  if (Tok.isNot(tok::l_paren)) {
> +    PP.Diag(PragmaLocation, diag::warn_pragma_expected_lparen) << PragmaName;
> +    return false;
> +  }
> +  PP.Lex(Tok); // (

Use ExpectAndConsume instead?

> +
> +  // Parse either the known section names or the string section name.
> +  StringLiteral *SegmentName = nullptr;
> +  if (Tok.isAnyIdentifier()) { // compiler, lib, user
> +    auto *II = Tok.getIdentifierInfo();
> +    StringRef Section = llvm::StringSwitch<StringRef>(II->getName())
> +                            .Case("compiler", "\".CRT$XCC\"")
> +                            .Case("lib", "\".CRT$XCL\"")
> +                            .Case("user", "\".CRT$XCU\"")
> +                            .Default("");
> +
> +    if (!Section.empty()) {
> +      // Pretend the user wrote the appropriate string literal here.
> +      Token Toks[1];
> +      Toks[0].startToken();
> +      Toks[0].setKind(tok::string_literal);
> +      Toks[0].setLocation(Tok.getLocation());
> +      Toks[0].setLiteralData(Section.data());
> +      Toks[0].setLength(Section.size());
> +      SegmentName =
> +          cast<StringLiteral>(Actions.ActOnStringLiteral(Toks, nullptr).get());
> +      PP.Lex(Tok);
> +    }
> +  } else if (Tok.is(tok::string_literal)) {
> +    ExprResult StringResult = ParseStringLiteralExpression();
> +    if (StringResult.isInvalid())
> +      return false;
> +    SegmentName = cast<StringLiteral>(StringResult.get());
> +    if (SegmentName->getCharByteWidth() != 1) {
> +      PP.Diag(PragmaLocation, diag::warn_pragma_expected_non_wide_string)
> +          << PragmaName;
> +      return false;
> +    }
> +  }
> +
> +  if (!SegmentName) {
> +    PP.Diag(PragmaLocation, diag::warn_pragma_expected_init_seg) << PragmaName;
> +    return false;
> +  }
> +
> +  // FIXME: Add support for the '[, func-name]' part of the pragma.
> +
> +  if (Tok.isNot(tok::r_paren)) {
> +    PP.Diag(PragmaLocation, diag::warn_pragma_expected_rparen) << PragmaName;
> +    return false;
> +  }
> +  PP.Lex(Tok); // )

ExpectAndConsume here as well.

> +  if (Tok.isNot(tok::eof)) {
> +    PP.Diag(PragmaLocation, diag::warn_pragma_extra_tokens_at_eol)
> +        << PragmaName;
> +    return false;
> +  }
> +  PP.Lex(Tok); // eof
> +  Actions.ActOnPragmaMSInitSeg(PragmaLocation, SegmentName);
> +  return true;
>  }
>
>  struct PragmaLoopHintInfo {
> Index: lib/Sema/Sema.cpp
> ===================================================================
> --- lib/Sema/Sema.cpp
> +++ lib/Sema/Sema.cpp
> @@ -87,7 +87,7 @@
>          LangOpts.getMSPointerToMemberRepresentationMethod()),
>      VtorDispModeStack(1, MSVtorDispAttr::Mode(LangOpts.VtorDispMode)),
>      DataSegStack(nullptr), BSSSegStack(nullptr), ConstSegStack(nullptr),
> -    CodeSegStack(nullptr), VisContext(nullptr),
> +    CodeSegStack(nullptr), CurInitSeg(nullptr), VisContext(nullptr),
>      IsBuildingRecoveryCallExpr(false),
>      ExprNeedsCleanups(false), LateTemplateParser(nullptr),
>      OpaqueParser(nullptr), IdResolver(pp), StdInitializerList(nullptr),
> Index: lib/Sema/SemaAttr.cpp
> ===================================================================
> --- lib/Sema/SemaAttr.cpp
> +++ lib/Sema/SemaAttr.cpp
> @@ -431,6 +431,15 @@
>    UnifySection(SegmentName->getString(), SectionFlags, PragmaLocation);
>  }
>
> +void Sema::ActOnPragmaMSInitSeg(SourceLocation PragmaLocation,
> +                                StringLiteral *SegmentName) {
> +  // 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;
> +}
> +
>  void Sema::ActOnPragmaUnused(const Token &IdTok, Scope *curScope,
>                               SourceLocation PragmaLoc) {
>
> Index: lib/Sema/SemaDecl.cpp
> ===================================================================
> --- lib/Sema/SemaDecl.cpp
> +++ lib/Sema/SemaDecl.cpp
> @@ -9125,6 +9125,13 @@
>      if (const SectionAttr *SA = var->getAttr<SectionAttr>())
>        if (UnifySection(SA->getName(), SectionFlags, var))
>          var->dropAttr<SectionAttr>();
> +
> +    // 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));
>    }
>
>    // All the following checks are C++ only.
> Index: test/CodeGenCXX/pragma-init_seg.cpp
> ===================================================================
> --- test/CodeGenCXX/pragma-init_seg.cpp
> +++ test/CodeGenCXX/pragma-init_seg.cpp
> @@ -1,16 +1,72 @@
> -// RUN: not %clang_cc1 %s -triple=i686-pc-win32 -fms-extensions -emit-llvm-only 2>&1 | FileCheck %s
> -
> -// Reduced from WebKit.
> -
> -// FIXME: Implement this pragma and test the codegen.  We probably want to
> -// completely skip @llvm.global_ctors and just create global function pointers
> -// to the initializer with the right section.
> -
> -// CHECK: '#pragma init_seg' not implemented
> -#pragma init_seg(".unwantedstaticinits")
> -struct A {
> -  A();
> -  ~A();
> -  int a;
> -};
> -A a;
> +// RUN: %clang_cc1 %s -triple=i686-pc-win32 -fms-extensions -emit-llvm -o - | FileCheck %s
> +
> +int f();
> +
> +// CHECK: $"\01?x at selectany_init@@3HA" = comdat any
> +// CHECK: $"\01?x@?$A at H@explicit_template_instantiation@@2HB" = comdat any
> +// CHECK: $"\01?x@?$A at H@implicit_template_instantiation@@2HB" = comdat any
> +
> +namespace simple_init {
> +#pragma init_seg(compiler)
> +int x = f();
> +// CHECK: @"\01?x at simple_init@@3HA" = global i32 0, align 4
> +// CHECK: @__cxx_init_fn_ptr = private constant void ()* @"\01??__Ex at simple_init@@YAXXZ", section ".CRT$XCC"
> +
> +#pragma init_seg(lib)
> +int y = f();
> +// CHECK: @"\01?y at simple_init@@3HA" = global i32 0, align 4
> +// CHECK: @__cxx_init_fn_ptr1 = private constant void ()* @"\01??__Ey at simple_init@@YAXXZ", section ".CRT$XCL"
> +
> +#pragma init_seg(user)
> +int z = f();
> +// CHECK: @"\01?z at simple_init@@3HA" = global i32 0, align 4
> +// No function pointer!  This one goes on @llvm.global_ctors.
> +}
> +
> +#pragma init_seg(".asdf")
> +
> +namespace internal_init {
> +namespace {
> +int x = f();
> +// CHECK: @"\01?x@?A at internal_init@@3HA" = internal global i32 0, align 4
> +// CHECK: @__cxx_init_fn_ptr2 = private constant void ()* @"\01??__Ex@?A at internal_init@@YAXXZ", section ".asdf"
> +}
> +}
> +
> +namespace selectany_init {
> +int __declspec(selectany) x = f();
> +// CHECK: @"\01?x at selectany_init@@3HA" = weak_odr global i32 0, comdat $"\01?x at selectany_init@@3HA", align 4
> +// CHECK: @__cxx_init_fn_ptr3 = private constant void ()* @"\01??__Ex at selectany_init@@YAXXZ", section ".asdf", comdat $"\01?x at selectany_init@@3HA"
> +}
> +
> +namespace explicit_template_instantiation {
> +template <typename T> struct A { static const int x; };
> +template <typename T> const int A<T>::x = f();
> +template struct A<int>;
> +// CHECK: @"\01?x@?$A at H@explicit_template_instantiation@@2HB" = weak_odr global i32 0, comdat $"\01?x@?$A at H@explicit_template_instantiation@@2HB", align 4
> +// CHECK: @__cxx_init_fn_ptr4 = private constant void ()* @"\01??__Ex@?$A at H@explicit_template_instantiation@@2HB at YAXXZ", section ".asdf", comdat $"\01?x@?$A at H@explicit_template_instantiation@@2HB"
> +}
> +
> +namespace implicit_template_instantiation {
> +template <typename T> struct A { static const int x; };
> +template <typename T> const int A<T>::x = f();
> +int g() { return A<int>::x; }
> +// CHECK: @"\01?x@?$A at H@implicit_template_instantiation@@2HB" = linkonce_odr global i32 0, comdat $"\01?x@?$A at H@implicit_template_instantiation@@2HB", align 4
> +// CHECK: @__cxx_init_fn_ptr5 = private constant void ()* @"\01??__Ex@?$A at H@implicit_template_instantiation@@2HB at YAXXZ", section ".asdf", comdat $"\01?x@?$A at H@implicit_template_instantiation@@2HB"
> +}
> +
> +// ... and here's where we emitted user level ctors.
> +// CHECK: @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }]
> +// CHECK: [{ i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__sub_I_pragma_init_seg.cpp, i8* null }]
> +
> +// We have to mark everything used so we can survive globalopt, even through
> +// LTO.  There's no way LLVM could really understand if data in the .asdf
> +// section is really used or dead.
> +//
> +// CHECK: @llvm.used = appending global [6 x i8*]
> +// CHECK: [i8* bitcast (void ()** @__cxx_init_fn_ptr to i8*),
> +// CHECK: i8* bitcast (void ()** @__cxx_init_fn_ptr1 to i8*),
> +// CHECK: i8* bitcast (void ()** @__cxx_init_fn_ptr2 to i8*),
> +// CHECK: i8* bitcast (void ()** @__cxx_init_fn_ptr3 to i8*),
> +// CHECK: i8* bitcast (void ()** @__cxx_init_fn_ptr4 to i8*),
> +// CHECK: i8* bitcast (void ()** @__cxx_init_fn_ptr5 to i8*)], section "llvm.metadata"
> Index: test/SemaCXX/pragma-init_seg.cpp
> ===================================================================
> --- /dev/null
> +++ test/SemaCXX/pragma-init_seg.cpp
> @@ -0,0 +1,15 @@
> +// RUN: %clang_cc1 -fsyntax-only -verify -fms-extensions %s -triple x86_64-pc-win32
> +
> +#pragma init_seg(L".my_seg") // expected-warning {{expected 'compiler', 'lib', 'user', or a string literal}}
> +#pragma init_seg( // expected-warning {{expected 'compiler', 'lib', 'user', or a string literal}}
> +#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") 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'}}

I think these should be in test/Preprocessor instead of test/SemaCXX.

> +
> +int f();
> +#pragma init_seg(compiler)
> +int __declspec(thread) x = f(); // expected-error {{initializer for thread-local variable must be a constant expression}}
>

~Aaron



More information about the cfe-commits mailing list