| //===-- SystemZELFAsmPrinter.cpp - SystemZ ELF asm 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements the SystemZELFAsmPrinter class. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "SystemZELFAsmPrinter.h" |
| #include "MCTargetDesc/SystemZMCTargetDesc.h" |
| #include "SystemZConstantPoolValue.h" |
| #include "SystemZMCInstLower.h" |
| #include "SystemZSubtarget.h" |
| #include "llvm/BinaryFormat/ELF.h" |
| #include "llvm/CodeGen/MachineConstantPool.h" |
| #include "llvm/CodeGen/StackMaps.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/MC/MCExpr.h" |
| #include "llvm/MC/MCInstBuilder.h" |
| #include "llvm/MC/MCSectionELF.h" |
| #include "llvm/MC/MCStreamer.h" |
| |
| using namespace llvm; |
| |
| // 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; |
| } |
| } |
| |
| 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); |
| } |
| |
| SystemZELFAsmPrinter::SystemZELFAsmPrinter(TargetMachine &TM, |
| std::unique_ptr<MCStreamer> Streamer) |
| : SystemZAsmPrinter(TM, std::move(Streamer)) {} |
| |
| void SystemZELFAsmPrinter::emitInstruction(const MachineInstr *MI) { |
| SystemZMCInstLower Lower(MF->getContext(), *this); |
| |
| switch (MI->getOpcode()) { |
| 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 SystemZ::CallBASR: |
| EmitToStreamer(*OutStreamer, MCInstBuilder(SystemZ::BASR) |
| .addReg(SystemZ::R14D) |
| .addReg(MI->getOperand(0).getReg())); |
| return; |
| case SystemZ::CallBRASL: |
| EmitToStreamer(*OutStreamer, MCInstBuilder(SystemZ::BRASL) |
| .addReg(SystemZ::R14D) |
| .addExpr(Lower.getExpr(MI->getOperand(0), |
| SystemZ::S_PLT))); |
| return; |
| case SystemZ::CallJG: |
| EmitToStreamer(*OutStreamer, MCInstBuilder(SystemZ::JG) |
| .addExpr(Lower.getExpr(MI->getOperand(0), |
| SystemZ::S_PLT))); |
| return; |
| case SystemZ::CallBRCL: |
| EmitToStreamer(*OutStreamer, MCInstBuilder(SystemZ::BRCL) |
| .addImm(MI->getOperand(0).getImm()) |
| .addImm(MI->getOperand(1).getImm()) |
| .addExpr(Lower.getExpr(MI->getOperand(2), |
| SystemZ::S_PLT))); |
| return; |
| case SystemZ::TLS_GDCALL: |
| EmitToStreamer(*OutStreamer, MCInstBuilder(SystemZ::BRASL) |
| .addReg(SystemZ::R14D) |
| .addExpr(getTLSGetOffset(MF->getContext())) |
| .addExpr(Lower.getExpr(MI->getOperand(0), |
| SystemZ::S_TLSGD))); |
| return; |
| case SystemZ::TLS_LDCALL: |
| EmitToStreamer( |
| *OutStreamer, |
| MCInstBuilder(SystemZ::BRASL) |
| .addReg(SystemZ::R14D) |
| .addExpr(getTLSGetOffset(MF->getContext())) |
| .addExpr(Lower.getExpr(MI->getOperand(0), SystemZ::S_TLSLDM))); |
| return; |
| case SystemZ::GOT: |
| EmitToStreamer(*OutStreamer, |
| MCInstBuilder(SystemZ::LARL) |
| .addReg(MI->getOperand(0).getReg()) |
| .addExpr(getGlobalOffsetTable(MF->getContext()))); |
| return; |
| case SystemZ::LOAD_TLS_BLOCK_ADDR: |
| lowerLOAD_TLS_BLOCK_ADDR(*MI, Lower); |
| return; |
| case SystemZ::LOAD_GLOBAL_STACKGUARD_ADDR: |
| lowerLOAD_GLOBAL_STACKGUARD_ADDR(*MI, Lower); |
| return; |
| default: |
| SystemZAsmPrinter::emitInstruction(MI); |
| return; |
| } |
| } |
| |
| void SystemZELFAsmPrinter::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 SystemZELFAsmPrinter::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 SystemZELFAsmPrinter::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 SystemZELFAsmPrinter::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")) { |
| unsigned Num = F.getFnAttributeAsParsedInteger("patchable-function-entry"); |
| 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 SystemZELFAsmPrinter::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); |
| } |
| |
| void SystemZELFAsmPrinter::lowerLOAD_TLS_BLOCK_ADDR(const MachineInstr &MI, |
| SystemZMCInstLower &Lower) { |
| Register AddrReg = MI.getOperand(0).getReg(); |
| const MachineRegisterInfo &MRI = MI.getParent()->getParent()->getRegInfo(); |
| |
| // EAR can only load the low subregister so use a shift for %a0 to produce |
| // the GR containing %a0 and %a1. |
| const Register Reg32 = |
| MRI.getTargetRegisterInfo()->getSubReg(AddrReg, SystemZ::subreg_l32); |
| |
| // ear <reg>, %a0 |
| EmitToStreamer(*OutStreamer, |
| MCInstBuilder(SystemZ::EAR).addReg(Reg32).addReg(SystemZ::A0)); |
| |
| // sllg <reg>, <reg>, 32 |
| EmitToStreamer(*OutStreamer, MCInstBuilder(SystemZ::SLLG) |
| .addReg(AddrReg) |
| .addReg(AddrReg) |
| .addReg(0) |
| .addImm(32)); |
| |
| // ear <reg>, %a1 |
| EmitToStreamer(*OutStreamer, |
| MCInstBuilder(SystemZ::EAR).addReg(Reg32).addReg(SystemZ::A1)); |
| } |
| |
| void SystemZELFAsmPrinter::lowerLOAD_GLOBAL_STACKGUARD_ADDR( |
| const MachineInstr &MI, SystemZMCInstLower &Lower) { |
| Register AddrReg = MI.getOperand(0).getReg(); |
| const MachineFunction &MF = *(MI.getParent()->getParent()); |
| const Module *M = MF.getFunction().getParent(); |
| const TargetLowering *TLI = MF.getSubtarget().getTargetLowering(); |
| |
| // Obtain the global value (assert if stack guard variable can't be found). |
| const GlobalVariable *GV = cast<GlobalVariable>( |
| TLI->getSDagStackGuard(*M, TLI->getLibcallLoweringInfo())); |
| |
| // If configured, emit the `__stack_protector_loc` entry |
| if (M->hasStackProtectorGuardRecord()) { |
| MCSymbol *Sym = OutContext.createTempSymbol(); |
| OutStreamer->pushSection(); |
| OutStreamer->switchSection(OutContext.getELFSection( |
| "__stack_protector_loc", ELF::SHT_PROGBITS, ELF::SHF_ALLOC)); |
| OutStreamer->emitSymbolValue(Sym, getDataLayout().getPointerSize()); |
| OutStreamer->popSection(); |
| OutStreamer->emitLabel(Sym); |
| } |
| // Emit the address load. |
| if (M->getPICLevel() == PICLevel::NotPIC) { |
| EmitToStreamer(*OutStreamer, MCInstBuilder(SystemZ::LARL) |
| .addReg(AddrReg) |
| .addExpr(MCSymbolRefExpr::create( |
| getSymbol(GV), OutContext))); |
| } else { |
| EmitToStreamer(*OutStreamer, |
| MCInstBuilder(SystemZ::LGRL) |
| .addReg(AddrReg) |
| .addExpr(MCSymbolRefExpr::create( |
| getSymbol(GV), SystemZ::S_GOTENT, OutContext))); |
| } |
| } |
| |
| // The *alignment* of 128-bit vector types is different between the software |
| // and hardware vector ABIs. If there is an externally visible use of a |
| // vector type in the module it should be annotated with an attribute. |
| void SystemZELFAsmPrinter::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 SystemZELFAsmPrinter::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); |
| } |
| |
| void SystemZELFAsmPrinter::emitEndOfAsmFile(Module &M) { emitAttributes(M); } |