Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
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
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@ LANGOPT(ConstantNSDictionaryLiterals , 1, 0, NotCompatible, "constant dictionary
LANGOPT(InlineVisibilityHidden , 1, 0, Benign, "hidden visibility for inline C++ methods")
ENUM_LANGOPT(DefaultVisibilityExportMapping, DefaultVisiblityExportMapping, 2, DefaultVisiblityExportMapping::None, Benign, "controls mapping of default visibility to dllexport")
LANGOPT(IgnoreXCOFFVisibility, 1, 0, Benign, "All the visibility attributes that are specified in the source code are ignored in aix XCOFF.")
ENUM_LANGOPT(ZOSPPA1Name, ZOSPPA1NameKind, 2, ZOSPPA1NameKind::Default, Benign,
"Emit the function name in PPA1 on z/OS.")
LANGOPT(VisibilityInlinesHiddenStaticLocalVar, 1, 0, Benign,
"hidden visibility for static local variables in inline C++ "
"methods when -fvisibility-inlines hidden is enabled")
Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,16 @@ class LangOptionsBase {
Microsoft = 2,
};

enum class ZOSPPA1NameKind {
/// Optimization for size level decides whether to emit the function name
/// in PPA1 on z/OS.
Default,
/// Emit the function name in PPA1 on z/OS.
Emit,
/// Do not emit the function name in PPA1 on z/OS.
NoEmit,
};

// Define simple language options (with no accessors).
#define LANGOPT(Name, Bits, Default, Compatibility, Description) \
unsigned Name : Bits;
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Options/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -6068,6 +6068,13 @@ defm backchain : BoolMOption<"backchain",
PosFlag<SetTrue, [], [ClangOption], "Link stack frames through backchain on System Z">,
NegFlag<SetFalse>, BothFlags<[], [ClangOption, CC1Option]>>;

def mzos_ppa1_name : Flag<["-"], "mzos-ppa1-name">, Group<m_Group>,
Flags<[TargetSpecific]>, Visibility<[ClangOption, CC1Option]>,
HelpText<"Emit the function name in PPA1 on z/OS">;
def mno_zos_ppa1_name : Flag<["-"], "mno-zos-ppa1-name">, Group<m_Group>,
Flags<[TargetSpecific]>, Visibility<[ClangOption, CC1Option]>,
HelpText<"Do not emit the function name in PPA1 on z/OS">;

def mno_warn_nonportable_cfstrings : Flag<["-"], "mno-warn-nonportable-cfstrings">, Group<m_Group>;
def mno_omit_leaf_frame_pointer : Flag<["-"], "mno-omit-leaf-frame-pointer">, Group<m_Group>;
def momit_leaf_frame_pointer : Flag<["-"], "momit-leaf-frame-pointer">, Group<m_Group>,
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/CodeGen/Targets/SystemZ.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,19 @@ class ZOSXPLinkTargetCodeGenInfo : public TargetCodeGenInfo {
SwiftInfo =
std::make_unique<SwiftABIInfo>(CGT, /*SwiftErrorInRegister=*/false);
}

void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
CodeGen::CodeGenModule &M) const override {
if (dyn_cast_or_null<FunctionDecl>(D)) {
if (auto *Fn = dyn_cast<llvm::Function>(GV)) {
auto ZOSPPA1Name = M.getLangOpts().getZOSPPA1Name();
if (ZOSPPA1Name == clang::LangOptions::ZOSPPA1NameKind::Emit)
Fn->addFnAttr("zos-ppa1-name", "true");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is a string, I'd use values more descriptive like "include"/"exclude". That will leave room for other values (eg. leaf or not). If it's a value can't you use true/false constants?

@sujianIBM sujianIBM Jun 29, 2026

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a string with a value.

My understanding (mainly from copilot)

LLVM supports 3 main kinds:

  1. Enum attributes (most common): Backed by Attribute::AttrKind enum
Attribute::NoInline
Attribute::ReadOnly
  1. String (target-dependent)
“target-cpu"="x86-64"
  1. Key-value pairs
align = 16

I am using the 2nd one.
The first one is over-killing in our case.
The 3rd one, I think the underneath type is int.

I will make the values more descriptive. Is it ok to use "emit" and "no-emit"? They match with LangOpts.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I'm fine with different values as long as those values that make the code in llvm that use them self documenting. A names like "always" and "none" could be better.

The llvm code never uses the false value. It would be good to add an assert to make sure the value is one of the two set here.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with different values as long as those values that make the code in llvm that use them self documenting. A names like "always" and "none" could be better.

Do you mean "zos-ppa1-name"=["always"|"none"]?
I actually feel they are quite confusing.
To start with, they are not a pair. "always" should be grouped with "sometimes" and "never", and "none" should be grouped with "single" and "multiple".
I don't think "emit" and "no-emit" are perfect, but they match with the LangOpts option. I'd like to use them if you don't object.

It would be good to add an assert to make sure the value is one of the two set here.

I don't follow very well.
Do you mean to add an assert of ZOSPPA1Name = M.getLangOpts().getZOSPPA1Name() only having clang::LangOptions::ZOSPPA1NameKind::Emit and clang::LangOptions::ZOSPPA1NameKind::NoEmit here in clang/lib/CodeGen/Targets/SystemZ.cpp?
I think the values ZOSPPA1Name can have are limited by enum class ZOSPPA1NameKind.
Or you want to add an assert in SystemZAsmPrinter::calculatePPA1() that the "zos-ppa1-name" attribute only has two values, in case this attribute is set somewhere else by mistake?

Thanks!

else if (ZOSPPA1Name == clang::LangOptions::ZOSPPA1NameKind::NoEmit)
Fn->addFnAttr("zos-ppa1-name", "false");
}
}
}
};

} // namespace
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Driver/ToolChains/ZOS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ void ZOS::addClangTargetOptions(const ArgList &DriverArgs,
if (!DriverArgs.hasArgNoClaim(options::OPT_fsized_deallocation,
options::OPT_fno_sized_deallocation))
CC1Args.push_back("-fno-sized-deallocation");

DriverArgs.AddLastArg(CC1Args, options::OPT_mzos_ppa1_name, options::OPT_mno_zos_ppa1_name);
}

