diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index b1a6683a66bd0..8e88166241315 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -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)); + 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)); + + // 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)); + + time_t TT = PreprocessorOpts.SourceDateEpoch + ? *PreprocessorOpts.SourceDateEpoch + : std::time(nullptr); + getModule().addModuleFlag(llvm::Module::Max, "zos_translation_time", + static_cast(TT)); + + // Multiple modes will be supported here. + getModule().addModuleFlag(llvm::Module::Error, "zos_le_char_mode", + llvm::MDString::get(VMContext, "ascii")); + } + llvm::Triple::ArchType Arch = Context.getTargetInfo().getTriple().getArch(); if ( Arch == llvm::Triple::arm || Arch == llvm::Triple::armeb diff --git a/clang/test/CodeGen/SystemZ/systemz-ppa2.c b/clang/test/CodeGen/SystemZ/systemz-ppa2.c new file mode 100644 index 0000000000000..21ccd0d7b834c --- /dev/null +++ b/clang/test/CodeGen/SystemZ/systemz-ppa2.c @@ -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]] diff --git a/llvm/include/llvm/BinaryFormat/GOFF.h b/llvm/include/llvm/BinaryFormat/GOFF.h index f1a30e41b736b..443bcfc9479a8 100644 --- a/llvm/include/llvm/BinaryFormat/GOFF.h +++ b/llvm/include/llvm/BinaryFormat/GOFF.h @@ -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 diff --git a/llvm/include/llvm/MC/MCObjectFileInfo.h b/llvm/include/llvm/MC/MCObjectFileInfo.h index 54f696cb795fb..2b2adf5012def 100644 --- a/llvm/include/llvm/MC/MCObjectFileInfo.h +++ b/llvm/include/llvm/MC/MCObjectFileInfo.h @@ -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; @@ -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; } diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp index 0b5109e41e717..1b30645cea3c1 100644 --- a/llvm/lib/MC/MCObjectFileInfo.cpp +++ b/llvm/lib/MC/MCObjectFileInfo.cpp @@ -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) { diff --git a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp index b3075c150ebb3..df6f52449ac3c 100644 --- a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp +++ b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp @@ -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; @@ -953,6 +956,7 @@ void SystemZAsmPrinter::emitEndOfAsmFile(Module &M) { auto TT = OutContext.getTargetTriple(); if (TT.isOSzOS()) { emitADASection(); + emitIDRLSection(M); } emitAttributes(M); } @@ -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(MD)->getString().str(); + if (ProductID.empty()) + ProductID = "LLVM"; + return ProductID; +} + +static uint32_t getProductVersion(Module &M) { + if (auto *VersionVal = mdconst::extract_or_null( + 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( + 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( + 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( + M.getModuleFlag("zos_translation_time"))) { + long SecondsSinceEpoch = Val->getSExtValue(); + Time = static_cast(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 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 Data; + ConverterEBCDIC::convertToEBCDIC(TempStr, Data); + + OutStreamer->emitInt8(0); // Reserved. + OutStreamer->emitInt8(3); // Format. + OutStreamer->emitInt16(IDRLDataLength); // Length. + OutStreamer->emitBytes(Data.str()); + OutStreamer->popSection(); +} + void SystemZAsmPrinter::emitFunctionBodyEnd() { if (TM.getTargetTriple().isOSzOS()) { // Emit symbol for the end of function if the z/OS target streamer @@ -1150,6 +1220,8 @@ static void emitPPA1Name(std::unique_ptr &OutStreamer, } void SystemZAsmPrinter::emitPPA1(MCSymbol *FnEndSym) { + assert(PPA2Sym != nullptr && "PPA2 Symbol not defined"); + const TargetRegisterInfo *TRI = MF->getRegInfo().getTargetRegisterInfo(); const SystemZSubtarget &Subtarget = MF->getSubtarget(); const auto TargetHasVector = Subtarget.hasVector(); @@ -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; @@ -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 CompilationTimeStr; + SmallString 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(MD)->getString(); + MemberSubId = StringSwitch(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(PPA2MemberId::LE_C_Runtime)); + OutStreamer->emitInt8(static_cast(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(PPA2Flags::CompileForBinaryFloatingPoint); + Flgs |= static_cast(PPA2Flags::CompiledWithXPLink); + + if (auto *MD = M.getModuleFlag("zos_le_char_mode")) { + const StringRef &CharMode = cast(MD)->getString(); + if (CharMode == "ascii") { + Flgs |= static_cast( + 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(); @@ -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; diff --git a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.h b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.h index c9dbbfd0b4c43..303cce1a1b658 100644 --- a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.h +++ b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.h @@ -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(); @@ -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 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"; } @@ -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); diff --git a/llvm/test/CodeGen/SystemZ/zos-ppa2.ll b/llvm/test/CodeGen/SystemZ/zos-ppa2.ll new file mode 100644 index 0000000000000..f54f654b804a2 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/zos-ppa2.ll @@ -0,0 +1,31 @@ +; RUN: llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true < %s | FileCheck %s +; REQUIRES: systemz-registered-target + +; CHECK: .section ".ppa2" +; CHECK: @@PPA2: +; CHECK: .byte 3 +; CHECK: .byte 231 +; CHECK: .byte 34 +; CHECK: .byte 4 +; CHECK: .long CELQSTRT-@@PPA2 +; CHECK: .long 0 +; CHECK: .long @@DVS-@@PPA2 +; CHECK: .long 0 +; CHECK: .byte 129 +; CHECK: .byte 0 +; CHECK: .short 0 +; CHECK: @@DVS: +; CHECK: .ascii "\361\371\367\360\360\361\360\361\360\360\360\360\360\360" +; CHECK: .short 0 +; CHECK: .quad @@PPA2-CELQSTRT * A(PPA2-CELQSTRT) +; CHECK: @@PPA1_void_test_0: +; CHECK: .long @@PPA2-@@PPA1_void_test_0 * Offset to PPA2 +; CHECK: .section "B_IDRL" +; CHECK: .byte 0 +; CHECK: .byte 3 +; CHECK: .short 30 +; CHECK: .ascii "\323\323\345\324@@@@@@\361\370\360\360\361\371\367\360\360\361\360\361\360\360\360\360\360\360\360\360" +define void @void_test() { +entry: + ret void +}