[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