void zos::Assembler::ConstructJob(Compilation &C, const JobAction &JA,
Expand Down
15 changes: 15 additions & 0 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3809,6 +3809,11 @@ void CompilerInvocationBase::GenerateLangArgs(const LangOptions &Opts,
if (Opts.IgnoreXCOFFVisibility)
GenerateArg(Consumer, OPT_mignore_xcoff_visibility);

if (Opts.getZOSPPA1Name() == LangOptions::ZOSPPA1NameKind::Emit)
GenerateArg(Consumer, OPT_mzos_ppa1_name);
else if (Opts.getZOSPPA1Name() == LangOptions::ZOSPPA1NameKind::NoEmit)
GenerateArg(Consumer, OPT_mno_zos_ppa1_name);

if (Opts.SignedOverflowBehavior == LangOptions::SOB_Trapping) {
GenerateArg(Consumer, OPT_ftrapv);
GenerateArg(Consumer, OPT_ftrapv_handler, Opts.OverflowHandler);
Expand Down Expand Up @@ -4227,6 +4232,16 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args,
if (T.isOSAIX() && (Args.hasArg(OPT_mignore_xcoff_visibility)))
Opts.IgnoreXCOFFVisibility = 1;

if (const Arg *A = Args.getLastArg(OPT_mzos_ppa1_name,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow the pattern AIX has above. If it is z/OS query the option and set the Opts. You don't need to handle the unsupported error if you follow that pattern.

If hasArg, getLastArg etc are not called you will get the unused arg warning on other platforms. That is consistent with other target specific options.

@sujianIBM sujianIBM Jun 29, 2026

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If hasArg, getLastArg etc are not called you will get the unused arg warning on other platforms. That is consistent with other target specific options.

What mechanism guarantees this in clang cc1?
My understanding is Flags<[TargetSpecific]> guarantees this in clang Driver, but not in cc1.

For the above AIX option, I tried this

>/plex/sujian/llvm/main/build/bin/clang -mignore-xcoff-visibility -target powerpc-unknown-linux main.c 
clang: error: unsupported option '-mignore-xcoff-visibility' for target 'powerpc-unknown-linux'

>/plex/sujian/llvm/main/build/bin/clang -cc1 -mignore-xcoff-visibility -triple powerpc-unknown-linux main.c
>

The definition of this option in clang/include/clang/Options/Options.td is

def mignore_xcoff_visibility : Flag<["-"], "mignore-xcoff-visibility">, Group<m_Group>,
HelpText<"Not emit the visibility attribute for asm in AIX OS or give all symbols 'unspecified' visibility in XCOFF object file">,
  Flags<[TargetSpecific]>, Visibility<[ClangOption, CC1Option]>;

It is handled in clang/lib/Driver/ToolChains/AIX.cpp

void AIX::addClangTargetOptions(
    const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CC1Args,
    BoundArch BA, Action::OffloadKind DeviceOffloadingKind) const {
  Args.AddLastArg(CC1Args, options::OPT_mignore_xcoff_visibility);

I think Flags<[TargetSpecific]> and AIX::addClangTargetOptions() guarantees an error is emitted on non-aix platforms when the option is specified to clang.
But there is no such mechanism in cc1. That aix option is silently ignored in cc1 if specified on non-aix platforms.

Thanks!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AIX:addClangTargetOptions() doesn't generate the error message. That passes the option on to cc1. Verify this but, it looks like the behaviour is any unclaimed options marked as target specific generate that error message.

Don't worry about generating an error from -cc1 for not z/OS systems. Just make sure you only check for the option if the target is z/OS. That will leave the option as unclaimed for non z/OS systems.

@sujianIBM sujianIBM Jun 29, 2026

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AIX:addClangTargetOptions() doesn't generate the error message.

I agree.
Any options with Flags<[TargetSpecific]> is only accepted on platforms handling it. An error is emitted on platforms that do not handle this option. This is done in clang Driver.
AIX:addClangTargetOptions() calls Args.AddLastArg(CC1Args, options::OPT_mignore_xcoff_visibility);, which makes this option handled on AIX. This avoids the error on AIX. Other platforms do not handle this option, and they would get the error.

So

I think Flags<[TargetSpecific]> and AIX::addClangTargetOptions() guarantees an error is emitted on non-aix platforms when the option is specified to clang.

@sujianIBM sujianIBM Jun 29, 2026

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verify this but, it looks like the behaviour is any unclaimed options marked as target specific generate that error message.

Yes, I agree. This happens only in clang Driver, but not in cc1.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't worry about generating an error from -cc1 for not z/OS systems. Just make sure you only check for the option if the target is z/OS.

I am emitting an error from cc1 because most downstream zos-only options do this.
We actually do not emit an error in Driver. We let Driver pass the option to cc1 and check there.
But upstream checks in Driver and let cc1 silently ignore the option if it is not on the platforms supporting it.
I will follow the upstream pattern. It also matches with lit test.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just make sure you only check for the option if the target is z/OS. That will leave the option as unclaimed for non z/OS systems.

I am not sure whether you are mixing clang Driver and cc1.
To "leave the option as unclaimed for non z/OS systems"

  • I do it in Driver by putting the handling in clang/lib/Driver/ToolChains/ZOS.cpp
  • I feel "unclaimed" is not a concept applied in cc1

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't worry about generating an error from -cc1 for not z/OS systems. Just make sure you only check for the option if the target is z/OS.

I have made this change.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just make sure you only check for the option if the target is z/OS. That will leave the option as unclaimed for non z/OS systems.

I am not sure whether you are mixing clang Driver and cc1. To "leave the option as unclaimed for non z/OS systems"

  • I do it in Driver by putting the handling in clang/lib/Driver/ToolChains/ZOS.cpp
  • I feel "unclaimed" is not a concept applied in cc1

That may be the case, but it's good to always follow the practice of checking the context before checking the option. Use "if zos and hasArg" vs "if hasArg and zos".

OPT_mno_zos_ppa1_name)) {
if (T.isOSzOS()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use following as it's faster, follows the usual convention and avoids marking an option as claimed when it isn't used.

if (T.isOSzOS()) {
  if (const Arg *A = Args.getLastArg(OPT_mzos_ppa1_name, OPT_mno_zos_ppa1_name)) {
    ...
  }
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! I will make the change. Thanks!

if (A->getOption().matches(OPT_mzos_ppa1_name))
Opts.setZOSPPA1Name(LangOptions::ZOSPPA1NameKind::Emit);
else
Opts.setZOSPPA1Name(LangOptions::ZOSPPA1NameKind::NoEmit);
}
}

if (Args.hasArg(OPT_ftrapv)) {
Opts.setSignedOverflowBehavior(LangOptions::SOB_Trapping);
// Set the handler, if one is specified.
Expand Down
19 changes: 19 additions & 0 deletions clang/test/CodeGen/SystemZ/zos-ppa1-name.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// REQUIRES: systemz-registered-target

// test default
Comment thread
sujianIBM marked this conversation as resolved.
// RUN: %clang_cc1 -triple s390x-ibm-zos -emit-llvm %s -o -\
// RUN: | FileCheck %s -check-prefix=DEFAULT

// test the positive and negative options
// RUN: %clang_cc1 -triple s390x-ibm-zos -mzos-ppa1-name -emit-llvm %s -o -\
// RUN: | FileCheck %s -check-prefix=EMIT-NAME
// RUN: %clang_cc1 -triple s390x-ibm-zos -mno-zos-ppa1-name -emit-llvm %s -o -\
// RUN: | FileCheck %s -check-prefix=NOT-EMIT-NAME

// DEFAULT-NOT: attributes #0 = {{{.*}}"zos-ppa1-name"{{.*}}}
// EMIT-NAME: attributes #0 = {{{.*}}"zos-ppa1-name"="true"{{.*}}}
// NOT-EMIT-NAME: attributes #0 = {{{.*}}"zos-ppa1-name"="false"{{.*}}}

int main() {
return 0;
}
15 changes: 15 additions & 0 deletions clang/test/Driver/zos-ppa1-name.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// REQUIRES: systemz-registered-target

// RUN: %clang -### -target s390x-ibm-zos -mzos-ppa1-name -c %s 2>&1 \
// RUN: | FileCheck -check-prefix=EMIT-NAME %s
// EMIT-NAME: "-mzos-ppa1-name"

// RUN: %clang -### -target s390x-ibm-zos -mno-zos-ppa1-name -c %s 2>&1 \
// RUN: | FileCheck -check-prefix=NOT-EMIT-NAME %s
// NOT-EMIT-NAME: "-mno-zos-ppa1-name"

// RUN: not %clang -target systemz-unknown-elf -mzos-ppa1-name -c %s 2>&1 \
// RUN: | FileCheck -check-prefix=ERR %s
// RUN: not %clang -target systemz-unknown-elf -mno-zos-ppa1-name -c %s 2>&1 \
// RUN: | FileCheck -check-prefix=ERR %s
// ERR: error: unsupported option '-m{{.*}}zos-ppa1-name' for target 'systemz-unknown-elf'
10 changes: 9 additions & 1 deletion llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1756,7 +1756,15 @@ void SystemZAsmPrinter::calculatePPA1() {
: "");

// Save the calculated values.
if (MF->getFunction().hasName())
// Whether to emit the function name is decided by optimization level,
// unless it is explicitly required via -m[no-]zos-ppa1-name options.
bool IncludeFunctionName = !MF->getFunction().hasMinSize();
if (MF->getFunction().hasFnAttribute("zos-ppa1-name")) {
auto ZOSPPA1Name =
MF->getFunction().getFnAttribute("zos-ppa1-name").getValueAsString();
IncludeFunctionName = ZOSPPA1Name == "true";
}
if (MF->getFunction().hasName() && IncludeFunctionName)
Info.Name = MF->getFunction().getName();
Info.PPA1 = OutContext.createTempSymbol(Twine("PPA1_").concat(N), true);
Info.EPMarker = OutContext.createTempSymbol(Twine("EPM_").concat(N), true);
Expand Down
54 changes: 54 additions & 0 deletions llvm/test/CodeGen/SystemZ/zos-ppa1.ll
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,57 @@ define void @void_test() {
entry:
ret void
}

; Attribute "zos-ppa1-name"="false" removes the function name from PPA1.
; CHECK: * PPA1
; CHECK-NEXT: L#PPA1_void_test_no_name_0 DS 0H
; CHECK: * PPA1 Flags 4
; CHECK-NEXT: DC XL1'80'
; CHECK-NEXT: * Length/4 of Parms
; CHECK-NEXT: DC XL2'0000'
; CHECK-NEXT: * Length of Code
; CHECK-NEXT: DC AD(L#void_test_no_name_end_0-L#EPM_void_test_no_name_0)
; CHECK-NEXT: DC AD(L#EPM_void_test_no_name_0-L#PPA1_void_test_no_name_0)
define void @void_test_no_name() #0 {
entry:
ret void
}
attributes #0 = { "zos-ppa1-name"="false" }

; Attribute minsize removes the function name from PPA1.
; CHECK: * PPA1
; CHECK-NEXT: L#PPA1_void_test_minsize_0 DS 0H
; CHECK: * PPA1 Flags 4
; CHECK-NEXT: DC XL1'80'
; CHECK-NEXT: * Length/4 of Parms
; CHECK-NEXT: DC XL2'0000'
; CHECK-NEXT: * Length of Code
; CHECK-NEXT: DC AD(L#void_test_minsize_end_0-L#EPM_void_test_minsize_0)
; CHECK-NEXT: DC AD(L#EPM_void_test_minsize_0-L#PPA1_void_test_minsize_0)
define void @void_test_minsize() #1 {
entry:
ret void
}
attributes #1 = { minsize }

; Attribute "zos-ppa1-name"="true" takes precedence over minsize,
; and thus emits the function name in PPA1.
; CHECK: * PPA1
; CHECK-NEXT: L#PPA1_void_test_name_0 DS 0H
; CHECK: * PPA1 Flags 4
; CHECK-NEXT: * Bit 7: 1 = Name Length and Name
; CHECK-NEXT: DC XL1'81'
; CHECK-NEXT: * Length/4 of Parms
; CHECK-NEXT: DC XL2'0000'
; CHECK-NEXT: * Length of Code
; CHECK-NEXT: DC AD(L#void_test_name_end_0-L#EPM_void_test_name_0)
; CHECK-NEXT: * Length of Name
; CHECK-NEXT: DC XL2'000E'
; CHECK-NEXT: * Name of Function
; CHECK-NEXT: DC XL14'A59689846DA385A2A36D95819485'
; CHECK-NEXT: DC AD(L#EPM_void_test_name_0-L#PPA1_void_test_name_0)
define void @void_test_name() #2 {
entry:
ret void
}
attributes #2 = { "zos-ppa1-name"="true" minsize }