Skip to content

[SystemZ][z/OS] Implement _Export keyword #140944

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,11 @@ WebAssembly Support
AVR Support
^^^^^^^^^^^

SystemZ Support
^^^^^^^^^^^^^^^

- Add support for `_Export` keyword for z/OS

DWARF Support in Clang
----------------------

Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -4267,6 +4267,12 @@ def Thread : Attr {
let Subjects = SubjectList<[Var]>;
}

def zOSExport : InheritableAttr {
let Spellings = [CustomKeyword<"_Export">];
let Subjects = SubjectList<[Function, Var, CXXRecord]>;
let Documentation = [zOSExportDocs];
}

def Win64 : IgnoredAttr {
let Spellings = [CustomKeyword<"__w64">];
let LangOpts = [MicrosoftExt];
Expand Down
33 changes: 33 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,39 @@ members, and static locals.
}];
}

def zOSExportDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
Use the ``_Export`` keyword on a function or external variable to declare
that it is to be exported (made available to other modules). A symbol needs to be
declared exported on or before the definition of the symbol. The ``_Export``
keyword must immediately precede the declaration name in the declarator.
For example:

.. code-block:: c

int _Export func(float);
int (*_Export funcPtr)(float);

The first statement exports the function ``func``, if ``func`` is defined in the
translation unit after this declaration.

All of the static data members and member functions of a class or struct and its vague
linkage objects (vtable, typeinfo, typeinfo name) can be exported
by including ``_Export`` in tag of the class during the class definition or forward
declaration of the class.

.. code-block:: c++

class _Export C {
int func();
};

Select members of a class can be exported by using the ``_Export`` keyword on
declaration within the class or definition of the member.
}];
}

