Skip to content
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

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

Merged
merged 22 commits into from
Nov 27, 2023
Merged
35 changes: 35 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,41 @@ void CodeGenModule::Release() {
Context.getTypeSizeInChars(Context.getWideCharType()).getQuantity();
getModule().addModuleFlag(llvm::Module::Error, "wchar_size", WCharWidth);

if (getTriple().isOSzOS()) {
getModule().addModuleFlag(llvm::Module::Warning,
"zos_product_major_version",
uint32_t(CLANG_VERSION_MAJOR));
getModule().addModuleFlag(llvm::Module::Warning,
"zos_product_minor_version",
uint32_t(CLANG_VERSION_MINOR));
getModule().addModuleFlag(llvm::Module::Warning, "zos_product_patchlevel",
uint32_t(CLANG_VERSION_PATCHLEVEL));
uweigand marked this conversation as resolved.
Show resolved Hide resolved
std::string ProductId;
#ifdef CLANG_VENDOR
ProductId = #CLANG_VENDOR;
#else
ProductId = "clang";
#endif
getModule().addModuleFlag(llvm::Module::Error, "zos_product_id",
llvm::MDString::get(VMContext, ProductId));
ysyeda marked this conversation as resolved.
Show resolved Hide resolved

// 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));
ysyeda marked this conversation as resolved.
Show resolved Hide resolved

time_t TT = PreprocessorOpts.SourceDateEpoch
? *PreprocessorOpts.SourceDateEpoch
: std::time(nullptr);
getModule().addModuleFlag(llvm::Module::Max, "zos_translation_time",
static_cast<uint64_t>(TT));

// Multiple modes will be supported here.
getModule().addModuleFlag(llvm::Module::Error, "zos_le_char_mode",
llvm::MDString::get(VMContext, "ascii"));
ysyeda marked this conversation as resolved.
Show resolved Hide resolved
}

llvm::Triple::ArchType Arch = Context.getTargetInfo().getTriple().getArch();
if ( Arch == llvm::Triple::arm
|| Arch == llvm::Triple::armeb
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 1
// 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
194 changes: 193 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,72 @@ void SystemZAsmPrinter::emitADASection() {
OutStreamer->popSection();
}

static std::string getProductID(Module &M) {
std::string ProductID;
if (auto *MD = M.getModuleFlag("zos_product_id"))
ProductID = cast<MDString>(MD)->getString().str();
if (ProductID.empty())
ProductID = "LLVM";
return ProductID;
}

static uint32_t getProductVersion(Module &M) {
if (auto *VersionVal = mdconst::extract_or_null<ConstantInt>(
M.getModuleFlag("zos_product_major_version")))
return VersionVal->getZExtValue();
return LLVM_VERSION_MAJOR;
}

static uint32_t getProductRelease(Module &M) {
if (auto *ReleaseVal = mdconst::extract_or_null<ConstantInt>(
M.getModuleFlag("zos_product_minor_version")))
return ReleaseVal->getZExtValue();
return LLVM_VERSION_MINOR;
}

static uint32_t getProductPatch(Module &M) {
if (auto *PatchVal = mdconst::extract_or_null<ConstantInt>(
M.getModuleFlag("zos_product_patchlevel")))
return PatchVal->getZExtValue();
return LLVM_VERSION_PATCH;
}

static time_t getTranslationTime(Module &M) {
std::time_t Time = 0;
if (auto *Val = mdconst::extract_or_null<ConstantInt>(
M.getModuleFlag("zos_translation_time"))) {
long SecondsSinceEpoch = Val->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 = getProductID(M);

SmallString<IDRLDataLength + 1> TempStr;
raw_svector_ostream O(TempStr);
O << formatv("{0,-10}{1,0-2:d}{2,0-2:d}{3:%Y%m%d%H%M%S}{4,0-2}",
ProductID.substr(0, 10).c_str(), ProductVersion, ProductRelease,
llvm::sys::toUtcTime(Time), "0");
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 +1220,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 +1311,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 +1370,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("C++", 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")) {
ysyeda marked this conversation as resolved.
Show resolved Hide resolved
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 +1510,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
7 changes: 6 additions & 1 deletion llvm/lib/Target/SystemZ/SystemZAsmPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class LLVM_LIBRARY_VISIBILITY SystemZAsmPrinter : public AsmPrinter {
private:
MCSymbol *CurrentFnPPA1Sym; // PPA1 Symbol.
MCSymbol *CurrentFnEPMarkerSym; // Entry Point Marker.
MCSymbol *PPA2Sym;

SystemZTargetStreamer *getTargetStreamer() {
MCTargetStreamer *TS = OutStreamer->getTargetStreamer();
Expand Down Expand Up @@ -90,12 +91,15 @@ class LLVM_LIBRARY_VISIBILITY SystemZAsmPrinter : public AsmPrinter {
AssociatedDataAreaTable ADATable;

void emitPPA1(MCSymbol *FnEndSym);
void emitPPA2(Module &M);
void emitADASection();
void emitIDRLSection(Module &M);

public:
SystemZAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer)
: AsmPrinter(TM, std::move(Streamer)), CurrentFnPPA1Sym(nullptr),
CurrentFnEPMarkerSym(nullptr), ADATable(TM.getPointerSize(0)) {}
CurrentFnEPMarkerSym(nullptr), PPA2Sym(nullptr),
ADATable(TM.getPointerSize(0)) {}

// Override AsmPrinter.
StringRef getPassName() const override { return "SystemZ Assembly Printer"; }
Expand All @@ -113,6 +117,7 @@ class LLVM_LIBRARY_VISIBILITY SystemZAsmPrinter : public AsmPrinter {
}
void emitFunctionEntryLabel() override;
void emitFunctionBodyEnd() override;
void emitStartOfAsmFile(Module &M) override;

private:
void emitCallInformation(CallType CT);
Expand Down
Loading