Skip to content

[SystemZ][z/OS] This change adds support for the PPA2 section in zOS #68926

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

Merged
merged 22 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from 11 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
1 change: 1 addition & 0 deletions clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ LANGOPT(C23 , 1, 0, "C23")
LANGOPT(MSVCCompat , 1, 0, "Microsoft Visual C++ full compatibility mode")
LANGOPT(Kernel , 1, 0, "Kernel mode")
LANGOPT(MicrosoftExt , 1, 0, "Microsoft C++ extensions")
LANGOPT(ASCIICharMode , 1, 1, "z/OS Language Environment Character mode")
LANGOPT(AsmBlocks , 1, 0, "Microsoft inline asm blocks")
LANGOPT(Borland , 1, 0, "Borland extensions")
LANGOPT(CPlusPlus , 1, 0, "C++")
Expand Down
34 changes: 34 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,40 @@ void CodeGenModule::Release() {
Context.getTypeSizeInChars(Context.getWideCharType()).getQuantity();
getModule().addModuleFlag(llvm::Module::Error, "wchar_size", WCharWidth);

if (getTriple().isOSzOS()) {
getModule().addModuleFlag(llvm::Module::Warning, "Product Major Version",
uint32_t(CLANG_VERSION_MAJOR));
getModule().addModuleFlag(llvm::Module::Warning, "Product Minor Version",
uint32_t(CLANG_VERSION_MINOR));
getModule().addModuleFlag(llvm::Module::Warning, "Product Patchlevel",
uint32_t(CLANG_VERSION_PATCHLEVEL));

// Record the language because we need it for the PPA2.
StringRef lang_str = languageToString(
LangStandard::getLangStandardForKind(LangOpts.LangStd).Language);
getModule().addModuleFlag(llvm::Module::Error, "zos_cu_language",
llvm::MDString::get(VMContext, lang_str));

std::string ProductId;
#ifdef CLANG_VENDOR
ProductId = #CLANG_VENDOR;
#else
ProductId = "clang";
#endif

getModule().addModuleFlag(llvm::Module::Error, "Product Id",
llvm::MDString::get(VMContext, ProductId));

getModule().addModuleFlag(llvm::Module::Error, "TranslationTime",
static_cast<uint64_t>(std::time(nullptr)));

getModule().addModuleFlag(
llvm::Module::Error, "zos_le_char_mode",
llvm::MDString::get(VMContext, Context.getLangOpts().ASCIICharMode
? "ascii"
: "ebcdic"));
}

llvm::Triple::ArchType Arch = Context.getTargetInfo().getTriple().getArch();
if ( Arch == llvm::Triple::arm
|| Arch == llvm::Triple::armeb
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1765,7 +1765,7 @@ void Clang::RenderTargetOptions(const llvm::Triple &EffectiveTriple,
break;

case llvm::Triple::systemz:
AddSystemZTargetArgs(Args, CmdArgs);
AddSystemZTargetArgs(EffectiveTriple, Args, CmdArgs);
break;

case llvm::Triple::x86:
Expand Down Expand Up @@ -2262,7 +2262,8 @@ void Clang::AddSparcTargetArgs(const ArgList &Args,
}
}

void Clang::AddSystemZTargetArgs(const ArgList &Args,
void Clang::AddSystemZTargetArgs(const llvm::Triple &Triple,
const ArgList &Args,
ArgStringList &CmdArgs) const {
if (const Arg *A = Args.getLastArg(options::OPT_mtune_EQ)) {
CmdArgs.push_back("-tune-cpu");
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Driver/ToolChains/Clang.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ class LLVM_LIBRARY_VISIBILITY Clang : public Tool {
llvm::opt::ArgStringList &CmdArgs) const;
void AddSparcTargetArgs(const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs) const;
void AddSystemZTargetArgs(const llvm::opt::ArgList &Args,
void AddSystemZTargetArgs(const llvm::Triple &Triple,
const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs) const;
void AddX86TargetArgs(const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs) const;
Expand Down
25 changes: 25 additions & 0 deletions clang/test/CodeGen/SystemZ/systemz-ppa2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Please note the following:
// + we are checking that the first bytes of the PPA2 are 0x3 0x0
// for C, and 0x3 0x1 for C++
// + the label for the PPA2 seems to vary on different versions.
// We try to cover all cases, and use substitution blocks to
// help write the tests. The contents of the PPA2 itself should
// not be different.
// + the [[:space:]] combines the two .byte lines into one pattern.
// This is necessary because if the lines were separated, the first
// .byte (i.e., the one for the 3) would, it seems, also match
// the .byte line below for the 34.

// RUN: %clang_cc1 -triple s390x-ibm-zos -xc -S -o - %s | FileCheck %s --check-prefix CHECK-C
// CHECK-C: [[PPA2:(.L)|(@@)PPA2]]:
// CHECK-C-NEXT: .byte 3{{[[:space:]]*}}.byte 0
// CHECK-C-NEXT: .byte 34{{$}}
// CHECK-C-NEXT: .byte {{4}}
// CHECK-C-NEXT: .long {{(CELQSTRT)}}-[[PPA2]]

// RUN: %clang_cc1 -triple s390x-ibm-zos -xc++ -S -o - %s | FileCheck %s --check-prefix CHECK-CXX
// CHECK-CXX: [[PPA2:(.L)|(@@)PPA2]]:
// CHECK-CXX-NEXT: .byte 3{{[[:space:]]*}}.byte 231
// CHECK-CXX-NEXT: .byte 34{{$}}
// CHECK-CXX-NEXT: .byte {{4}}
// CHECK-CXX-NEXT: .long {{(CELQSTRT)}}-[[PPA2]]
1 change: 1 addition & 0 deletions llvm/include/llvm/BinaryFormat/GOFF.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ enum ENDEntryPointRequest : uint8_t {
// \brief Subsections of the primary C_CODE section in the object file.
enum SubsectionKind : uint8_t {
SK_PPA1 = 2,
SK_PPA2 = 4,
};
} // end namespace GOFF

Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/MC/MCObjectFileInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,9 @@ class MCObjectFileInfo {

// GOFF specific sections.
MCSection *PPA1Section = nullptr;
MCSection *PPA2Section = nullptr;
MCSection *ADASection = nullptr;
MCSection *IDRLSection = nullptr;

// XCOFF specific sections
MCSection *TOCBaseSection = nullptr;
Expand Down Expand Up @@ -431,7 +433,9 @@ class MCObjectFileInfo {

// GOFF specific sections.
MCSection *getPPA1Section() const { return PPA1Section; }
MCSection *getPPA2Section() const { return PPA2Section; }
MCSection *getADASection() const { return ADASection; }
MCSection *getIDRLSection() const { return IDRLSection; }

// XCOFF specific sections
MCSection *getTOCBaseSection() const { return TOCBaseSection; }
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/MC/MCObjectFileInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -547,8 +547,13 @@ void MCObjectFileInfo::initGOFFMCObjectFileInfo(const Triple &T) {
PPA1Section =
Ctx->getGOFFSection(".ppa1", SectionKind::getMetadata(), TextSection,
MCConstantExpr::create(GOFF::SK_PPA1, *Ctx));
PPA2Section =
Ctx->getGOFFSection(".ppa2", SectionKind::getMetadata(), TextSection,
MCConstantExpr::create(GOFF::SK_PPA2, *Ctx));
ADASection =
Ctx->getGOFFSection(".ada", SectionKind::getData(), nullptr, nullptr);
IDRLSection =
Ctx->getGOFFSection("B_IDRL", SectionKind::getData(), nullptr, nullptr);
}

void MCObjectFileInfo::initCOFFMCObjectFileInfo(const Triple &T) {
Expand Down
200 changes: 199 additions & 1 deletion llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@
#include "llvm/MC/MCSectionELF.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/Chrono.h"
#include "llvm/Support/ConvertEBCDIC.h"
#include "llvm/Support/FormatProviders.h"
#include "llvm/Support/FormatVariadic.h"

using namespace llvm;

Expand Down Expand Up @@ -953,6 +956,7 @@ void SystemZAsmPrinter::emitEndOfAsmFile(Module &M) {
auto TT = OutContext.getTargetTriple();
if (TT.isOSzOS()) {
emitADASection();
emitIDRLSection(M);
}
emitAttributes(M);
}
Expand Down Expand Up @@ -1026,6 +1030,78 @@ void SystemZAsmPrinter::emitADASection() {
OutStreamer->popSection();
}

static uint32_t getProductVersion(Module &M) {
if (auto *VersionVal = cast_or_null<ConstantAsMetadata>(
M.getModuleFlag("Product Major Version")))
return cast<ConstantInt>(VersionVal->getValue())->getZExtValue();
return LLVM_VERSION_MAJOR;
}

static uint32_t getProductRelease(Module &M) {
if (auto *ReleaseVal = cast_or_null<ConstantAsMetadata>(
M.getModuleFlag("Product Minor Version")))
return cast<ConstantInt>(ReleaseVal->getValue())->getZExtValue();
return LLVM_VERSION_MINOR;
}

static uint32_t getProductPatch(Module &M) {
if (auto *PatchVal = cast_or_null<ConstantAsMetadata>(
M.getModuleFlag("Product Patchlevel")))
return cast<ConstantInt>(PatchVal->getValue())->getZExtValue();
return LLVM_VERSION_PATCH;
}

static time_t getTranslationTime(Module &M) {
std::time_t Time = 0;
if (auto *Val = cast_or_null<ConstantAsMetadata>(
M.getModuleFlag("TranslationTime"))) {
long SecondsSinceEpoch = cast<ConstantInt>(Val->getValue())->getSExtValue();
Time = static_cast<time_t>(SecondsSinceEpoch);
}
return Time;
}

void SystemZAsmPrinter::emitIDRLSection(Module &M) {
OutStreamer->pushSection();
OutStreamer->switchSection(getObjFileLowering().getIDRLSection());
constexpr unsigned IDRLDataLength = 30;
std::time_t Time = getTranslationTime(M);

uint32_t ProductVersion = getProductVersion(M);
uint32_t ProductRelease = getProductRelease(M);

std::string ProductID;
if (auto *MD = M.getModuleFlag("Product Id"))
ProductID = cast<MDString>(MD)->getString().str();

if (ProductID.empty()) {
char ProductIDFormatted[11]; // 10 + null.
snprintf(ProductIDFormatted, sizeof(ProductIDFormatted), "LLVM %02d%02d",
ProductVersion, ProductRelease);
ProductID = ProductIDFormatted;
}

// Remove - from Product Id, which makes it consistent with legacy.
// The binder expects alphanumeric characters only.
std::size_t DashFound = ProductID.find("-");
if (DashFound != std::string::npos)
ProductID.erase(ProductID.begin() + DashFound);

SmallString<IDRLDataLength + 1> TempStr;
raw_svector_ostream O(TempStr);
O << formatv("{0}{1,0-2:d}{2,0-2:d}{3:%Y-%m-%d %H:%M:%S}", ProductID.c_str(),
ProductVersion, ProductRelease, llvm::sys::toUtcTime(Time));
SmallString<IDRLDataLength> Data;

ConverterEBCDIC::convertToEBCDIC(TempStr, Data);

OutStreamer->emitInt8(0); // Reserved.
OutStreamer->emitInt8(3); // Format.
OutStreamer->emitInt16(IDRLDataLength); // Length.
OutStreamer->emitBytes(Data.str());
Copy link
Member

Choose a reason for hiding this comment

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

One more question about this string - the Length field is hardcoded to 30. Does that mean that there are exactly 30 bytes supposed to always follow here? I'm not sure this is guaranteed by the formatv above ...

Also, there doesn't seem to be any test that verifies the layout of this IDRL section, I think we definitely need one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added testing for the IDRL section and fixed the length to be 30 bytes.

OutStreamer->popSection();
}

void SystemZAsmPrinter::emitFunctionBodyEnd() {
if (TM.getTargetTriple().isOSzOS()) {
// Emit symbol for the end of function if the z/OS target streamer
Expand Down Expand Up @@ -1150,6 +1226,8 @@ static void emitPPA1Name(std::unique_ptr<MCStreamer> &OutStreamer,
}

void SystemZAsmPrinter::emitPPA1(MCSymbol *FnEndSym) {
assert(PPA2Sym != nullptr && "PPA2 Symbol not defined");

const TargetRegisterInfo *TRI = MF->getRegInfo().getTargetRegisterInfo();
const SystemZSubtarget &Subtarget = MF->getSubtarget<SystemZSubtarget>();
const auto TargetHasVector = Subtarget.hasVector();
Expand Down Expand Up @@ -1239,6 +1317,8 @@ void SystemZAsmPrinter::emitPPA1(MCSymbol *FnEndSym) {
OutStreamer->emitInt8(0xCE); // CEL signature.
OutStreamer->AddComment("Saved GPR Mask");
OutStreamer->emitInt16(SavedGPRMask);
OutStreamer->AddComment("Offset to PPA2");
OutStreamer->emitAbsoluteSymbolDiff(PPA2Sym, CurrentFnPPA1Sym, 4);

bool HasName =
MF->getFunction().hasName() && MF->getFunction().getName().size() > 0;
Expand Down Expand Up @@ -1296,6 +1376,124 @@ void SystemZAsmPrinter::emitPPA1(MCSymbol *FnEndSym) {
4);
}

void SystemZAsmPrinter::emitStartOfAsmFile(Module &M) {
if (TM.getTargetTriple().isOSzOS())
emitPPA2(M);
AsmPrinter::emitStartOfAsmFile(M);
}

void SystemZAsmPrinter::emitPPA2(Module &M) {
OutStreamer->pushSection();
OutStreamer->switchSection(getObjFileLowering().getPPA2Section());
MCContext &OutContext = OutStreamer->getContext();
// Make CELQSTRT symbol.
const char *StartSymbolName = "CELQSTRT";
MCSymbol *CELQSTRT = OutContext.getOrCreateSymbol(StartSymbolName);

// Create symbol and assign to class field for use in PPA1.
PPA2Sym = OutContext.createTempSymbol("PPA2", false);
MCSymbol *DateVersionSym = OutContext.createTempSymbol("DVS", false);

std::time_t Time = getTranslationTime(M);
SmallString<15> CompilationTime; // 14 + null
raw_svector_ostream O(CompilationTime);
O << formatv("{0:%Y%m%d%H%M%S}", llvm::sys::toUtcTime(Time));

uint32_t ProductVersion = getProductVersion(M),
ProductRelease = getProductRelease(M),
ProductPatch = getProductPatch(M);

SmallString<7> Version; // 6 + null
raw_svector_ostream ostr(Version);
ostr << formatv("{0,0-2:d}{1,0-2:d}{2,0-2:d}", ProductVersion, ProductRelease,
ProductPatch);

// Drop 0 during conversion.
SmallString<sizeof(CompilationTime) - 1> CompilationTimeStr;
SmallString<sizeof(Version) - 1> VersionStr;

ConverterEBCDIC::convertToEBCDIC(CompilationTime, CompilationTimeStr);
ConverterEBCDIC::convertToEBCDIC(Version, VersionStr);

enum class PPA2MemberId : uint8_t {
// See z/OS Language Environment Vendor Interfaces v2r5, p.23, for
// complete list. Only the C runtime is supported by this backend.
LE_C_Runtime = 3,
};
enum class PPA2MemberSubId : uint8_t {
// List of languages using the LE C runtime implementation.
C = 0x00,
CXX = 0x01,
Swift = 0x03,
Go = 0x60,
LLVMBasedLang = 0xe7,
};
// PPA2 Flags
enum class PPA2Flags : uint8_t {
CompileForBinaryFloatingPoint = 0x80,
CompiledWithXPLink = 0x01,
CompiledUnitASCII = 0x04,
HasServiceInfo = 0x20,
};

PPA2MemberSubId MemberSubId = PPA2MemberSubId::LLVMBasedLang;
if (auto *MD = M.getModuleFlag("zos_cu_language")) {
StringRef Language = cast<MDString>(MD)->getString();
MemberSubId = StringSwitch<PPA2MemberSubId>(Language)
.Case("C", PPA2MemberSubId::C)
.Case("CXX", PPA2MemberSubId::CXX)
.Case("Swift", PPA2MemberSubId::Swift)
.Case("Go", PPA2MemberSubId::Go)
.Default(PPA2MemberSubId::LLVMBasedLang);
}

// Emit PPA2 section.
OutStreamer->emitLabel(PPA2Sym);
OutStreamer->emitInt8(static_cast<uint8_t>(PPA2MemberId::LE_C_Runtime));
OutStreamer->emitInt8(static_cast<uint8_t>(MemberSubId));
OutStreamer->emitInt8(0x22); // Member defined, c370_plist+c370_env
OutStreamer->emitInt8(0x04); // Control level 4 (XPLink)
OutStreamer->emitAbsoluteSymbolDiff(CELQSTRT, PPA2Sym, 4);
OutStreamer->emitInt32(0x00000000);
OutStreamer->emitAbsoluteSymbolDiff(DateVersionSym, PPA2Sym, 4);
OutStreamer->emitInt32(
0x00000000); // Offset to main entry point, always 0 (so says TR).
uint8_t Flgs = static_cast<uint8_t>(PPA2Flags::CompileForBinaryFloatingPoint);
Flgs |= static_cast<uint8_t>(PPA2Flags::CompiledWithXPLink);

if (auto *MD = M.getModuleFlag("zos_le_char_mode")) {
const StringRef &CharMode = cast<MDString>(MD)->getString();
if (CharMode == "ascii") {
Flgs |= static_cast<uint8_t>(
PPA2Flags::CompiledUnitASCII); // Setting bit for ASCII char. mode.
} else if (CharMode != "ebcdic") {
report_fatal_error(
"Only ascii or ebcdic are valid values for zos_le_char_mode "
"metadata");
}
}

OutStreamer->emitInt8(Flgs);
OutStreamer->emitInt8(0x00); // Reserved.
// No MD5 signature before timestamp.
// No FLOAT(AFP(VOLATILE)).
// Remaining 5 flag bits reserved.
OutStreamer->emitInt16(0x0000); // 16 Reserved flag bits.

// Emit date and version section.
OutStreamer->emitLabel(DateVersionSym);
OutStreamer->emitBytes(CompilationTimeStr.str());
OutStreamer->emitBytes(VersionStr.str());

OutStreamer->emitInt16(0x0000); // Service level string length.

// Emit 8 byte alignment.
// Emit pointer to PPA2 label.
OutStreamer->AddComment("A(PPA2-CELQSTRT)");
OutStreamer->emitAbsoluteSymbolDiff(PPA2Sym, CELQSTRT, 8);
OutStreamer->popSection();
}

void SystemZAsmPrinter::emitFunctionEntryLabel() {
const SystemZSubtarget &Subtarget = MF->getSubtarget<SystemZSubtarget>();

Expand All @@ -1318,7 +1516,7 @@ void SystemZAsmPrinter::emitFunctionEntryLabel() {
uint32_t DSASize = MFFrame.getStackSize();
bool IsLeaf = DSASize == 0 && MFFrame.getCalleeSavedInfo().empty();

// Set Flags
// Set Flags.
uint8_t Flags = 0;
if (IsLeaf)
Flags |= 0x08;
Expand Down
Loading