def NoEscapeDocs : Documentation {
let Category = DocCatVariable;
let Content = [{
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -9655,6 +9655,8 @@ def warn_redefine_extname_not_applied : Warning<
"#pragma redefine_extname is applicable to external C declarations only; "
"not applied to %select{function|variable}0 %1">,
InGroup<Pragmas>;
def err_cannot_be_exported : Error<
"needs to have external linkage to be '_Export` qualified">;
} // End of general sema category.

// inline asm.
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,9 @@ KEYWORD(__ptrauth , KEYALL)
// C2y
UNARY_EXPR_OR_TYPE_TRAIT(_Countof, CountOf, KEYNOCXX)

// z/OS specific keywords
KEYWORD(_Export , KEYZOS)

// C++ 2.11p1: Keywords.
KEYWORD(asm , KEYCXX|KEYGNU)
KEYWORD(bool , BOOLSUPPORT|KEYC23)
Expand Down
38 changes: 34 additions & 4 deletions clang/include/clang/Sema/DeclSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,8 @@ class DeclSpec {
unsigned FS_virtual_specified : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned FS_noreturn_specified : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned ExportSpecified : 1; // z/OS extension

// friend-specifier
LLVM_PREFERRED_TYPE(bool)
Expand Down Expand Up @@ -443,6 +445,7 @@ class DeclSpec {
SourceLocation FS_forceinlineLoc;
SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc;
SourceLocation TQ_pipeLoc;
SourceLocation ExportLoc;

WrittenBuiltinSpecs writtenBS;
void SaveWrittenBuiltinSpecs();
Expand Down Expand Up @@ -491,9 +494,9 @@ class DeclSpec {
TypeSpecPipe(false), TypeSpecSat(false), ConstrainedAuto(false),
TypeQualifiers(TQ_unspecified), FS_inline_specified(false),
FS_forceinline_specified(false), FS_virtual_specified(false),
FS_noreturn_specified(false), FriendSpecifiedFirst(false),
ConstexprSpecifier(
static_cast<unsigned>(ConstexprSpecKind::Unspecified)),
FS_noreturn_specified(false), ExportSpecified(false),
FriendSpecifiedFirst(false), ConstexprSpecifier(static_cast<unsigned>(
ConstexprSpecKind::Unspecified)),
Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {}

// storage-class-specifier
Expand Down Expand Up @@ -660,6 +663,9 @@ class DeclSpec {
bool isNoreturnSpecified() const { return FS_noreturn_specified; }
SourceLocation getNoreturnSpecLoc() const { return FS_noreturnLoc; }

bool isExportSpecified() const { return ExportSpecified; }
SourceLocation getExportSpecLoc() const { return ExportLoc; }

void ClearFunctionSpecs() {
FS_inline_specified = false;
FS_inlineLoc = SourceLocation();
Expand Down Expand Up @@ -810,6 +816,8 @@ class DeclSpec {
bool setFunctionSpecNoreturn(SourceLocation Loc, const char *&PrevSpec,
unsigned &DiagID);

bool setExportSpec(SourceLocation Loc);

bool SetFriendSpec(SourceLocation Loc, const char *&PrevSpec,
unsigned &DiagID);
bool setModulePrivateSpec(SourceLocation Loc, const char *&PrevSpec,
Expand Down Expand Up @@ -1955,6 +1963,10 @@ class Declarator {
LLVM_PREFERRED_TYPE(bool)
unsigned InlineStorageUsed : 1;

/// Indicates whether this is set as _Export.
LLVM_PREFERRED_TYPE(bool)
unsigned ExportSpecified : 1; // z/OS extension

/// Indicates whether this declarator has an initializer.
LLVM_PREFERRED_TYPE(bool)
unsigned HasInitializer : 1;
Expand Down Expand Up @@ -2001,6 +2013,9 @@ class Declarator {
/// this declarator as a parameter pack.
SourceLocation EllipsisLoc;

/// The source location of the _Export keyword on this declarator.
SourceLocation ExportLoc;

Expr *PackIndexingExpr;

friend struct DeclaratorChunk;
Expand Down Expand Up @@ -2030,7 +2045,8 @@ class Declarator {
FunctionDefinitionKind::Declaration)),
Redeclaration(false), Extension(false), ObjCIvar(false),
ObjCWeakProperty(false), InlineStorageUsed(false),
HasInitializer(false), Attrs(DS.getAttributePool().getFactory()),
ExportSpecified(false), HasInitializer(false),
Attrs(DS.getAttributePool().getFactory()),
DeclarationAttrs(DeclarationAttrs), AsmLabel(nullptr),
TrailingRequiresClause(nullptr),
InventedTemplateParameterList(nullptr) {
Expand Down Expand Up @@ -2109,6 +2125,18 @@ class Declarator {
Range.setEnd(SR.getEnd());
}

/// Set this declarator as _Export.
void SetExport(SourceLocation Loc) {
ExportSpecified = true;
ExportLoc = Loc;
}

/// Whether this declarator is marked as _Export.
bool IsExport() const { return ExportSpecified; }

/// Get the location of the _Export keyword.
SourceLocation getExportLoc() const { return ExportLoc; }

/// Reset the contents of this Declarator.
void clear() {
SS.clear();
Expand All @@ -2125,8 +2153,10 @@ class Declarator {
HasInitializer = false;
ObjCIvar = false;
ObjCWeakProperty = false;
ExportSpecified = false;
CommaLoc = SourceLocation();
EllipsisLoc = SourceLocation();
ExportLoc = SourceLocation();
PackIndexingExpr = nullptr;
}

Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -4871,6 +4871,8 @@ class Sema final : public SemaBase {
TypeVisibilityAttr::VisibilityType Vis);
VisibilityAttr *mergeVisibilityAttr(Decl *D, const AttributeCommonInfo &CI,
VisibilityAttr::VisibilityType Vis);
void mergeVisibilityType(Decl *D, SourceLocation Loc,
VisibilityAttr::VisibilityType Type);
SectionAttr *mergeSectionAttr(Decl *D, const AttributeCommonInfo &CI,
StringRef Name);

Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Driver/ToolChains/ZOS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ void ZOS::addClangTargetOptions(const ArgList &DriverArgs,
options::OPT_fno_aligned_allocation))
CC1Args.push_back("-faligned-alloc-unavailable");

if (!DriverArgs.hasArg(options::OPT_fvisibility_EQ,
options::OPT_fvisibility_ms_compat))
CC1Args.push_back("-fvisibility=hidden");

if (DriverArgs.hasFlag(options::OPT_fxl_pragma_pack,
options::OPT_fno_xl_pragma_pack, true))
CC1Args.push_back("-fxl-pragma-pack");
Expand Down
15 changes: 15 additions & 0 deletions clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4217,6 +4217,11 @@ void Parser::ParseDeclarationSpecifiers(
isInvalid = DS.setFunctionSpecNoreturn(Loc, PrevSpec, DiagID);
break;

case tok::kw__Export:
// _Export keyword is part of the declarator id
goto DoneWithDeclSpec;
break;

// friend
case tok::kw_friend:
if (DSContext == DeclSpecContext::DSC_class) {
Expand Down Expand Up @@ -6418,6 +6423,16 @@ void Parser::ParseDeclaratorInternal(Declarator &D,

tok::TokenKind Kind = Tok.getKind();

if (Kind == tok::kw__Export) {
SourceLocation loc = ConsumeToken();
D.SetExport(loc);
D.SetRangeEnd(loc);

if (DirectDeclParser)
(this->*DirectDeclParser)(D);
return;
}

if (D.getDeclSpec().isTypeSpecPipe() && !isPipeDeclarator(D)) {
DeclSpec DS(AttrFactory);
ParseTypeQualifierListOpt(DS);
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Parse/ParseDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1588,6 +1588,12 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
// If attributes exist after tag, parse them.
for (;;) {
MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs);
if (Tok.is(tok::kw__Export)) {
SourceLocation loc = ConsumeToken();
DS.setExportSpec(loc);
continue;
}

// Parse inheritance specifiers.
if (Tok.isOneOf(tok::kw___single_inheritance,
tok::kw___multiple_inheritance,
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Sema/DeclSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,12 @@ bool DeclSpec::setFunctionSpecNoreturn(SourceLocation Loc,
return false;
}

bool DeclSpec::setExportSpec(SourceLocation Loc) {
ExportSpecified = true;
ExportLoc = Loc;
return false;
}

bool DeclSpec::SetFriendSpec(SourceLocation Loc, const char *&PrevSpec,
unsigned &DiagID) {
if (isFriendSpecified()) {
Expand Down
21 changes: 21 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5175,6 +5175,9 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
assert(EllipsisLoc.isInvalid() &&
"Friend ellipsis but not friend-specified?");

if (DS.isExportSpecified())
mergeVisibilityType(Tag, DS.getExportSpecLoc(), VisibilityAttr::Default);

// Track whether this decl-specifier declares anything.
bool DeclaresAnything = true;

Expand Down Expand Up @@ -6515,6 +6518,9 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D,
if (!New)
return nullptr;

if (D.IsExport())
mergeVisibilityType(New, D.getExportLoc(), VisibilityAttr::Default);

warnOnCTypeHiddenInCPlusPlus(New);

// If this has an identifier and is not a function template specialization,
Expand Down Expand Up @@ -6754,6 +6760,9 @@ Sema::ActOnTypedefDeclarator(Scope* S, Declarator& D, DeclContext* DC,
return nullptr;
}

if (D.IsExport())
Diag(D.getName().StartLocation, diag::err_cannot_be_exported);

TypedefDecl *NewTD = ParseTypedefDecl(S, D, TInfo->getType(), TInfo);
if (!NewTD) return nullptr;

Expand Down Expand Up @@ -8213,6 +8222,9 @@ NamedDecl *Sema::ActOnVariableDeclarator(

ProcessPragmaWeak(S, NewVD);

if (D.IsExport() && !NewVD->hasExternalFormalLinkage())
Diag(D.getIdentifierLoc(), diag::err_cannot_be_exported);

// If this is the first declaration of an extern C variable, update
// the map of such variables.
if (NewVD->isFirstDecl() && !NewVD->isInvalidDecl() &&
Expand Down Expand Up @@ -10864,6 +10876,9 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
ProcessPragmaWeak(S, NewFD);
checkAttributesAfterMerging(*this, *NewFD);

if (D.IsExport() && !NewFD->hasExternalFormalLinkage())
Diag(D.getIdentifierLoc(), diag::err_cannot_be_exported);

AddKnownFunctionAttributes(NewFD);

if (NewFD->hasAttr<OverloadableAttr>() &&
Expand Down Expand Up @@ -15423,6 +15438,9 @@ Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D,
if (getLangOpts().OpenCL)
deduceOpenCLAddressSpace(New);

if (D.IsExport())
Diag(D.getIdentifierLoc(), diag::err_cannot_be_exported);

return New;
}

Expand Down Expand Up @@ -19005,6 +19023,9 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T,
PPC().CheckPPCMMAType(T, NewFD->getLocation()))
NewFD->setInvalidDecl();

if (D && D->IsExport())
Diag(D->getIdentifierLoc(), diag::err_cannot_be_exported);

NewFD->setAccess(AS);
return NewFD;
}
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2632,6 +2632,15 @@ static void handleExternalSourceSymbolAttr(Sema &S, Decl *D,
S.Context, AL, Language, DefinedIn, IsGeneratedDeclaration, USR));
}

void Sema::mergeVisibilityType(Decl *D, SourceLocation Loc,
VisibilityAttr::VisibilityType Value) {
if (VisibilityAttr *Attr = D->getAttr<VisibilityAttr>()) {
if (Attr->getVisibility() != Value)
Diag(Loc, diag::err_mismatched_visibility);
} else
D->addAttr(VisibilityAttr::CreateImplicit(Context, Value));
}

template <class T>
static T *mergeVisibilityAttr(Sema &S, Decl *D, const AttributeCommonInfo &CI,
typename T::VisibilityType value) {
Expand Down
23 changes: 23 additions & 0 deletions clang/test/CodeGen/attr-export.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// REQUIRES: systemz-registered-target
// RUN: %clang --target=s390x-none-zos -S -emit-llvm %s -o - | FileCheck %s

// Check the variables
// CHECK: @func_ptr = global ptr null, align 8
// CHECK: @var1 = global i32 0, align 4
// CHECK: @var2 = hidden global i32 0, align 4
// CHECK: @var3 = global i32 0, align 4
// CHECK: @var4 = hidden global i32 0, align 4
// CHECK: @var5 = global i32 0, align 4

// Check the functions
// CHECK: define void @foo1
// CHECK: define hidden void @foo2

int _Export var1;
int var2;
int _Export var3, var4, _Export var5;

void _Export foo1(){};
void foo2(){};

int (*_Export func_ptr)(void) = 0;
Loading
Loading