| //===-- SystemZAsmPrinter.cpp - SystemZ LLVM assembly printer -------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // Streams SystemZ assembly language and associated data, in the form of | |
| // MCInsts and MCExprs respectively. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "SystemZAsmPrinter.h" | |
| #include "MCTargetDesc/SystemZGNUInstPrinter.h" | |
| #include "MCTargetDesc/SystemZHLASMInstPrinter.h" | |
| #include "MCTargetDesc/SystemZMCAsmInfo.h" | |
| #include "MCTargetDesc/SystemZMCTargetDesc.h" | |
| #include "SystemZConstantPoolValue.h" | |
| #include "SystemZMCInstLower.h" | |
| #include "TargetInfo/SystemZTargetInfo.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/BinaryFormat/ELF.h" | |
| #include "llvm/BinaryFormat/GOFF.h" | |
| #include "llvm/CodeGen/MachineModuleInfoImpls.h" | |
| #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" | |
| #include "llvm/IR/GlobalVariable.h" | |
| #include "llvm/IR/Mangler.h" | |
| #include "llvm/IR/Module.h" | |
| #include "llvm/MC/MCDirectives.h" | |
| #include "llvm/MC/MCExpr.h" | |
| #include "llvm/MC/MCInstBuilder.h" | |
| #include "llvm/MC/MCSectionELF.h" | |
| #include "llvm/MC/MCStreamer.h" | |
| #include "llvm/MC/MCSymbolGOFF.h" | |
| #include "llvm/MC/TargetRegistry.h" | |
| #include "llvm/Support/Chrono.h" | |
| #include "llvm/Support/Compiler.h" | |
| #include "llvm/Support/ConvertEBCDIC.h" | |
| #include "llvm/Support/FormatVariadic.h" | |
| using namespace llvm; | |
| // Return an RI instruction like MI with opcode Opcode, but with the | |
| // GR64 register operands turned into GR32s. | |
| static MCInst lowerRILow(const MachineInstr *MI, unsigned Opcode) { | |
| if (MI->isCompare()) | |
| return MCInstBuilder(Opcode) | |
| .addReg(SystemZMC::getRegAsGR32(MI->getOperand(0).getReg())) | |
| .addImm(MI->getOperand(1).getImm()); | |
| else | |
| return MCInstBuilder(Opcode) | |
| .addReg(SystemZMC::getRegAsGR32(MI->getOperand(0).getReg())) | |
| .addReg(SystemZMC::getRegAsGR32(MI->getOperand(1).getReg())) | |
| .addImm(MI->getOperand(2).getImm()); | |
| } | |
| // Return an RI instruction like MI with opcode Opcode, but with the | |
| // GR64 register operands turned into GRH32s. | |
| static MCInst lowerRIHigh(const MachineInstr *MI, unsigned Opcode) { | |
| if (MI->isCompare()) | |
| return MCInstBuilder(Opcode) | |
| .addReg(SystemZMC::getRegAsGRH32(MI->getOperand(0).getReg())) | |
| .addImm(MI->getOperand(1).getImm()); | |
| else | |
| return MCInstBuilder(Opcode) | |
| .addReg(SystemZMC::getRegAsGRH32(MI->getOperand(0).getReg())) | |
| .addReg(SystemZMC::getRegAsGRH32(MI->getOperand(1).getReg())) | |
| .addImm(MI->getOperand(2).getImm()); | |
| } | |
| // Return an RI instruction like MI with opcode Opcode, but with the | |
| // R2 register turned into a GR64. | |
| static MCInst lowerRIEfLow(const MachineInstr *MI, unsigned Opcode) { | |
| return MCInstBuilder(Opcode) | |
| .addReg(MI->getOperand(0).getReg()) | |
| .addReg(MI->getOperand(1).getReg()) | |
| .addReg(SystemZMC::getRegAsGR64(MI->getOperand(2).getReg())) | |
| .addImm(MI->getOperand(3).getImm()) | |
| .addImm(MI->getOperand(4).getImm()) | |
| .addImm(MI->getOperand(5).getImm()); | |
| } | |
| static const MCSymbolRefExpr *getTLSGetOffset(MCContext &Context) { | |
| StringRef Name = "__tls_get_offset"; | |
| return MCSymbolRefExpr::create(Context.getOrCreateSymbol(Name), | |
| SystemZ::S_PLT, Context); | |
| } | |
| static const MCSymbolRefExpr *getGlobalOffsetTable(MCContext &Context) { | |
| StringRef Name = "_GLOBAL_OFFSET_TABLE_"; | |
| return MCSymbolRefExpr::create(Context.getOrCreateSymbol(Name), | |
| Context); | |
| } | |
| // MI is an instruction that accepts an optional alignment hint, | |
| // and which was already lowered to LoweredMI. If the alignment | |
| // of the original memory operand is known, update LoweredMI to | |
| // an instruction with the corresponding hint set. | |
| static void lowerAlignmentHint(const MachineInstr *MI, MCInst &LoweredMI, | |
| unsigned Opcode) { | |
| if (MI->memoperands_empty()) | |
| return; | |
| Align Alignment = Align(16); | |
| for (MachineInstr::mmo_iterator MMOI = MI->memoperands_begin(), | |
| EE = MI->memoperands_end(); MMOI != EE; ++MMOI) | |
| if ((*MMOI)->getAlign() < Alignment) | |
| Alignment = (*MMOI)->getAlign(); | |
| unsigned AlignmentHint = 0; | |
| if (Alignment >= Align(16)) | |
| AlignmentHint = 4; | |
| else if (Alignment >= Align(8)) | |
| AlignmentHint = 3; | |
| if (AlignmentHint == 0) | |
| return; | |
| LoweredMI.setOpcode(Opcode); | |
| LoweredMI.addOperand(MCOperand::createImm(AlignmentHint)); | |
| } | |
| // MI loads the high part of a vector from memory. Return an instruction | |
| // that uses replicating vector load Opcode to do the same thing. | |
| static MCInst lowerSubvectorLoad(const MachineInstr *MI, unsigned Opcode) { | |
| return MCInstBuilder(Opcode) | |
| .addReg(SystemZMC::getRegAsVR128(MI->getOperand(0).getReg())) | |
| .addReg(MI->getOperand(1).getReg()) | |
| .addImm(MI->getOperand(2).getImm()) | |
| .addReg(MI->getOperand(3).getReg()); | |
| } | |
| // MI stores the high part of a vector to memory. Return an instruction | |
| // that uses elemental vector store Opcode to do the same thing. | |
| static MCInst lowerSubvectorStore(const MachineInstr *MI, unsigned Opcode) { | |
| return MCInstBuilder(Opcode) | |
| .addReg(SystemZMC::getRegAsVR128(MI->getOperand(0).getReg())) | |
| .addReg(MI->getOperand(1).getReg()) | |
| .addImm(MI->getOperand(2).getImm()) | |
| .addReg(MI->getOperand(3).getReg()) | |
| .addImm(0); | |
| } | |
| // MI extracts the first element of the source vector. | |
| static MCInst lowerVecEltExtraction(const MachineInstr *MI, unsigned Opcode) { | |
| return MCInstBuilder(Opcode) | |
| .addReg(SystemZMC::getRegAsGR64(MI->getOperand(0).getReg())) | |
| .addReg(SystemZMC::getRegAsVR128(MI->getOperand(1).getReg())) | |
| .addReg(0) | |
| .addImm(0); | |
| } | |
| // MI inserts value into the first element of the destination vector. | |
| static MCInst lowerVecEltInsertion(const MachineInstr *MI, unsigned Opcode) { | |
| return MCInstBuilder(Opcode) | |
| .addReg(SystemZMC::getRegAsVR128(MI->getOperand(0).getReg())) | |
| .addReg(SystemZMC::getRegAsVR128(MI->getOperand(0).getReg())) | |
| .addReg(MI->getOperand(1).getReg()) | |
| .addReg(0) | |
| .addImm(0); | |
| } | |
| bool SystemZAsmPrinter::doInitialization(Module &M) { | |
| SM.reset(); | |
| // In HLASM, the only way to represent aliases is to use the | |
| // extra-label-at-definition strategy. This is similar to the AIX | |
| // implementation with the additional caveat that all symbol attributes must | |
| // be emitted before the label is emitted. | |
| if (TM.getTargetTriple().isOSzOS()) { | |
| // Construct an aliasing list for each GlobalObject. | |
| for (const auto &Alias : M.aliases()) { | |
| const GlobalObject *Aliasee = Alias.getAliaseeObject(); | |
| if (!Aliasee) | |
| OutContext.reportError( | |
| {}, "Alias without a base object is not yet supported on z/OS."); | |
| bool IsFunc = isa<Function>(Aliasee->stripPointerCasts()); | |
| if (IsFunc) { | |
| if (Alias.hasWeakLinkage() || Alias.hasLinkOnceLinkage()) | |
| OutContext.reportError({}, | |
| "Weak alias/reference not supported on z/OS"); | |
| GOAliasMap[Aliasee].push_back(&Alias); | |
| } else | |
| OutContext.reportError( | |
| {}, "Only aliases to functions is supported in GOFF."); | |
| } | |
| } | |
| return AsmPrinter::doInitialization(M); | |
| } | |
| // The XPLINK ABI requires that a no-op encoding the call type is emitted after | |
| // each call to a subroutine. This information can be used by the called | |
| // function to determine its entry point, e.g. for generating a backtrace. The | |
| // call type is encoded as a register number in the bcr instruction. See | |
| // enumeration CallType for the possible values. | |
| void SystemZAsmPrinter::emitCallInformation(CallType CT) { | |
| EmitToStreamer(*OutStreamer, | |
| MCInstBuilder(SystemZ::BCRAsm) | |
| .addImm(0) | |
| .addReg(SystemZMC::GR64Regs[static_cast<unsigned>(CT)])); | |
| } | |
| uint32_t SystemZAsmPrinter::AssociatedDataAreaTable::insert(const MCSymbol *Sym, | |
| unsigned SlotKind) { | |
| auto Key = std::make_pair(Sym, SlotKind); | |
| auto It = Displacements.find(Key); | |
| if (It != Displacements.end()) | |
| return (*It).second; | |
| // Determine length of descriptor. | |
| uint32_t Length; | |
| switch (SlotKind) { | |
| case SystemZII::MO_ADA_DIRECT_FUNC_DESC: | |
| Length = 2 * PointerSize; | |
| break; | |
| default: | |
| Length = PointerSize; | |
| break; | |
| } | |
| uint32_t Displacement = NextDisplacement; | |
| Displacements[std::make_pair(Sym, SlotKind)] = NextDisplacement; | |
| NextDisplacement += Length; | |
| return Displacement; | |
| } | |
| uint32_t | |
| SystemZAsmPrinter::AssociatedDataAreaTable::insert(const MachineOperand MO) { | |
| MCSymbol *Sym; | |
| if (MO.getType() == MachineOperand::MO_GlobalAddress) { | |
| const GlobalValue *GV = MO.getGlobal(); | |
| Sym = MO.getParent()->getMF()->getTarget().getSymbol(GV); | |
| assert(Sym && "No symbol"); | |
| } else if (MO.getType() == MachineOperand::MO_ExternalSymbol) { | |
| const char *SymName = MO.getSymbolName(); | |
| Sym = MO.getParent()->getMF()->getContext().getOrCreateSymbol(SymName); | |
| assert(Sym && "No symbol"); | |
| } else | |
| llvm_unreachable("Unexpected operand type"); | |
| unsigned ADAslotType = MO.getTargetFlags(); | |
| return insert(Sym, ADAslotType); | |
| } | |
| void SystemZAsmPrinter::emitInstruction(const MachineInstr *MI) { | |
| SystemZ_MC::verifyInstructionPredicates(MI->getOpcode(), | |
| getSubtargetInfo().getFeatureBits()); | |
| SystemZMCInstLower Lower(MF->getContext(), *this); | |
| MCInst LoweredMI; | |
| switch (MI->getOpcode()) { | |
| case SystemZ::Return: | |
| LoweredMI = MCInstBuilder(SystemZ::BR) | |
| .addReg(SystemZ::R14D); | |
| break; | |
| case SystemZ::Return_XPLINK: | |
| LoweredMI = MCInstBuilder(SystemZ::B) | |
| .addReg(SystemZ::R7D) | |
| .addImm(2) | |
| .addReg(0); | |
| break; | |
| case SystemZ::CondReturn: | |
| LoweredMI = MCInstBuilder(SystemZ::BCR) | |
| .addImm(MI->getOperand(0).getImm()) | |
| .addImm(MI->getOperand(1).getImm()) | |
| .addReg(SystemZ::R14D); | |
| break; | |
| case SystemZ::CondReturn_XPLINK: | |
| LoweredMI = MCInstBuilder(SystemZ::BC) | |
| .addImm(MI->getOperand(0).getImm()) | |
| .addImm(MI->getOperand(1).getImm()) | |
| .addReg(SystemZ::R7D) | |
| .addImm(2) | |
| .addReg(0); | |
| break; | |
| case SystemZ::CRBReturn: | |
| LoweredMI = MCInstBuilder(SystemZ::CRB) | |
| .addReg(MI->getOperand(0).getReg()) | |
| .addReg(MI->getOperand(1).getReg()) | |
| .addImm(MI->getOperand(2).getImm()) | |
| .addReg(SystemZ::R14D) | |
| .addImm(0); | |
| break; | |
| case SystemZ::CGRBReturn: | |
| LoweredMI = MCInstBuilder(SystemZ::CGRB) | |
| .addReg(MI->getOperand(0).getReg()) | |
| .addReg(MI->getOperand(1).getReg()) | |
| .addImm(MI->getOperand(2).getImm()) | |
| .addReg(SystemZ::R14D) | |
| .addImm(0); | |
| break; | |
| case SystemZ::CIBReturn: | |
| LoweredMI = MCInstBuilder(SystemZ::CIB) | |
| .addReg(MI->getOperand(0).getReg()) | |
| .addImm(MI->getOperand(1).getImm()) | |
| .addImm(MI->getOperand(2).getImm()) | |
| .addReg(SystemZ::R14D) | |
| .addImm(0); | |
| break; | |
| case SystemZ::CGIBReturn: | |
| LoweredMI = MCInstBuilder(SystemZ::CGIB) | |
| .addReg(MI->getOperand(0).getReg()) | |
| .addImm(MI->getOperand(1).getImm()) | |
| .addImm(MI->getOperand(2).getImm()) | |
| .addReg(SystemZ::R14D) | |
| .addImm(0); | |
| break; | |
| case SystemZ::CLRBReturn: | |
| LoweredMI = MCInstBuilder(SystemZ::CLRB) | |
| .addReg(MI->getOperand(0).getReg()) | |
| .addReg(MI->getOperand(1).getReg()) | |
| .addImm(MI->getOperand(2).getImm()) | |
| .addReg(SystemZ::R14D) | |
| .addImm(0); | |
| break; | |
| case SystemZ::CLGRBReturn: | |
| LoweredMI = MCInstBuilder(SystemZ::CLGRB) | |
| .addReg(MI->getOperand(0).getReg()) | |
| .addReg(MI->getOperand(1).getReg()) | |
| .addImm(MI->getOperand(2).getImm()) | |
| .addReg(SystemZ::R14D) | |
| .addImm(0); | |
| break; | |
| case SystemZ::CLIBReturn: | |
| LoweredMI = MCInstBuilder(SystemZ::CLIB) | |
| .addReg(MI->getOperand(0).getReg()) | |
| .addImm(MI->getOperand(1).getImm()) | |
| .addImm(MI->getOperand(2).getImm()) | |
| .addReg(SystemZ::R14D) | |
| .addImm(0); | |
| break; | |
| case SystemZ::CLGIBReturn: | |
| LoweredMI = MCInstBuilder(SystemZ::CLGIB) | |
| .addReg(MI->getOperand(0).getReg()) | |
| .addImm(MI->getOperand(1).getImm()) | |
| .addImm(MI->getOperand(2).getImm()) | |
| .addReg(SystemZ::R14D) | |
| .addImm(0); | |
| break; | |
| case SystemZ::CallBRASL_XPLINK64: | |
| EmitToStreamer(*OutStreamer, MCInstBuilder(SystemZ::BRASL) | |
| .addReg(SystemZ::R7D) | |
| .addExpr(Lower.getExpr(MI->getOperand(0), | |
| SystemZ::S_None))); | |
| emitCallInformation(CallType::BRASL7); | |
| return; | |
| case SystemZ::CallBASR_XPLINK64: | |
| EmitToStreamer(*OutStreamer, MCInstBuilder(SystemZ::BASR) | |
| .addReg(SystemZ::R7D) | |
| .addReg(MI->getOperand(0).getReg())); | |
| emitCallInformation(CallType::BASR76); | |
| return; | |
| case SystemZ::CallBASR_STACKEXT: | |
| EmitToStreamer(*OutStreamer, MCInstBuilder(SystemZ::BASR) | |
| .addReg(SystemZ::R3D) | |
| .addReg(MI->getOperand(0).getReg())); | |
| emitCallInformation(CallType::BASR33); | |
| return; | |
| case SystemZ::ADA_ENTRY_VALUE: | |
| case SystemZ::ADA_ENTRY: { | |
| const SystemZSubtarget &Subtarget = MF->getSubtarget<SystemZSubtarget>(); | |
| const SystemZInstrInfo *TII = Subtarget.getInstrInfo(); | |
| uint32_t Disp = ADATable.insert(MI->getOperand(1)); | |
| Register TargetReg = MI->getOperand(0).getReg(); | |
| Register ADAReg = MI->getOperand(2).getReg(); | |
| Disp += MI->getOperand(3).getImm(); | |
| bool LoadAddr = MI->getOpcode() == SystemZ::ADA_ENTRY; | |
| unsigned Op0 = LoadAddr ? SystemZ::LA : SystemZ::LG; | |
| unsigned Op = TII->getOpcodeForOffset(Op0, Disp); | |
| Register IndexReg = 0; | |
| if (!Op) { | |
| if (TargetReg != ADAReg) { | |
| IndexReg = TargetReg; | |
| // Use TargetReg to store displacement. | |
| EmitToStreamer( | |
| *OutStreamer, | |
| MCInstBuilder(SystemZ::LLILF).addReg(TargetReg).addImm(Disp)); | |
| } else | |
| EmitToStreamer(*OutStreamer, MCInstBuilder(SystemZ::ALGFI) | |
| .addReg(TargetReg) | |
| .addReg(TargetReg) | |
| .addImm(Disp)); | |
| Disp = 0; | |
| Op = Op0; | |
| } | |
| EmitToStreamer(*OutStreamer, MCInstBuilder(Op) | |
| .addReg(TargetReg) | |
| .addReg(ADAReg) | |
| .addImm(Disp) | |
| .addReg(IndexReg)); | |
| return; | |
| } | |
| case SystemZ::CallBRASL: | |
| LoweredMI = MCInstBuilder(SystemZ::BRASL) | |
| .addReg(SystemZ::R14D) | |
| .addExpr(Lower.getExpr(MI->getOperand(0), SystemZ::S_PLT)); | |
| break; | |
| case SystemZ::CallBASR: | |
| LoweredMI = MCInstBuilder(SystemZ::BASR) | |
| .addReg(SystemZ::R14D) | |
| .addReg(MI->getOperand(0).getReg()); | |
| break; | |
| case SystemZ::CallJG: | |
| LoweredMI = MCInstBuilder(SystemZ::JG) | |
| .addExpr(Lower.getExpr(MI->getOperand(0), SystemZ::S_PLT)); | |
| break; | |
| case SystemZ::CallBRCL: | |
| LoweredMI = MCInstBuilder(SystemZ::BRCL) | |
| .addImm(MI->getOperand(0).getImm()) | |
| .addImm(MI->getOperand(1).getImm()) | |
| .addExpr(Lower.getExpr(MI->getOperand(2), SystemZ::S_PLT)); | |
| break; | |
| case SystemZ::CallBR: | |
| LoweredMI = MCInstBuilder(SystemZ::BR) | |
| .addReg(MI->getOperand(0).getReg()); | |
| break; | |
| case SystemZ::CallBCR: | |
| LoweredMI = MCInstBuilder(SystemZ::BCR) | |
| .addImm(MI->getOperand(0).getImm()) | |
| .addImm(MI->getOperand(1).getImm()) | |
| .addReg(MI->getOperand(2).getReg()); | |
| break; | |
| case SystemZ::CRBCall: | |
| LoweredMI = MCInstBuilder(SystemZ::CRB) | |
| .addReg(MI->getOperand(0).getReg()) | |
| .addReg(MI->getOperand(1).getReg()) | |
| .addImm(MI->getOperand(2).getImm()) | |
| .addReg(MI->getOperand(3).getReg()) | |
| .addImm(0); | |
| break; | |
| case SystemZ::CGRBCall: | |
| LoweredMI = MCInstBuilder(SystemZ::CGRB) | |
| .addReg(MI->getOperand(0).getReg()) | |
| .addReg(MI->getOperand(1).getReg()) | |
| .addImm(MI->getOperand(2).getImm()) | |
| .addReg(MI->getOperand(3).getReg()) | |
| .addImm(0); | |
| break; | |
| case SystemZ::CIBCall: | |
| LoweredMI = MCInstBuilder(SystemZ::CIB) | |
| .addReg(MI->getOperand(0).getReg()) | |
| .addImm(MI->getOperand(1).getImm()) | |
| .addImm(MI->getOperand(2).getImm()) | |
| .addReg(MI->getOperand(3).getReg()) | |
| .addImm(0); | |
| break; | |
| case SystemZ::CGIBCall: | |
| LoweredMI = MCInstBuilder(SystemZ::CGIB) | |
| .addReg(MI->getOperand(0).getReg()) | |
| .addImm(MI->getOperand(1).getImm()) | |
| .addImm(MI->getOperand(2).getImm()) | |
| .addReg(MI->getOperand(3).getReg()) | |
| .addImm(0); | |
| break; | |
| case SystemZ::CLRBCall: | |
| LoweredMI = MCInstBuilder(SystemZ::CLRB) | |
| .addReg(MI->getOperand(0).getReg()) | |
| .addReg(MI->getOperand(1).getReg()) | |
| .addImm(MI->getOperand(2).getImm()) | |
| .addReg(MI->getOperand(3).getReg()) | |
| .addImm(0); | |
| break; | |
| case SystemZ::CLGRBCall: | |
| LoweredMI = MCInstBuilder(SystemZ::CLGRB) | |
| .addReg(MI->getOperand(0).getReg()) | |
| .addReg(MI->getOperand(1).getReg()) | |
| .addImm(MI->getOperand(2).getImm()) | |
| .addReg(MI->getOperand(3).getReg()) | |
| .addImm(0); | |
| break; | |
| case SystemZ::CLIBCall: | |
| LoweredMI = MCInstBuilder(SystemZ::CLIB) | |
| .addReg(MI->getOperand(0).getReg()) | |
| .addImm(MI->getOperand(1).getImm()) | |
| .addImm(MI->getOperand(2).getImm()) | |
| .addReg(MI->getOperand(3).getReg()) | |
| .addImm(0); | |
| break; | |
| case SystemZ::CLGIBCall: | |
| LoweredMI = MCInstBuilder(SystemZ::CLGIB) | |
| .addReg(MI->getOperand(0).getReg()) | |
| .addImm(MI->getOperand(1).getImm()) | |
| .addImm(MI->getOperand(2).getImm()) | |
| .addReg(MI->getOperand(3).getReg()) | |
| .addImm(0); | |
| break; | |
| case SystemZ::TLS_GDCALL: | |
| LoweredMI = | |
| MCInstBuilder(SystemZ::BRASL) | |
| .addReg(SystemZ::R14D) | |
| .addExpr(getTLSGetOffset(MF->getContext())) | |
| .addExpr(Lower.getExpr(MI->getOperand(0), SystemZ::S_TLSGD)); | |
| break; | |
| case SystemZ::TLS_LDCALL: | |
| LoweredMI = | |
| MCInstBuilder(SystemZ::BRASL) | |
| .addReg(SystemZ::R14D) | |
| .addExpr(getTLSGetOffset(MF->getContext())) | |
| .addExpr(Lower.getExpr(MI->getOperand(0), SystemZ::S_TLSLDM)); | |
| break; | |
| case SystemZ::GOT: | |
| LoweredMI = MCInstBuilder(SystemZ::LARL) | |
| .addReg(MI->getOperand(0).getReg()) | |
| .addExpr(getGlobalOffsetTable(MF->getContext())); | |
| break; | |
| case SystemZ::IILF64: | |
| LoweredMI = MCInstBuilder(SystemZ::IILF) | |
| .addReg(SystemZMC::getRegAsGR32(MI->getOperand(0).getReg())) | |
| .addImm(MI->getOperand(2).getImm()); | |
| break; | |
| case SystemZ::IIHF64: | |
| LoweredMI = MCInstBuilder(SystemZ::IIHF) | |
| .addReg(SystemZMC::getRegAsGRH32(MI->getOperand(0).getReg())) | |
| .addImm(MI->getOperand(2).getImm()); | |
| break; | |
| case SystemZ::RISBHH: | |
| case SystemZ::RISBHL: | |
| LoweredMI = lowerRIEfLow(MI, SystemZ::RISBHG); | |
| break; | |
| case SystemZ::RISBLH: | |
| case SystemZ::RISBLL: | |
| LoweredMI = lowerRIEfLow(MI, SystemZ::RISBLG); | |
| break; | |
| case SystemZ::VLVGP32: | |
| LoweredMI = MCInstBuilder(SystemZ::VLVGP) | |
| .addReg(MI->getOperand(0).getReg()) | |
| .addReg(SystemZMC::getRegAsGR64(MI->getOperand(1).getReg())) | |
| .addReg(SystemZMC::getRegAsGR64(MI->getOperand(2).getReg())); | |
| break; | |
| case SystemZ::VLR16: | |
| case SystemZ::VLR32: | |
| case SystemZ::VLR64: | |
| LoweredMI = MCInstBuilder(SystemZ::VLR) | |
| .addReg(SystemZMC::getRegAsVR128(MI->getOperand(0).getReg())) | |
| .addReg(SystemZMC::getRegAsVR128(MI->getOperand(1).getReg())); | |
| break; | |
| case SystemZ::VL: | |
| Lower.lower(MI, LoweredMI); | |
| lowerAlignmentHint(MI, LoweredMI, SystemZ::VLAlign); | |
| break; | |
| case SystemZ::VST: | |
| Lower.lower(MI, LoweredMI); | |
| lowerAlignmentHint(MI, LoweredMI, SystemZ::VSTAlign); | |
| break; | |
| case SystemZ::VLM: | |
| Lower.lower(MI, LoweredMI); | |
| lowerAlignmentHint(MI, LoweredMI, SystemZ::VLMAlign); | |
| break; | |
| case SystemZ::VSTM: | |
| Lower.lower(MI, LoweredMI); | |
| lowerAlignmentHint(MI, LoweredMI, SystemZ::VSTMAlign); | |
| break; | |
| case SystemZ::VL16: | |
| LoweredMI = lowerSubvectorLoad(MI, SystemZ::VLREPH); | |
| break; | |
| case SystemZ::VL32: | |
| LoweredMI = lowerSubvectorLoad(MI, SystemZ::VLREPF); | |
| break; | |
| case SystemZ::VL64: | |
| LoweredMI = lowerSubvectorLoad(MI, SystemZ::VLREPG); | |
| break; | |
| case SystemZ::VST16: | |
| LoweredMI = lowerSubvectorStore(MI, SystemZ::VSTEH); | |
| break; | |
| case SystemZ::VST32: | |
| LoweredMI = lowerSubvectorStore(MI, SystemZ::VSTEF); | |
| break; | |
| case SystemZ::VST64: | |
| LoweredMI = lowerSubvectorStore(MI, SystemZ::VSTEG); | |
| break; | |
| case SystemZ::LFER: | |
| LoweredMI = lowerVecEltExtraction(MI, SystemZ::VLGVF); | |
| break; | |
| case SystemZ::LFER_16: | |
| LoweredMI = lowerVecEltExtraction(MI, SystemZ::VLGVH); | |
| break; | |
| case SystemZ::LEFR: | |
| LoweredMI = lowerVecEltInsertion(MI, SystemZ::VLVGF); | |
| break; | |
| case SystemZ::LEFR_16: | |
| LoweredMI = lowerVecEltInsertion(MI, SystemZ::VLVGH); | |
| break; | |
| #define LOWER_LOW(NAME) \ | |
| case SystemZ::NAME##64: LoweredMI = lowerRILow(MI, SystemZ::NAME); break | |
| LOWER_LOW(IILL); | |
| LOWER_LOW(IILH); | |
| LOWER_LOW(TMLL); | |
| LOWER_LOW(TMLH); | |
| LOWER_LOW(NILL); | |
| LOWER_LOW(NILH); | |
| LOWER_LOW(NILF); | |
| LOWER_LOW(OILL); | |
| LOWER_LOW(OILH); | |
| LOWER_LOW(OILF); | |
| LOWER_LOW(XILF); | |
| #undef LOWER_LOW | |
| #define LOWER_HIGH(NAME) \ | |
| case SystemZ::NAME##64: LoweredMI = lowerRIHigh(MI, SystemZ::NAME); break | |
| LOWER_HIGH(IIHL); | |
| LOWER_HIGH(IIHH); | |
| LOWER_HIGH(TMHL); | |
| LOWER_HIGH(TMHH); | |
| LOWER_HIGH(NIHL); | |
| LOWER_HIGH(NIHH); | |
| LOWER_HIGH(NIHF); | |
| LOWER_HIGH(OIHL); | |
| LOWER_HIGH(OIHH); | |
| LOWER_HIGH(OIHF); | |
| LOWER_HIGH(XIHF); | |
| #undef LOWER_HIGH | |
| case SystemZ::Serialize: | |
| if (MF->getSubtarget<SystemZSubtarget>().hasFastSerialization()) | |
| LoweredMI = MCInstBuilder(SystemZ::BCRAsm) | |
| .addImm(14).addReg(SystemZ::R0D); | |
| else | |
| LoweredMI = MCInstBuilder(SystemZ::BCRAsm) | |
| .addImm(15).addReg(SystemZ::R0D); | |
| break; | |
| // We want to emit "j .+2" for traps, jumping to the relative immediate field | |
| // of the jump instruction, which is an illegal instruction. We cannot emit a | |
| // "." symbol, so create and emit a temp label before the instruction and use | |
| // that instead. | |
| case SystemZ::Trap: { | |
| MCSymbol *DotSym = OutContext.createTempSymbol(); | |
| OutStreamer->emitLabel(DotSym); | |
| const MCSymbolRefExpr *Expr = MCSymbolRefExpr::create(DotSym, OutContext); | |
| const MCConstantExpr *ConstExpr = MCConstantExpr::create(2, OutContext); | |
| LoweredMI = MCInstBuilder(SystemZ::J) | |
| .addExpr(MCBinaryExpr::createAdd(Expr, ConstExpr, OutContext)); | |
| } | |
| break; | |
| // Conditional traps will create a branch on condition instruction that jumps | |
| // to the relative immediate field of the jump instruction. (eg. "jo .+2") | |
| case SystemZ::CondTrap: { | |
| MCSymbol *DotSym = OutContext.createTempSymbol(); | |
| OutStreamer->emitLabel(DotSym); | |
| const MCSymbolRefExpr *Expr = MCSymbolRefExpr::create(DotSym, OutContext); | |
| const MCConstantExpr *ConstExpr = MCConstantExpr::create(2, OutContext); | |
| LoweredMI = MCInstBuilder(SystemZ::BRC) | |
| .addImm(MI->getOperand(0).getImm()) | |
| .addImm(MI->getOperand(1).getImm()) | |
| .addExpr(MCBinaryExpr::createAdd(Expr, ConstExpr, OutContext)); | |
| } | |
| break; | |
| case TargetOpcode::FENTRY_CALL: | |
| LowerFENTRY_CALL(*MI, Lower); | |
| return; | |
| case TargetOpcode::STACKMAP: | |
| LowerSTACKMAP(*MI); | |
| return; | |
| case TargetOpcode::PATCHPOINT: | |
| LowerPATCHPOINT(*MI, Lower); | |
| return; | |
| case TargetOpcode::PATCHABLE_FUNCTION_ENTER: | |
| LowerPATCHABLE_FUNCTION_ENTER(*MI, Lower); | |
| return; | |
| case TargetOpcode::PATCHABLE_RET: | |
| LowerPATCHABLE_RET(*MI, Lower); | |
| return; | |
| case TargetOpcode::PATCHABLE_FUNCTION_EXIT: | |
| llvm_unreachable("PATCHABLE_FUNCTION_EXIT should never be emitted"); | |
| case TargetOpcode::PATCHABLE_TAIL_CALL: | |
| // TODO: Define a trampoline `__xray_FunctionTailExit` and differentiate a | |
| // normal function exit from a tail exit. | |
| llvm_unreachable("Tail call is handled in the normal case. See comments " | |
| "around this assert."); | |
| case SystemZ::EXRL_Pseudo: { | |
| unsigned TargetInsOpc = MI->getOperand(0).getImm(); | |
| Register LenMinus1Reg = MI->getOperand(1).getReg(); | |
| Register DestReg = MI->getOperand(2).getReg(); | |
| int64_t DestDisp = MI->getOperand(3).getImm(); | |
| Register SrcReg = MI->getOperand(4).getReg(); | |
| int64_t SrcDisp = MI->getOperand(5).getImm(); | |
| SystemZTargetStreamer *TS = getTargetStreamer(); | |
| MCInst ET = MCInstBuilder(TargetInsOpc) | |
| .addReg(DestReg) | |
| .addImm(DestDisp) | |
| .addImm(1) | |
| .addReg(SrcReg) | |
| .addImm(SrcDisp); | |
| SystemZTargetStreamer::MCInstSTIPair ET_STI(ET, &MF->getSubtarget()); | |
| auto [It, Inserted] = TS->EXRLTargets2Sym.try_emplace(ET_STI); | |
| if (Inserted) | |
| It->second = OutContext.createTempSymbol(); | |
| MCSymbol *DotSym = It->second; | |
| const MCSymbolRefExpr *Dot = MCSymbolRefExpr::create(DotSym, OutContext); | |
| EmitToStreamer( | |
| *OutStreamer, | |
| MCInstBuilder(SystemZ::EXRL).addReg(LenMinus1Reg).addExpr(Dot)); | |
| return; | |
| } | |
| // EH_SjLj_Setup is a dummy terminator instruction of size 0. | |
| // It is used to handle the clobber register for builtin setjmp. | |
| case SystemZ::EH_SjLj_Setup: | |
| return; | |
| default: | |
| Lower.lower(MI, LoweredMI); | |
| break; | |
| } | |
| EmitToStreamer(*OutStreamer, LoweredMI); | |
| } | |
| // Emit the largest nop instruction smaller than or equal to NumBytes | |
| // bytes. Return the size of nop emitted. | |
| static unsigned EmitNop(MCContext &OutContext, MCStreamer &OutStreamer, | |
| unsigned NumBytes, const MCSubtargetInfo &STI) { | |
| if (NumBytes < 2) { | |
| llvm_unreachable("Zero nops?"); | |
| return 0; | |
| } | |
| else if (NumBytes < 4) { | |
| OutStreamer.emitInstruction( | |
| MCInstBuilder(SystemZ::BCRAsm).addImm(0).addReg(SystemZ::R0D), STI); | |
| return 2; | |
| } | |
| else if (NumBytes < 6) { | |
| OutStreamer.emitInstruction( | |
| MCInstBuilder(SystemZ::BCAsm).addImm(0).addReg(0).addImm(0).addReg(0), | |
| STI); | |
| return 4; | |
| } | |
| else { | |
| MCSymbol *DotSym = OutContext.createTempSymbol(); | |
| const MCSymbolRefExpr *Dot = MCSymbolRefExpr::create(DotSym, OutContext); | |
| OutStreamer.emitLabel(DotSym); | |
| OutStreamer.emitInstruction( | |
| MCInstBuilder(SystemZ::BRCLAsm).addImm(0).addExpr(Dot), STI); | |
| return 6; | |
| } | |
| } | |
| void SystemZAsmPrinter::LowerFENTRY_CALL(const MachineInstr &MI, | |
| SystemZMCInstLower &Lower) { | |
| MCContext &Ctx = MF->getContext(); | |
| if (MF->getFunction().hasFnAttribute("mrecord-mcount")) { | |
| MCSymbol *DotSym = OutContext.createTempSymbol(); | |
| OutStreamer->pushSection(); | |
| OutStreamer->switchSection( | |
| Ctx.getELFSection("__mcount_loc", ELF::SHT_PROGBITS, ELF::SHF_ALLOC)); | |
| OutStreamer->emitSymbolValue(DotSym, 8); | |
| OutStreamer->popSection(); | |
| OutStreamer->emitLabel(DotSym); | |
| } | |
| if (MF->getFunction().hasFnAttribute("mnop-mcount")) { | |
| EmitNop(Ctx, *OutStreamer, 6, getSubtargetInfo()); | |
| return; | |
| } | |
| MCSymbol *fentry = Ctx.getOrCreateSymbol("__fentry__"); | |
| const MCSymbolRefExpr *Op = | |
| MCSymbolRefExpr::create(fentry, SystemZ::S_PLT, Ctx); | |
| OutStreamer->emitInstruction( | |
| MCInstBuilder(SystemZ::BRASL).addReg(SystemZ::R0D).addExpr(Op), | |
| getSubtargetInfo()); | |
| } | |
| void SystemZAsmPrinter::LowerSTACKMAP(const MachineInstr &MI) { | |
| auto *TII = MF->getSubtarget<SystemZSubtarget>().getInstrInfo(); | |
| unsigned NumNOPBytes = MI.getOperand(1).getImm(); | |
| auto &Ctx = OutStreamer->getContext(); | |
| MCSymbol *MILabel = Ctx.createTempSymbol(); | |
| OutStreamer->emitLabel(MILabel); | |
| SM.recordStackMap(*MILabel, MI); | |
| assert(NumNOPBytes % 2 == 0 && "Invalid number of NOP bytes requested!"); | |
| // Scan ahead to trim the shadow. | |
| unsigned ShadowBytes = 0; | |
| const MachineBasicBlock &MBB = *MI.getParent(); | |
| MachineBasicBlock::const_iterator MII(MI); | |
| ++MII; | |
| while (ShadowBytes < NumNOPBytes) { | |
| if (MII == MBB.end() || | |
| MII->getOpcode() == TargetOpcode::PATCHPOINT || | |
| MII->getOpcode() == TargetOpcode::STACKMAP) | |
| break; | |
| ShadowBytes += TII->getInstSizeInBytes(*MII); | |
| if (MII->isCall()) | |
| break; | |
| ++MII; | |
| } | |
| // Emit nops. | |
| while (ShadowBytes < NumNOPBytes) | |
| ShadowBytes += EmitNop(OutContext, *OutStreamer, NumNOPBytes - ShadowBytes, | |
| getSubtargetInfo()); | |
| } | |
| // Lower a patchpoint of the form: | |
| // [<def>], <id>, <numBytes>, <target>, <numArgs> | |
| void SystemZAsmPrinter::LowerPATCHPOINT(const MachineInstr &MI, | |
| SystemZMCInstLower &Lower) { | |
| auto &Ctx = OutStreamer->getContext(); | |
| MCSymbol *MILabel = Ctx.createTempSymbol(); | |
| OutStreamer->emitLabel(MILabel); | |
| SM.recordPatchPoint(*MILabel, MI); | |
| PatchPointOpers Opers(&MI); | |
| unsigned EncodedBytes = 0; | |
| const MachineOperand &CalleeMO = Opers.getCallTarget(); | |
| if (CalleeMO.isImm()) { | |
| uint64_t CallTarget = CalleeMO.getImm(); | |
| if (CallTarget) { | |
| unsigned ScratchIdx = -1; | |
| unsigned ScratchReg = 0; | |
| do { | |
| ScratchIdx = Opers.getNextScratchIdx(ScratchIdx + 1); | |
| ScratchReg = MI.getOperand(ScratchIdx).getReg(); | |
| } while (ScratchReg == SystemZ::R0D); | |
| // Materialize the call target address | |
| EmitToStreamer(*OutStreamer, MCInstBuilder(SystemZ::LLILF) | |
| .addReg(ScratchReg) | |
| .addImm(CallTarget & 0xFFFFFFFF)); | |
| EncodedBytes += 6; | |
| if (CallTarget >> 32) { | |
| EmitToStreamer(*OutStreamer, MCInstBuilder(SystemZ::IIHF) | |
| .addReg(ScratchReg) | |
| .addImm(CallTarget >> 32)); | |
| EncodedBytes += 6; | |
| } | |
| EmitToStreamer(*OutStreamer, MCInstBuilder(SystemZ::BASR) | |
| .addReg(SystemZ::R14D) | |
| .addReg(ScratchReg)); | |
| EncodedBytes += 2; | |
| } | |
| } else if (CalleeMO.isGlobal()) { | |
| const MCExpr *Expr = Lower.getExpr(CalleeMO, SystemZ::S_PLT); | |
| EmitToStreamer(*OutStreamer, MCInstBuilder(SystemZ::BRASL) | |
| .addReg(SystemZ::R14D) | |
| .addExpr(Expr)); | |
| EncodedBytes += 6; | |
| } | |
| // Emit padding. | |
| unsigned NumBytes = Opers.getNumPatchBytes(); | |
| assert(NumBytes >= EncodedBytes && | |
| "Patchpoint can't request size less than the length of a call."); | |
| assert((NumBytes - EncodedBytes) % 2 == 0 && | |
| "Invalid number of NOP bytes requested!"); | |
| while (EncodedBytes < NumBytes) | |
| EncodedBytes += EmitNop(OutContext, *OutStreamer, NumBytes - EncodedBytes, | |
| getSubtargetInfo()); | |
| } | |
| void SystemZAsmPrinter::LowerPATCHABLE_FUNCTION_ENTER( | |
| const MachineInstr &MI, SystemZMCInstLower &Lower) { | |
| const MachineFunction &MF = *(MI.getParent()->getParent()); | |
| const Function &F = MF.getFunction(); | |
| // If patchable-function-entry is set, emit in-function nops here. | |
| if (F.hasFnAttribute("patchable-function-entry")) { | |
| // get M-N from function attribute (CodeGenFunction subtracts N | |
| // from M to yield the correct patchable-function-entry). | |
| unsigned Num = F.getFnAttributeAsParsedInteger("patchable-function-entry"); | |
| // Emit M-N 2-byte nops. Use getNop() here instead of emitNops() | |
| // to keep it aligned with the common code implementation emitting | |
| // the prefix nops. | |
| for (unsigned I = 0; I < Num; ++I) | |
| EmitToStreamer(*OutStreamer, MF.getSubtarget().getInstrInfo()->getNop()); | |
| return; | |
| } | |
| // Otherwise, emit xray sled. | |
| // .begin: | |
| // j .end # -> stmg %r2, %r15, 16(%r15) | |
| // nop | |
| // llilf %2, FuncID | |
| // brasl %r14, __xray_FunctionEntry@GOT | |
| // .end: | |
| // | |
| // Update compiler-rt/lib/xray/xray_s390x.cpp accordingly when number | |
| // of instructions change. | |
| bool HasVectorFeature = | |
| TM.getMCSubtargetInfo().hasFeature(SystemZ::FeatureVector) && | |
| !TM.getMCSubtargetInfo().hasFeature(SystemZ::FeatureSoftFloat); | |
| MCSymbol *FuncEntry = OutContext.getOrCreateSymbol( | |
| HasVectorFeature ? "__xray_FunctionEntryVec" : "__xray_FunctionEntry"); | |
| MCSymbol *BeginOfSled = OutContext.createTempSymbol("xray_sled_", true); | |
| MCSymbol *EndOfSled = OutContext.createTempSymbol(); | |
| OutStreamer->emitLabel(BeginOfSled); | |
| EmitToStreamer(*OutStreamer, | |
| MCInstBuilder(SystemZ::J) | |
| .addExpr(MCSymbolRefExpr::create(EndOfSled, OutContext))); | |
| EmitNop(OutContext, *OutStreamer, 2, getSubtargetInfo()); | |
| EmitToStreamer(*OutStreamer, | |
| MCInstBuilder(SystemZ::LLILF).addReg(SystemZ::R2D).addImm(0)); | |
| EmitToStreamer(*OutStreamer, MCInstBuilder(SystemZ::BRASL) | |
| .addReg(SystemZ::R14D) | |
| .addExpr(MCSymbolRefExpr::create( | |
| FuncEntry, SystemZ::S_PLT, OutContext))); | |
| OutStreamer->emitLabel(EndOfSled); | |
| recordSled(BeginOfSled, MI, SledKind::FUNCTION_ENTER, 2); | |
| } | |
| void SystemZAsmPrinter::LowerPATCHABLE_RET(const MachineInstr &MI, | |
| SystemZMCInstLower &Lower) { | |
| unsigned OpCode = MI.getOperand(0).getImm(); | |
| MCSymbol *FallthroughLabel = nullptr; | |
| if (OpCode == SystemZ::CondReturn) { | |
| FallthroughLabel = OutContext.createTempSymbol(); | |
| int64_t Cond0 = MI.getOperand(1).getImm(); | |
| int64_t Cond1 = MI.getOperand(2).getImm(); | |
| EmitToStreamer(*OutStreamer, MCInstBuilder(SystemZ::BRC) | |
| .addImm(Cond0) | |
| .addImm(Cond1 ^ Cond0) | |
| .addExpr(MCSymbolRefExpr::create( | |
| FallthroughLabel, OutContext))); | |
| } | |
| // .begin: | |
| // br %r14 # -> stmg %r2, %r15, 24(%r15) | |
| // nop | |
| // nop | |
| // llilf %2,FuncID | |
| // j __xray_FunctionExit@GOT | |
| // | |
| // Update compiler-rt/lib/xray/xray_s390x.cpp accordingly when number | |
| // of instructions change. | |
| bool HasVectorFeature = | |
| TM.getMCSubtargetInfo().hasFeature(SystemZ::FeatureVector) && | |
| !TM.getMCSubtargetInfo().hasFeature(SystemZ::FeatureSoftFloat); | |
| MCSymbol *FuncExit = OutContext.getOrCreateSymbol( | |
| HasVectorFeature ? "__xray_FunctionExitVec" : "__xray_FunctionExit"); | |
| MCSymbol *BeginOfSled = OutContext.createTempSymbol("xray_sled_", true); | |
| OutStreamer->emitLabel(BeginOfSled); | |
| EmitToStreamer(*OutStreamer, | |
| MCInstBuilder(SystemZ::BR).addReg(SystemZ::R14D)); | |
| EmitNop(OutContext, *OutStreamer, 4, getSubtargetInfo()); | |
| EmitToStreamer(*OutStreamer, | |
| MCInstBuilder(SystemZ::LLILF).addReg(SystemZ::R2D).addImm(0)); | |
| EmitToStreamer(*OutStreamer, MCInstBuilder(SystemZ::J) | |
| .addExpr(MCSymbolRefExpr::create( | |
| FuncExit, SystemZ::S_PLT, OutContext))); | |
| if (FallthroughLabel) | |
| OutStreamer->emitLabel(FallthroughLabel); | |
| recordSled(BeginOfSled, MI, SledKind::FUNCTION_EXIT, 2); | |
| } | |
| // The *alignment* of 128-bit vector types is different between the software | |
| // and hardware vector ABIs. If the there is an externally visible use of a | |
| // vector type in the module it should be annotated with an attribute. | |
| void SystemZAsmPrinter::emitAttributes(Module &M) { | |
| if (M.getModuleFlag("s390x-visible-vector-ABI")) { | |
| bool HasVectorFeature = | |
| TM.getMCSubtargetInfo().hasFeature(SystemZ::FeatureVector); | |
| OutStreamer->emitGNUAttribute(8, HasVectorFeature ? 2 : 1); | |
| } | |
| } | |
| // Convert a SystemZ-specific constant pool modifier into the associated | |
| // specifier. | |
| static uint8_t getSpecifierFromModifier(SystemZCP::SystemZCPModifier Modifier) { | |
| switch (Modifier) { | |
| case SystemZCP::TLSGD: | |
| return SystemZ::S_TLSGD; | |
| case SystemZCP::TLSLDM: | |
| return SystemZ::S_TLSLDM; | |
| case SystemZCP::DTPOFF: | |
| return SystemZ::S_DTPOFF; | |
| case SystemZCP::NTPOFF: | |
| return SystemZ::S_NTPOFF; | |
| } | |
| llvm_unreachable("Invalid SystemCPModifier!"); | |
| } | |
| void SystemZAsmPrinter::emitMachineConstantPoolValue( | |
| MachineConstantPoolValue *MCPV) { | |
| auto *ZCPV = static_cast<SystemZConstantPoolValue*>(MCPV); | |
| const MCExpr *Expr = MCSymbolRefExpr::create( | |
| getSymbol(ZCPV->getGlobalValue()), | |
| getSpecifierFromModifier(ZCPV->getModifier()), OutContext); | |
| uint64_t Size = getDataLayout().getTypeAllocSize(ZCPV->getType()); | |
| OutStreamer->emitValue(Expr, Size); | |
| } | |
| // Emit the ctor or dtor list taking into account the init priority. | |
| void SystemZAsmPrinter::emitXXStructorList(const DataLayout &DL, | |
| const Constant *List, bool IsCtor) { | |
| if (!TM.getTargetTriple().isOSBinFormatGOFF()) | |
| return AsmPrinter::emitXXStructorList(DL, List, IsCtor); | |
| SmallVector<Structor, 8> Structors; | |
| preprocessXXStructorList(DL, List, Structors); | |
| if (Structors.empty()) | |
| return; | |
| const Align Align = llvm::Align(4); | |
| const TargetLoweringObjectFileGOFF &Obj = | |
| static_cast<const TargetLoweringObjectFileGOFF &>(getObjFileLowering()); | |
| for (Structor &S : Structors) { | |
| MCSectionGOFF *Section = | |
| static_cast<MCSectionGOFF *>(Obj.getStaticXtorSection(S.Priority)); | |
| OutStreamer->switchSection(Section); | |
| if (OutStreamer->getCurrentSection() != OutStreamer->getPreviousSection()) | |
| emitAlignment(Align); | |
| // The priority is provided as an input to getStaticXtorSection(), and is | |
| // recalculated within that function as `Prio` going to going into the | |
| // PR section. | |
| // This priority retrieved via the `SortKey` below is the recalculated | |
| // Priority. | |
| uint32_t XtorPriority = Section->getPRAttributes().SortKey; | |
| const GlobalValue *GV = dyn_cast<GlobalValue>(S.Func->stripPointerCasts()); | |
| assert(GV && "C++ xxtor pointer was not a GlobalValue!"); | |
| MCSymbolGOFF *Symbol = static_cast<MCSymbolGOFF *>(getSymbol(GV)); | |
| // @@SQINIT entry: { unsigned prio; void (*ctor)(); void (*dtor)(); } | |
| unsigned PointerSizeInBytes = DL.getPointerSize(); | |
| auto &Ctx = OutStreamer->getContext(); | |
| const MCExpr *ADAFuncRefExpr; | |
| unsigned SlotKind = SystemZII::MO_ADA_DIRECT_FUNC_DESC; | |
| MCSectionGOFF *ADASection = | |
| static_cast<MCSectionGOFF *>(Obj.getADASection()); | |
| assert(ADASection && "ADA section must exist for GOFF targets!"); | |
| const MCSymbol *ADASym = ADASection->getBeginSymbol(); | |
| assert(ADASym && "ADA symbol should already be set!"); | |
| ADAFuncRefExpr = MCBinaryExpr::createAdd( | |
| MCSpecifierExpr::create(MCSymbolRefExpr::create(ADASym, OutContext), | |
| SystemZ::S_QCon, OutContext), | |
| MCConstantExpr::create(ADATable.insert(Symbol, SlotKind), Ctx), Ctx); | |
| emitInt32(XtorPriority); | |
| if (IsCtor) { | |
| OutStreamer->emitValue(ADAFuncRefExpr, PointerSizeInBytes); | |
| OutStreamer->emitIntValue(0, PointerSizeInBytes); | |
| } else { | |
| OutStreamer->emitIntValue(0, PointerSizeInBytes); | |
| OutStreamer->emitValue(ADAFuncRefExpr, PointerSizeInBytes); | |
| } | |
| } | |
| } | |
| static void printFormattedRegName(const MCAsmInfo *MAI, unsigned RegNo, | |
| raw_ostream &OS) { | |
| const char *RegName; | |
| if (MAI->getAssemblerDialect() == AD_HLASM) { | |
| RegName = SystemZHLASMInstPrinter::getRegisterName(RegNo); | |
| // Skip register prefix so that only register number is left | |
| assert(isalpha(RegName[0]) && isdigit(RegName[1])); | |
| OS << (RegName + 1); | |
| } else { | |
| RegName = SystemZGNUInstPrinter::getRegisterName(RegNo); | |
| OS << '%' << RegName; | |
| } | |
| } | |
| static void printReg(unsigned Reg, const MCAsmInfo *MAI, raw_ostream &OS) { | |
| if (!Reg) | |
| OS << '0'; | |
| else | |
| printFormattedRegName(MAI, Reg, OS); | |
| } | |
| static void printOperand(const MCOperand &MCOp, const MCAsmInfo *MAI, | |
| raw_ostream &OS) { | |
| if (MCOp.isReg()) | |
| printReg(MCOp.getReg(), MAI, OS); | |
| else if (MCOp.isImm()) | |
| OS << MCOp.getImm(); | |
| else if (MCOp.isExpr()) | |
| MAI->printExpr(OS, *MCOp.getExpr()); | |
| else | |
| llvm_unreachable("Invalid operand"); | |
| } | |
| static void printAddress(const MCAsmInfo *MAI, unsigned Base, | |
| const MCOperand &DispMO, unsigned Index, | |
| raw_ostream &OS) { | |
| printOperand(DispMO, MAI, OS); | |
| if (Base || Index) { | |
| OS << '('; | |
| if (Index) { | |
| printFormattedRegName(MAI, Index, OS); | |
| if (Base) | |
| OS << ','; | |
| } | |
| if (Base) | |
| printFormattedRegName(MAI, Base, OS); | |
| OS << ')'; | |
| } | |
| } | |
| bool SystemZAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, | |
| const char *ExtraCode, | |
| raw_ostream &OS) { | |
| const MCRegisterInfo &MRI = TM.getMCRegisterInfo(); | |
| const MachineOperand &MO = MI->getOperand(OpNo); | |
| MCOperand MCOp; | |
| if (ExtraCode) { | |
| if (ExtraCode[0] == 'N' && !ExtraCode[1] && MO.isReg() && | |
| SystemZ::GR128BitRegClass.contains(MO.getReg())) | |
| MCOp = | |
| MCOperand::createReg(MRI.getSubReg(MO.getReg(), SystemZ::subreg_l64)); | |
| else | |
| return AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS); | |
| } else { | |
| SystemZMCInstLower Lower(MF->getContext(), *this); | |
| MCOp = Lower.lowerOperand(MO); | |
| } | |
| printOperand(MCOp, &MAI, OS); | |
| return false; | |
| } | |
| bool SystemZAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, | |
| unsigned OpNo, | |
| const char *ExtraCode, | |
| raw_ostream &OS) { | |
| if (ExtraCode && ExtraCode[0] && !ExtraCode[1]) { | |
| switch (ExtraCode[0]) { | |
| case 'A': | |
| // Unlike EmitMachineNode(), EmitSpecialNode(INLINEASM) does not call | |
| // setMemRefs(), so MI->memoperands() is empty and the alignment | |
| // information is not available. | |
| return false; | |
| case 'O': | |
| OS << MI->getOperand(OpNo + 1).getImm(); | |
| return false; | |
| case 'R': | |
| ::printReg(MI->getOperand(OpNo).getReg(), &MAI, OS); | |
| return false; | |
| } | |
| } | |
| printAddress(&MAI, MI->getOperand(OpNo).getReg(), | |
| MCOperand::createImm(MI->getOperand(OpNo + 1).getImm()), | |
| MI->getOperand(OpNo + 2).getReg(), OS); | |
| return false; | |
| } | |
| void SystemZAsmPrinter::emitEndOfAsmFile(Module &M) { | |
| auto TT = OutContext.getTargetTriple(); | |
| if (TT.isOSzOS()) { | |
| OutStreamer->switchSection(getObjFileLowering().getTextSection()); | |
| for (auto &Info : DeferredPPA1) | |
| emitPPA1(Info); | |
| emitADASection(); | |
| emitIDRLSection(M); | |
| // On z/OS, we need to associate an external data reference with an ED | |
| // symbol, for which we use the the ED of the ADA. We also need to mark the | |
| // reference as being to data, otherwise we cannot bind with code generated | |
| // by XL. | |
| for (auto &GO : M.global_objects()) { | |
| if (auto *GV = dyn_cast<GlobalVariable>(&GO)) { | |
| if (!GV->hasInitializer()) { | |
| MCSymbol *Sym = getSymbol(GV); | |
| getTargetStreamer()->emitADA( | |
| Sym, OutContext.getObjectFileInfo()->getADASection()); | |
| OutStreamer->emitSymbolAttribute(Sym, MCSA_ELF_TypeObject); | |
| } | |
| } | |
| } | |
| } | |
| emitAttributes(M); | |
| } | |
| void SystemZAsmPrinter::emitADASection() { | |
| OutStreamer->pushSection(); | |
| const unsigned PointerSize = getDataLayout().getPointerSize(); | |
| OutStreamer->switchSection(getObjFileLowering().getADASection()); | |
| unsigned EmittedBytes = 0; | |
| for (auto &Entry : ADATable.getTable()) { | |
| const MCSymbol *Sym; | |
| unsigned SlotKind; | |
| std::tie(Sym, SlotKind) = Entry.first; | |
| unsigned Offset = Entry.second; | |
| assert(Offset == EmittedBytes && "Offset not as expected"); | |
| (void)EmittedBytes; | |
| #define EMIT_COMMENT(Str) \ | |
| OutStreamer->AddComment(Twine("Offset ") \ | |
| .concat(utostr(Offset)) \ | |
| .concat(" " Str " ") \ | |
| .concat(Sym->getName())); | |
| switch (SlotKind) { | |
| case SystemZII::MO_ADA_DIRECT_FUNC_DESC: | |
| // Language Environment DLL logic requires function descriptors, for | |
| // imported functions, that are placed in the ADA to be 8 byte aligned. | |
| EMIT_COMMENT("function descriptor of"); | |
| OutStreamer->emitValue( | |
| MCSpecifierExpr::create(MCSymbolRefExpr::create(Sym, OutContext), | |
| SystemZ::S_RCon, OutContext), | |
| PointerSize); | |
| OutStreamer->emitValue( | |
| MCSpecifierExpr::create(MCSymbolRefExpr::create(Sym, OutContext), | |
| SystemZ::S_VCon, OutContext), | |
| PointerSize); | |
| EmittedBytes += PointerSize * 2; | |
| break; | |
| case SystemZII::MO_ADA_DATA_SYMBOL_ADDR: | |
| EMIT_COMMENT("pointer to data symbol"); | |
| OutStreamer->emitValue( | |
| MCSpecifierExpr::create(MCSymbolRefExpr::create(Sym, OutContext), | |
| SystemZ::S_None, OutContext), | |
| PointerSize); | |
| EmittedBytes += PointerSize; | |
| break; | |
| case SystemZII::MO_ADA_INDIRECT_FUNC_DESC: { | |
| MCSymbol *Alias = OutContext.getOrCreateSymbol( | |
| Twine(Sym->getName()).concat("@indirect")); | |
| OutStreamer->emitSymbolAttribute(Alias, MCSA_IndirectSymbol); | |
| OutStreamer->emitSymbolAttribute(Alias, MCSA_ELF_TypeFunction); | |
| OutStreamer->emitSymbolAttribute(Alias, MCSA_Global); | |
| OutStreamer->emitSymbolAttribute(Alias, MCSA_Extern); | |
| MCSymbolGOFF *GOFFSym = | |
| static_cast<llvm::MCSymbolGOFF *>(const_cast<llvm::MCSymbol *>(Sym)); | |
| getTargetStreamer()->emitExternalName(Alias, GOFFSym->getExternalName()); | |
| EMIT_COMMENT("pointer to function descriptor"); | |
| OutStreamer->emitValue( | |
| MCSpecifierExpr::create(MCSymbolRefExpr::create(Alias, OutContext), | |
| SystemZ::S_VCon, OutContext), | |
| PointerSize); | |
| EmittedBytes += PointerSize; | |
| break; | |
| } | |
| default: | |
| llvm_unreachable("Unexpected slot kind"); | |
| } | |
| #undef EMIT_COMMENT | |
| } | |
| 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()); | |
| OutStreamer->popSection(); | |
| } | |
| void SystemZAsmPrinter::emitFunctionBodyEnd() { | |
| if (TM.getTargetTriple().isOSzOS()) { | |
| // Emit symbol for the end of function if the z/OS target streamer | |
| // is used. This is needed to calculate the size of the function. | |
| OutStreamer->emitLabel(DeferredPPA1.back().FnEnd); | |
| } | |
| } | |
| static void emitPPA1Flags(std::unique_ptr<MCStreamer> &OutStreamer, bool VarArg, | |
| bool StackProtector, bool FPRMask, bool VRMask, | |
| bool EHBlock, bool HasArgAreaLength, bool HasName) { | |
| enum class PPA1Flag1 : uint8_t { | |
| DSA64Bit = (0x80 >> 0), | |
| VarArg = (0x80 >> 7), | |
| LLVM_MARK_AS_BITMASK_ENUM(DSA64Bit) | |
| }; | |
| enum class PPA1Flag2 : uint8_t { | |
| ExternalProcedure = (0x80 >> 0), | |
| STACKPROTECTOR = (0x80 >> 3), | |
| LLVM_MARK_AS_BITMASK_ENUM(ExternalProcedure) | |
| }; | |
| enum class PPA1Flag3 : uint8_t { | |
| HasArgAreaLength = (0x80 >> 1), | |
| FPRMask = (0x80 >> 2), | |
| LLVM_MARK_AS_BITMASK_ENUM(HasArgAreaLength) | |
| }; | |
| enum class PPA1Flag4 : uint8_t { | |
| EPMOffsetPresent = (0x80 >> 0), | |
| VRMask = (0x80 >> 2), | |
| EHBlock = (0x80 >> 3), | |
| ProcedureNamePresent = (0x80 >> 7), | |
| LLVM_MARK_AS_BITMASK_ENUM(EPMOffsetPresent) | |
| }; | |
| // Declare optional section flags that can be modified. | |
| auto Flags1 = PPA1Flag1(0); | |
| auto Flags2 = PPA1Flag2::ExternalProcedure; | |
| auto Flags3 = PPA1Flag3(0); | |
| auto Flags4 = PPA1Flag4::EPMOffsetPresent; | |
| Flags1 |= PPA1Flag1::DSA64Bit; | |
| if (VarArg) | |
| Flags1 |= PPA1Flag1::VarArg; | |
| if (StackProtector) | |
| Flags2 |= PPA1Flag2::STACKPROTECTOR; | |
| if (HasArgAreaLength) | |
| Flags3 |= PPA1Flag3::HasArgAreaLength; // Add emit ArgAreaLength flag. | |
| // SavedGPRMask, SavedFPRMask, and SavedVRMask are precomputed in. | |
| if (FPRMask) | |
| Flags3 |= PPA1Flag3::FPRMask; // Add emit FPR mask flag. | |
| if (VRMask) | |
| Flags4 |= PPA1Flag4::VRMask; // Add emit VR mask flag. | |
| if (EHBlock) | |
| Flags4 |= PPA1Flag4::EHBlock; // Add optional EH block. | |
| if (HasName) | |
| Flags4 |= PPA1Flag4::ProcedureNamePresent; // Add optional name block. | |
| OutStreamer->AddComment("PPA1 Flags 1"); | |
| OutStreamer->AddComment(" Bit 0: 1 = 64-bit DSA"); | |
| if ((Flags1 & PPA1Flag1::VarArg) == PPA1Flag1::VarArg) | |
| OutStreamer->AddComment(" Bit 7: 1 = Vararg function"); | |
| OutStreamer->emitInt8(static_cast<uint8_t>(Flags1)); // Flags 1. | |
| OutStreamer->AddComment("PPA1 Flags 2"); | |
| if ((Flags2 & PPA1Flag2::ExternalProcedure) == PPA1Flag2::ExternalProcedure) | |
| OutStreamer->AddComment(" Bit 0: 1 = External procedure"); | |
| if ((Flags2 & PPA1Flag2::STACKPROTECTOR) == PPA1Flag2::STACKPROTECTOR) | |
| OutStreamer->AddComment(" Bit 3: 1 = STACKPROTECT is enabled"); | |
| else | |
| OutStreamer->AddComment(" Bit 3: 0 = STACKPROTECT is not enabled"); | |
| OutStreamer->emitInt8(static_cast<uint8_t>(Flags2)); // Flags 2. | |
| OutStreamer->AddComment("PPA1 Flags 3"); | |
| if ((Flags3 & PPA1Flag3::HasArgAreaLength) == PPA1Flag3::HasArgAreaLength) | |
| OutStreamer->AddComment( | |
| " Bit 1: 1 = Argument Area Length is in optional area"); | |
| if ((Flags3 & PPA1Flag3::FPRMask) == PPA1Flag3::FPRMask) | |
| OutStreamer->AddComment(" Bit 2: 1 = FP Reg Mask is in optional area"); | |
| OutStreamer->emitInt8( | |
| static_cast<uint8_t>(Flags3)); // Flags 3 (optional sections). | |
| OutStreamer->AddComment("PPA1 Flags 4"); | |
| if ((Flags4 & PPA1Flag4::VRMask) == PPA1Flag4::VRMask) | |
| OutStreamer->AddComment(" Bit 2: 1 = Vector Reg Mask is in optional area"); | |
| if ((Flags4 & PPA1Flag4::EHBlock) == PPA1Flag4::EHBlock) | |
| OutStreamer->AddComment(" Bit 3: 1 = C++ EH block"); | |
| if ((Flags4 & PPA1Flag4::ProcedureNamePresent) == | |
| PPA1Flag4::ProcedureNamePresent) | |
| OutStreamer->AddComment(" Bit 7: 1 = Name Length and Name"); | |
| OutStreamer->emitInt8(static_cast<uint8_t>( | |
| Flags4)); // Flags 4 (optional sections, always emit these). | |
| } | |
| static void emitPPA1Name(std::unique_ptr<MCStreamer> &OutStreamer, | |
| StringRef OutName) { | |
| size_t NameSize = OutName.size(); | |
| uint16_t OutSize; | |
| if (NameSize < UINT16_MAX) { | |
| OutSize = static_cast<uint16_t>(NameSize); | |
| } else { | |
| OutName = OutName.substr(0, UINT16_MAX); | |
| OutSize = UINT16_MAX; | |
| } | |
| // Emit padding to ensure that the next optional field word-aligned. | |
| uint8_t ExtraZeros = 4 - ((2 + OutSize) % 4); | |
| SmallString<512> OutnameConv; | |
| ConverterEBCDIC::convertToEBCDIC(OutName, OutnameConv); | |
| OutName = OutnameConv.str(); | |
| OutStreamer->AddComment("Length of Name"); | |
| OutStreamer->emitInt16(OutSize); | |
| OutStreamer->AddComment("Name of Function"); | |
| OutStreamer->emitBytes(OutName); | |
| OutStreamer->emitZeros(ExtraZeros); | |
| } | |
| void SystemZAsmPrinter::emitPPA1(PPA1Info &Info) { | |
| assert(PPA2Sym != nullptr && "PPA2 Symbol not defined"); | |
| // Optional Argument Area Length. | |
| // Note: This represents the length of the argument area that we reserve | |
| // in our stack for setting up arguments for calls to other | |
| // routines. If this optional field is not set, LE will reserve | |
| // 128 bytes for the argument area. This optional field is | |
| // created if greater than 128 bytes is required - to guarantee | |
| // the required space is reserved on stack extension in the new | |
| // extension. This optional field is also created if the | |
| // routine has alloca(). This may reduce stack space | |
| // if alloca() call causes a stack extension. | |
| bool HasArgAreaLength = (Info.AllocaReg != 0) || (Info.CallFrameSize > 128); | |
| // Emit PPA1 section. | |
| OutStreamer->AddComment("PPA1"); | |
| OutStreamer->emitLabel(Info.PPA1); | |
| OutStreamer->AddComment("Version"); | |
| OutStreamer->emitInt8(0x02); // Version. | |
| OutStreamer->AddComment("LE Signature X'CE'"); | |
| OutStreamer->emitInt8(0xCE); // CEL signature. | |
| OutStreamer->AddComment("Saved GPR Mask"); | |
| OutStreamer->emitInt16(Info.SavedGPRMask); | |
| OutStreamer->AddComment("Offset to PPA2"); | |
| OutStreamer->emitAbsoluteSymbolDiff(PPA2Sym, Info.PPA1, 4); | |
| emitPPA1Flags(OutStreamer, Info.IsVarArg, Info.HasStackProtector, | |
| Info.SavedFPRMask != 0, Info.SavedVRMask != 0, | |
| Info.PersonalityRoutine != nullptr, HasArgAreaLength, | |
| Info.Name.size() > 0); | |
| OutStreamer->AddComment("Length/4 of Parms"); | |
| OutStreamer->emitInt16( | |
| static_cast<uint16_t>(Info.SizeOfFnParams / 4)); // Parms/4. | |
| OutStreamer->AddComment("Length of Code"); | |
| OutStreamer->emitAbsoluteSymbolDiff(Info.FnEnd, Info.EPMarker, 4); | |
| if (HasArgAreaLength) { | |
| OutStreamer->AddComment("Argument Area Length"); | |
| OutStreamer->emitInt32(Info.CallFrameSize); | |
| } | |
| // Emit saved FPR mask and offset to FPR save area (0x20 of flags 3). | |
| if (Info.SavedFPRMask) { | |
| OutStreamer->AddComment("FPR mask"); | |
| OutStreamer->emitInt16(Info.SavedFPRMask); | |
| OutStreamer->AddComment("AR mask"); | |
| OutStreamer->emitInt16(0); // AR Mask, unused currently. | |
| OutStreamer->AddComment("FPR Save Area Locator"); | |
| uint64_t FPRSaveAreaOffset = Info.OffsetFPR; | |
| assert(FPRSaveAreaOffset < 0x10000000 && "Offset out of range"); | |
| FPRSaveAreaOffset &= 0x0FFFFFFF; // Lose top 4 bits. | |
| OutStreamer->AddComment( | |
| Twine(" Bit 0-3: Register R").concat(utostr(Info.FrameReg)).str()); | |
| OutStreamer->AddComment( | |
| Twine(" Bit 4-31: Offset ").concat(utostr(FPRSaveAreaOffset)).str()); | |
| OutStreamer->emitInt32(FPRSaveAreaOffset | | |
| (Info.FrameReg << 28)); // Offset to FPR save area | |
| // with register to add | |
| // value to (alloca reg). | |
| } | |
| // Emit saved VR mask to VR save area. | |
| if (Info.SavedVRMask) { | |
| OutStreamer->AddComment("VR mask"); | |
| OutStreamer->emitInt8(Info.SavedVRMask); | |
| OutStreamer->emitInt8(0); // Reserved. | |
| OutStreamer->emitInt16(0); // Also reserved. | |
| uint64_t VRSaveAreaOffset = Info.OffsetVR; | |
| assert(VRSaveAreaOffset < 0x10000000 && "Offset out of range"); | |
| VRSaveAreaOffset &= 0x0FFFFFFF; // Lose top 4 bits. | |
| OutStreamer->AddComment("VR Save Area Locator"); | |
| OutStreamer->AddComment( | |
| Twine(" Bit 0-3: Register R").concat(utostr(Info.FrameReg)).str()); | |
| OutStreamer->AddComment( | |
| Twine(" Bit 4-31: Offset ").concat(utostr(VRSaveAreaOffset)).str()); | |
| OutStreamer->emitInt32(VRSaveAreaOffset | (Info.FrameReg << 28)); | |
| } | |
| // Emit C++ EH information block. | |
| if (Info.PersonalityRoutine) { | |
| OutStreamer->AddComment("Version"); | |
| OutStreamer->emitInt32(1); | |
| OutStreamer->AddComment("Flags"); | |
| OutStreamer->emitInt32(0); // LSDA field is a WAS offset | |
| OutStreamer->AddComment("Personality routine"); | |
| OutStreamer->emitInt64(ADATable.insert( | |
| Info.PersonalityRoutine, SystemZII::MO_ADA_INDIRECT_FUNC_DESC)); | |
| OutStreamer->AddComment("LSDA location"); | |
| OutStreamer->emitInt64( | |
| ADATable.insert(Info.GCCEH, SystemZII::MO_ADA_DATA_SYMBOL_ADDR)); | |
| } | |
| // Emit name length and name optional section (0x01 of flags 4) | |
| if (Info.Name.size()) | |
| emitPPA1Name(OutStreamer, Info.Name); | |
| // Emit offset to entry point optional section (0x80 of flags 4). | |
| OutStreamer->emitAbsoluteSymbolDiff(Info.EPMarker, Info.PPA1, 4); | |
| } | |
| void SystemZAsmPrinter::calculatePPA1() { | |
| assert(PPA2Sym != nullptr && "PPA2 Symbol not defined"); | |
| PPA1Info Info; | |
| const TargetRegisterInfo *TRI = MF->getRegInfo().getTargetRegisterInfo(); | |
| const SystemZSubtarget &Subtarget = MF->getSubtarget<SystemZSubtarget>(); | |
| const SystemZMachineFunctionInfo *ZFI = | |
| MF->getInfo<SystemZMachineFunctionInfo>(); | |
| const auto *ZFL = static_cast<const SystemZXPLINKFrameLowering *>( | |
| Subtarget.getFrameLowering()); | |
| const MachineFrameInfo &MFFrame = MF->getFrameInfo(); | |
| // Get saved GPR/FPR/VPR masks. | |
| const std::vector<CalleeSavedInfo> &CSI = MFFrame.getCalleeSavedInfo(); | |
| uint16_t SavedGPRMask = 0; | |
| uint16_t SavedFPRMask = 0; | |
| uint8_t SavedVRMask = 0; | |
| int64_t OffsetFPR = 0; | |
| int64_t OffsetVR = 0; | |
| const int64_t TopOfStack = | |
| MFFrame.getOffsetAdjustment() + MFFrame.getStackSize(); | |
| // Loop over the spilled registers. The CalleeSavedInfo can't be used because | |
| // it does not contain all spilled registers. | |
| for (unsigned I = ZFI->getSpillGPRRegs().LowGPR, | |
| E = ZFI->getSpillGPRRegs().HighGPR; | |
| I && E && I <= E; ++I) { | |
| unsigned V = TRI->getEncodingValue((Register)I); | |
| assert(V < 16 && "GPR index out of range"); | |
| SavedGPRMask |= 1 << (15 - V); | |
| } | |
| for (auto &CS : CSI) { | |
| unsigned Reg = CS.getReg(); | |
| unsigned I = TRI->getEncodingValue(Reg); | |
| if (SystemZ::FP64BitRegClass.contains(Reg)) { | |
| assert(I < 16 && "FPR index out of range"); | |
| SavedFPRMask |= 1 << (15 - I); | |
| int64_t Temp = MFFrame.getObjectOffset(CS.getFrameIdx()); | |
| if (Temp < OffsetFPR) | |
| OffsetFPR = Temp; | |
| } else if (SystemZ::VR128BitRegClass.contains(Reg)) { | |
| assert(I >= 16 && I <= 23 && "VPR index out of range"); | |
| unsigned BitNum = I - 16; | |
| SavedVRMask |= 1 << (7 - BitNum); | |
| int64_t Temp = MFFrame.getObjectOffset(CS.getFrameIdx()); | |
| if (Temp < OffsetVR) | |
| OffsetVR = Temp; | |
| } | |
| } | |
| // Adjust the offset. | |
| OffsetFPR += (OffsetFPR < 0) ? TopOfStack : 0; | |
| OffsetVR += (OffsetVR < 0) ? TopOfStack : 0; | |
| // Get alloca register. | |
| uint8_t FrameReg = TRI->getEncodingValue(TRI->getFrameRegister(*MF)); | |
| uint8_t AllocaReg = ZFL->hasFP(*MF) ? FrameReg : 0; | |
| assert(AllocaReg < 16 && "Can't have alloca register larger than 15"); | |
| MCSymbol *PersonalityRoutine = nullptr; | |
| MCSymbol *GCCEH = nullptr; | |
| if (!MF->getLandingPads().empty()) { | |
| const Function *Per = dyn_cast<Function>( | |
| MF->getFunction().getPersonalityFn()->stripPointerCasts()); | |
| PersonalityRoutine = Per ? MF->getTarget().getSymbol(Per) : nullptr; | |
| assert(PersonalityRoutine && "Missing personality routine"); | |
| GCCEH = MF->getContext().getOrCreateSymbol(Twine("GCC_except_table") + | |
| Twine(MF->getFunctionNumber())); | |
| } | |
| // Get the name of the function, with suffix _. | |
| std::string N(MF->getFunction().hasName() | |
| ? Twine(MF->getFunction().getName()).concat("_").str() | |
| : ""); | |
| // Save the calculated values. | |
| if (MF->getFunction().hasName()) | |
| Info.Name = MF->getFunction().getName(); | |
| Info.PPA1 = OutContext.createTempSymbol(Twine("PPA1_").concat(N).str(), true); | |
| Info.EPMarker = | |
| OutContext.createTempSymbol(Twine("EPM_").concat(N).str(), true); | |
| Info.FnEnd = OutContext.createTempSymbol(Twine(N).concat("end_").str()); | |
| Info.PersonalityRoutine = PersonalityRoutine; | |
| Info.GCCEH = GCCEH; | |
| Info.OffsetFPR = OffsetFPR; | |
| Info.OffsetVR = OffsetVR; | |
| Info.CallFrameSize = MFFrame.getMaxCallFrameSize(); | |
| Info.SizeOfFnParams = ZFI->getSizeOfFnParams(); | |
| Info.SavedGPRMask = SavedGPRMask; | |
| Info.SavedFPRMask = SavedFPRMask; | |
| Info.SavedVRMask = SavedVRMask; | |
| Info.FrameReg = FrameReg; | |
| Info.AllocaReg = AllocaReg; | |
| Info.IsVarArg = MF->getFunction().isVarArg(); | |
| Info.HasStackProtector = MFFrame.hasStackProtectorIndex(); | |
| DeferredPPA1.push_back(Info); | |
| } | |
| void SystemZAsmPrinter::emitStartOfAsmFile(Module &M) { | |
| if (TM.getTargetTriple().isOSzOS()) | |
| emitPPA2(M); | |
| AsmPrinter::emitStartOfAsmFile(M); | |
| } | |
| void SystemZAsmPrinter::emitPPA2(Module &M) { | |
| OutStreamer->pushSection(); | |
| OutStreamer->switchSection(getObjFileLowering().getTextSection()); | |
| MCContext &OutContext = OutStreamer->getContext(); | |
| // Make CELQSTRT symbol. | |
| const char *StartSymbolName = "CELQSTRT"; | |
| MCSymbol *CELQSTRT = OutContext.getOrCreateSymbol(StartSymbolName); | |
| OutStreamer->emitSymbolAttribute(CELQSTRT, MCSA_OSLinkage); | |
| OutStreamer->emitSymbolAttribute(CELQSTRT, MCSA_Global); | |
| // 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<14> CompilationTimeEBCDIC, CompilationTime; | |
| CompilationTime = formatv("{0:%Y%m%d%H%M%S}", llvm::sys::toUtcTime(Time)); | |
| uint32_t ProductVersion = getProductVersion(M), | |
| ProductRelease = getProductRelease(M), | |
| ProductPatch = getProductPatch(M); | |
| SmallString<6> VersionEBCDIC, Version; | |
| Version = formatv("{0,0-2:d}{1,0-2:d}{2,0-2:d}", ProductVersion, | |
| ProductRelease, ProductPatch); | |
| ConverterEBCDIC::convertToEBCDIC(CompilationTime, CompilationTimeEBCDIC); | |
| ConverterEBCDIC::convertToEBCDIC(Version, VersionEBCDIC); | |
| 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); | |
| bool IsASCII = true; | |
| if (auto *MD = M.getModuleFlag("zos_le_char_mode")) { | |
| const StringRef &CharMode = cast<MDString>(MD)->getString(); | |
| if (CharMode == "ebcdic") | |
| IsASCII = false; | |
| else if (CharMode != "ascii") | |
| OutContext.reportError( | |
| {}, "Only ascii or ebcdic are allowed for zos_le_char_mode"); | |
| } | |
| if (IsASCII) | |
| Flgs |= static_cast<uint8_t>( | |
| PPA2Flags::CompiledUnitASCII); // Setting bit for ASCII char. mode. | |
| 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(CompilationTimeEBCDIC.str()); | |
| OutStreamer->emitBytes(VersionEBCDIC.str()); | |
| OutStreamer->emitInt16(0x0000); // Service level string length. | |
| // The binder requires that the offset to the PPA2 be emitted in a different, | |
| // specially-named section. | |
| OutStreamer->switchSection(getObjFileLowering().getPPA2ListSection()); | |
| // Emit 8 byte alignment. | |
| // Emit pointer to PPA2 label. | |
| OutStreamer->AddComment("A(PPA2-CELQSTRT)"); | |
| OutStreamer->emitAbsoluteSymbolDiff(PPA2Sym, CELQSTRT, 8); | |
| OutStreamer->popSection(); | |
| } | |
| void SystemZAsmPrinter::emitGlobalAlias(const Module &M, | |
| const GlobalAlias &GA) { | |
| if (!TM.getTargetTriple().isOSzOS()) | |
| return AsmPrinter::emitGlobalAlias(M, GA); | |
| // Aliased function labels have already been emitted for z/OS | |
| } | |
| const MCExpr *SystemZAsmPrinter::lowerConstant(const Constant *CV, | |
| const Constant *BaseCV, | |
| uint64_t Offset) { | |
| const Triple &TargetTriple = TM.getTargetTriple(); | |
| if (TargetTriple.isOSzOS()) { | |
| const GlobalAlias *GA = dyn_cast<GlobalAlias>(CV); | |
| const GlobalVariable *GV = dyn_cast<GlobalVariable>(CV); | |
| const Function *FV = dyn_cast<Function>(CV); | |
| bool IsFunc = !GV && (FV || (GA && isa<Function>(GA->getAliaseeObject()))); | |
| MCSymbol *Sym = NULL; | |
| if (GA) | |
| Sym = getSymbol(GA); | |
| else if (IsFunc) | |
| Sym = getSymbol(FV); | |
| else if (GV) | |
| Sym = getSymbol(GV); | |
| if (IsFunc) { | |
| OutStreamer->emitSymbolAttribute(Sym, MCSA_ELF_TypeFunction); | |
| if (FV->hasExternalLinkage()) | |
| return MCSpecifierExpr::create(MCSymbolRefExpr::create(Sym, OutContext), | |
| SystemZ::S_VCon, OutContext); | |
| // Trigger creation of function descriptor in ADA for internal | |
| // functions. | |
| unsigned Disp = ADATable.insert(Sym, SystemZII::MO_ADA_DIRECT_FUNC_DESC); | |
| return MCBinaryExpr::createAdd( | |
| MCSpecifierExpr::create( | |
| MCSymbolRefExpr::create( | |
| getObjFileLowering().getADASection()->getBeginSymbol(), | |
| OutContext), | |
| SystemZ::S_None, OutContext), | |
| MCConstantExpr::create(Disp, OutContext), OutContext); | |
| } | |
| if (Sym) { | |
| OutStreamer->emitSymbolAttribute(Sym, MCSA_ELF_TypeObject); | |
| return MCSymbolRefExpr::create(Sym, OutContext); | |
| } | |
| } | |
| return AsmPrinter::lowerConstant(CV); | |
| } | |
| void SystemZAsmPrinter::emitFunctionEntryLabel() { | |
| const SystemZSubtarget &Subtarget = MF->getSubtarget<SystemZSubtarget>(); | |
| if (Subtarget.getTargetTriple().isOSzOS()) { | |
| calculatePPA1(); | |
| // EntryPoint Marker | |
| const MachineFrameInfo &MFFrame = MF->getFrameInfo(); | |
| bool IsUsingAlloca = MFFrame.hasVarSizedObjects(); | |
| uint32_t DSASize = MFFrame.getStackSize(); | |
| bool IsLeaf = DSASize == 0 && MFFrame.getCalleeSavedInfo().empty(); | |
| // Set Flags. | |
| uint8_t Flags = 0; | |
| if (IsLeaf) | |
| Flags |= 0x08; | |
| if (IsUsingAlloca) | |
| Flags |= 0x04; | |
| // Combine into top 27 bits of DSASize and bottom 5 bits of Flags. | |
| uint32_t DSAAndFlags = DSASize & 0xFFFFFFE0; // (x/32) << 5 | |
| DSAAndFlags |= Flags; | |
| // Emit entry point marker section. | |
| OutStreamer->AddComment("XPLINK Routine Layout Entry"); | |
| OutStreamer->emitLabel(DeferredPPA1.back().EPMarker); | |
| OutStreamer->AddComment("Eyecatcher 0x00C300C500C500"); | |
| OutStreamer->emitIntValueInHex(0x00C300C500C500, 7); // Eyecatcher. | |
| OutStreamer->AddComment("Mark Type C'1'"); | |
| OutStreamer->emitInt8(0xF1); // Mark Type. | |
| OutStreamer->AddComment("Offset to PPA1"); | |
| OutStreamer->emitAbsoluteSymbolDiff(DeferredPPA1.back().PPA1, | |
| DeferredPPA1.back().EPMarker, 4); | |
| if (OutStreamer->isVerboseAsm()) { | |
| OutStreamer->AddComment("DSA Size 0x" + Twine::utohexstr(DSASize)); | |
| OutStreamer->AddComment("Entry Flags"); | |
| if (Flags & 0x08) | |
| OutStreamer->AddComment(" Bit 1: 1 = Leaf function"); | |
| else | |
| OutStreamer->AddComment(" Bit 1: 0 = Non-leaf function"); | |
| if (Flags & 0x04) | |
| OutStreamer->AddComment(" Bit 2: 1 = Uses alloca"); | |
| else | |
| OutStreamer->AddComment(" Bit 2: 0 = Does not use alloca"); | |
| } | |
| OutStreamer->emitInt32(DSAAndFlags); | |
| getTargetStreamer()->emitADA(CurrentFnSym, | |
| getObjFileLowering().getADASection()); | |
| } | |
| AsmPrinter::emitFunctionEntryLabel(); | |
| if (Subtarget.getTargetTriple().isOSzOS()) { | |
| const Function *F = &MF->getFunction(); | |
| // Emit aliasing label for function entry point label. | |
| for (const GlobalAlias *Alias : GOAliasMap[F]) { | |
| MCSymbol *Sym = getSymbol(Alias); | |
| OutStreamer->emitSymbolAttribute(Sym, MCSA_ELF_TypeFunction); | |
| emitVisibility(Sym, Alias->getVisibility()); | |
| emitLinkage(Alias, Sym); | |
| OutStreamer->emitLabel(Sym); | |
| } | |
| } | |
| } | |
| char SystemZAsmPrinter::ID = 0; | |
| INITIALIZE_PASS(SystemZAsmPrinter, "systemz-asm-printer", | |
| "SystemZ Assembly Printer", false, false) | |
| // Force static initialization. | |
| extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void | |
| LLVMInitializeSystemZAsmPrinter() { | |
| RegisterAsmPrinter<SystemZAsmPrinter> X(getTheSystemZTarget()); | |
| } |