blob: 5bdafa1d1da284553e3f0b20604aad75db1828b4 [file] [edit]
//===-- 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); }