1//===-- SystemZELFAsmPrinter.cpp - SystemZ ELF asm printer ----------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements the SystemZELFAsmPrinter class.
10//
11//===----------------------------------------------------------------------===//
12
13#include "SystemZELFAsmPrinter.h"
14#include "MCTargetDesc/SystemZMCTargetDesc.h"
15#include "SystemZConstantPoolValue.h"
16#include "SystemZMCInstLower.h"
17#include "SystemZSubtarget.h"
18#include "llvm/BinaryFormat/ELF.h"
19#include "llvm/CodeGen/MachineConstantPool.h"
20#include "llvm/CodeGen/StackMaps.h"
21#include "llvm/IR/Module.h"
22#include "llvm/MC/MCExpr.h"
23#include "llvm/MC/MCInstBuilder.h"
24#include "llvm/MC/MCSectionELF.h"
25#include "llvm/MC/MCStreamer.h"
26
27using namespace llvm;
28
29// Emit the largest nop instruction smaller than or equal to NumBytes
30// bytes. Return the size of nop emitted.
31static unsigned EmitNop(MCContext &OutContext, MCStreamer &OutStreamer,
32 unsigned NumBytes, const MCSubtargetInfo &STI) {
33 if (NumBytes < 2) {
34 llvm_unreachable("Zero nops?");
35 return 0;
36 } else if (NumBytes < 4) {
37 OutStreamer.emitInstruction(
38 Inst: MCInstBuilder(SystemZ::BCRAsm).addImm(Val: 0).addReg(Reg: SystemZ::R0D), STI);
39 return 2;
40 } else if (NumBytes < 6) {
41 OutStreamer.emitInstruction(
42 Inst: MCInstBuilder(SystemZ::BCAsm).addImm(Val: 0).addReg(Reg: 0).addImm(Val: 0).addReg(Reg: 0),
43 STI);
44 return 4;
45 } else {
46 MCSymbol *DotSym = OutContext.createTempSymbol();
47 const MCSymbolRefExpr *Dot = MCSymbolRefExpr::create(Symbol: DotSym, Ctx&: OutContext);
48 OutStreamer.emitLabel(Symbol: DotSym);
49 OutStreamer.emitInstruction(
50 Inst: MCInstBuilder(SystemZ::BRCLAsm).addImm(Val: 0).addExpr(Val: Dot), STI);
51 return 6;
52 }
53}
54
55static const MCSymbolRefExpr *getTLSGetOffset(MCContext &Context) {
56 StringRef Name = "__tls_get_offset";
57 return MCSymbolRefExpr::create(Symbol: Context.getOrCreateSymbol(Name),
58 specifier: SystemZ::S_PLT, Ctx&: Context);
59}
60
61static const MCSymbolRefExpr *getGlobalOffsetTable(MCContext &Context) {
62 StringRef Name = "_GLOBAL_OFFSET_TABLE_";
63 return MCSymbolRefExpr::create(Symbol: Context.getOrCreateSymbol(Name), Ctx&: Context);
64}
65
66SystemZELFAsmPrinter::SystemZELFAsmPrinter(TargetMachine &TM,
67 std::unique_ptr<MCStreamer> Streamer)
68 : SystemZAsmPrinter(TM, std::move(Streamer)) {}
69
70void SystemZELFAsmPrinter::emitInstruction(const MachineInstr *MI) {
71 SystemZMCInstLower Lower(MF->getContext(), *this);
72
73 switch (MI->getOpcode()) {
74 case TargetOpcode::FENTRY_CALL:
75 LowerFENTRY_CALL(MI: *MI, Lower);
76 return;
77 case TargetOpcode::STACKMAP:
78 LowerSTACKMAP(MI: *MI);
79 return;
80 case TargetOpcode::PATCHPOINT:
81 LowerPATCHPOINT(MI: *MI, Lower);
82 return;
83 case TargetOpcode::PATCHABLE_FUNCTION_ENTER:
84 LowerPATCHABLE_FUNCTION_ENTER(MI: *MI, Lower);
85 return;
86 case TargetOpcode::PATCHABLE_RET:
87 LowerPATCHABLE_RET(MI: *MI, Lower);
88 return;
89 case SystemZ::CallBASR:
90 EmitToStreamer(S&: *OutStreamer, Inst: MCInstBuilder(SystemZ::BASR)
91 .addReg(Reg: SystemZ::R14D)
92 .addReg(Reg: MI->getOperand(i: 0).getReg()));
93 return;
94 case SystemZ::CallBRASL:
95 EmitToStreamer(S&: *OutStreamer, Inst: MCInstBuilder(SystemZ::BRASL)
96 .addReg(Reg: SystemZ::R14D)
97 .addExpr(Val: Lower.getExpr(MO: MI->getOperand(i: 0),
98 SystemZ::S_PLT)));
99 return;
100 case SystemZ::CallJG:
101 EmitToStreamer(S&: *OutStreamer, Inst: MCInstBuilder(SystemZ::JG)
102 .addExpr(Val: Lower.getExpr(MO: MI->getOperand(i: 0),
103 SystemZ::S_PLT)));
104 return;
105 case SystemZ::CallBRCL:
106 EmitToStreamer(S&: *OutStreamer, Inst: MCInstBuilder(SystemZ::BRCL)
107 .addImm(Val: MI->getOperand(i: 0).getImm())
108 .addImm(Val: MI->getOperand(i: 1).getImm())
109 .addExpr(Val: Lower.getExpr(MO: MI->getOperand(i: 2),
110 SystemZ::S_PLT)));
111 return;
112 case SystemZ::TLS_GDCALL:
113 EmitToStreamer(S&: *OutStreamer, Inst: MCInstBuilder(SystemZ::BRASL)
114 .addReg(Reg: SystemZ::R14D)
115 .addExpr(Val: getTLSGetOffset(Context&: MF->getContext()))
116 .addExpr(Val: Lower.getExpr(MO: MI->getOperand(i: 0),
117 SystemZ::S_TLSGD)));
118 return;
119 case SystemZ::TLS_LDCALL:
120 EmitToStreamer(
121 S&: *OutStreamer,
122 Inst: MCInstBuilder(SystemZ::BRASL)
123 .addReg(Reg: SystemZ::R14D)
124 .addExpr(Val: getTLSGetOffset(Context&: MF->getContext()))
125 .addExpr(Val: Lower.getExpr(MO: MI->getOperand(i: 0), SystemZ::S_TLSLDM)));
126 return;
127 case SystemZ::GOT:
128 EmitToStreamer(S&: *OutStreamer,
129 Inst: MCInstBuilder(SystemZ::LARL)
130 .addReg(Reg: MI->getOperand(i: 0).getReg())
131 .addExpr(Val: getGlobalOffsetTable(Context&: MF->getContext())));
132 return;
133 case SystemZ::LOAD_TLS_BLOCK_ADDR:
134 lowerLOAD_TLS_BLOCK_ADDR(MI: *MI, Lower);
135 return;
136 case SystemZ::LOAD_GLOBAL_STACKGUARD_ADDR:
137 lowerLOAD_GLOBAL_STACKGUARD_ADDR(MI: *MI, Lower);
138 return;
139 default:
140 SystemZAsmPrinter::emitInstruction(MI);
141 return;
142 }
143}
144
145void SystemZELFAsmPrinter::LowerFENTRY_CALL(const MachineInstr &MI,
146 SystemZMCInstLower &Lower) {
147 MCContext &Ctx = MF->getContext();
148 if (MF->getFunction().hasFnAttribute(Kind: "mrecord-mcount")) {
149 MCSymbol *DotSym = OutContext.createTempSymbol();
150 OutStreamer->pushSection();
151 OutStreamer->switchSection(
152 Section: Ctx.getELFSection(Section: "__mcount_loc", Type: ELF::SHT_PROGBITS, Flags: ELF::SHF_ALLOC));
153 OutStreamer->emitSymbolValue(Sym: DotSym, Size: 8);
154 OutStreamer->popSection();
155 OutStreamer->emitLabel(Symbol: DotSym);
156 }
157
158 if (MF->getFunction().hasFnAttribute(Kind: "mnop-mcount")) {
159 EmitNop(OutContext&: Ctx, OutStreamer&: *OutStreamer, NumBytes: 6, STI: getSubtargetInfo());
160 return;
161 }
162
163 MCSymbol *fentry = Ctx.getOrCreateSymbol(Name: "__fentry__");
164 const MCSymbolRefExpr *Op =
165 MCSymbolRefExpr::create(Symbol: fentry, specifier: SystemZ::S_PLT, Ctx);
166 OutStreamer->emitInstruction(
167 Inst: MCInstBuilder(SystemZ::BRASL).addReg(Reg: SystemZ::R0D).addExpr(Val: Op),
168 STI: getSubtargetInfo());
169}
170
171void SystemZELFAsmPrinter::LowerSTACKMAP(const MachineInstr &MI) {
172 auto *TII = MF->getSubtarget<SystemZSubtarget>().getInstrInfo();
173
174 unsigned NumNOPBytes = MI.getOperand(i: 1).getImm();
175
176 auto &Ctx = OutStreamer->getContext();
177 MCSymbol *MILabel = Ctx.createTempSymbol();
178 OutStreamer->emitLabel(Symbol: MILabel);
179
180 SM.recordStackMap(L: *MILabel, MI);
181 assert(NumNOPBytes % 2 == 0 && "Invalid number of NOP bytes requested!");
182
183 // Scan ahead to trim the shadow.
184 unsigned ShadowBytes = 0;
185 const MachineBasicBlock &MBB = *MI.getParent();
186 MachineBasicBlock::const_iterator MII(MI);
187 ++MII;
188 while (ShadowBytes < NumNOPBytes) {
189 if (MII == MBB.end() || MII->getOpcode() == TargetOpcode::PATCHPOINT ||
190 MII->getOpcode() == TargetOpcode::STACKMAP)
191 break;
192 ShadowBytes += TII->getInstSizeInBytes(MI: *MII);
193 if (MII->isCall())
194 break;
195 ++MII;
196 }
197
198 // Emit nops.
199 while (ShadowBytes < NumNOPBytes)
200 ShadowBytes += EmitNop(OutContext, OutStreamer&: *OutStreamer, NumBytes: NumNOPBytes - ShadowBytes,
201 STI: getSubtargetInfo());
202}
203
204// Lower a patchpoint of the form:
205// [<def>], <id>, <numBytes>, <target>, <numArgs>
206void SystemZELFAsmPrinter::LowerPATCHPOINT(const MachineInstr &MI,
207 SystemZMCInstLower &Lower) {
208 auto &Ctx = OutStreamer->getContext();
209 MCSymbol *MILabel = Ctx.createTempSymbol();
210 OutStreamer->emitLabel(Symbol: MILabel);
211
212 SM.recordPatchPoint(L: *MILabel, MI);
213 PatchPointOpers Opers(&MI);
214
215 unsigned EncodedBytes = 0;
216 const MachineOperand &CalleeMO = Opers.getCallTarget();
217
218 if (CalleeMO.isImm()) {
219 uint64_t CallTarget = CalleeMO.getImm();
220 if (CallTarget) {
221 unsigned ScratchIdx = -1;
222 unsigned ScratchReg = 0;
223 do {
224 ScratchIdx = Opers.getNextScratchIdx(StartIdx: ScratchIdx + 1);
225 ScratchReg = MI.getOperand(i: ScratchIdx).getReg();
226 } while (ScratchReg == SystemZ::R0D);
227
228 // Materialize the call target address
229 EmitToStreamer(S&: *OutStreamer, Inst: MCInstBuilder(SystemZ::LLILF)
230 .addReg(Reg: ScratchReg)
231 .addImm(Val: CallTarget & 0xFFFFFFFF));
232 EncodedBytes += 6;
233 if (CallTarget >> 32) {
234 EmitToStreamer(S&: *OutStreamer, Inst: MCInstBuilder(SystemZ::IIHF)
235 .addReg(Reg: ScratchReg)
236 .addImm(Val: CallTarget >> 32));
237 EncodedBytes += 6;
238 }
239
240 EmitToStreamer(S&: *OutStreamer, Inst: MCInstBuilder(SystemZ::BASR)
241 .addReg(Reg: SystemZ::R14D)
242 .addReg(Reg: ScratchReg));
243 EncodedBytes += 2;
244 }
245 } else if (CalleeMO.isGlobal()) {
246 const MCExpr *Expr = Lower.getExpr(MO: CalleeMO, SystemZ::S_PLT);
247 EmitToStreamer(
248 S&: *OutStreamer,
249 Inst: MCInstBuilder(SystemZ::BRASL).addReg(Reg: SystemZ::R14D).addExpr(Val: Expr));
250 EncodedBytes += 6;
251 }
252
253 // Emit padding.
254 unsigned NumBytes = Opers.getNumPatchBytes();
255 assert(NumBytes >= EncodedBytes &&
256 "Patchpoint can't request size less than the length of a call.");
257 assert((NumBytes - EncodedBytes) % 2 == 0 &&
258 "Invalid number of NOP bytes requested!");
259 while (EncodedBytes < NumBytes)
260 EncodedBytes += EmitNop(OutContext, OutStreamer&: *OutStreamer, NumBytes: NumBytes - EncodedBytes,
261 STI: getSubtargetInfo());
262}
263
264void SystemZELFAsmPrinter::LowerPATCHABLE_FUNCTION_ENTER(
265 const MachineInstr &MI, SystemZMCInstLower &Lower) {
266 const MachineFunction &MF = *(MI.getParent()->getParent());
267 const Function &F = MF.getFunction();
268
269 // If patchable-function-entry is set, emit in-function nops here.
270 if (F.hasFnAttribute(Kind: "patchable-function-entry")) {
271 unsigned Num = F.getFnAttributeAsParsedInteger(Kind: "patchable-function-entry");
272 for (unsigned I = 0; I < Num; ++I)
273 EmitToStreamer(S&: *OutStreamer, Inst: MF.getSubtarget().getInstrInfo()->getNop());
274 return;
275 }
276 // Otherwise, emit xray sled.
277 // .begin:
278 // j .end # -> stmg %r2, %r15, 16(%r15)
279 // nop
280 // llilf %2, FuncID
281 // brasl %r14, __xray_FunctionEntry@GOT
282 // .end:
283 //
284 // Update compiler-rt/lib/xray/xray_s390x.cpp accordingly when number
285 // of instructions change.
286 bool HasVectorFeature =
287 TM.getMCSubtargetInfo().hasFeature(Feature: SystemZ::FeatureVector) &&
288 !TM.getMCSubtargetInfo().hasFeature(Feature: SystemZ::FeatureSoftFloat);
289 MCSymbol *FuncEntry = OutContext.getOrCreateSymbol(
290 Name: HasVectorFeature ? "__xray_FunctionEntryVec" : "__xray_FunctionEntry");
291 MCSymbol *BeginOfSled = OutContext.createTempSymbol(Name: "xray_sled_", AlwaysAddSuffix: true);
292 MCSymbol *EndOfSled = OutContext.createTempSymbol();
293 OutStreamer->emitLabel(Symbol: BeginOfSled);
294 EmitToStreamer(S&: *OutStreamer,
295 Inst: MCInstBuilder(SystemZ::J)
296 .addExpr(Val: MCSymbolRefExpr::create(Symbol: EndOfSled, Ctx&: OutContext)));
297 EmitNop(OutContext, OutStreamer&: *OutStreamer, NumBytes: 2, STI: getSubtargetInfo());
298 EmitToStreamer(S&: *OutStreamer,
299 Inst: MCInstBuilder(SystemZ::LLILF).addReg(Reg: SystemZ::R2D).addImm(Val: 0));
300 EmitToStreamer(S&: *OutStreamer, Inst: MCInstBuilder(SystemZ::BRASL)
301 .addReg(Reg: SystemZ::R14D)
302 .addExpr(Val: MCSymbolRefExpr::create(
303 Symbol: FuncEntry, specifier: SystemZ::S_PLT, Ctx&: OutContext)));
304 OutStreamer->emitLabel(Symbol: EndOfSled);
305 recordSled(Sled: BeginOfSled, MI, Kind: SledKind::FUNCTION_ENTER, Version: 2);
306}
307
308void SystemZELFAsmPrinter::LowerPATCHABLE_RET(const MachineInstr &MI,
309 SystemZMCInstLower &Lower) {
310 unsigned OpCode = MI.getOperand(i: 0).getImm();
311 MCSymbol *FallthroughLabel = nullptr;
312 if (OpCode == SystemZ::CondReturn) {
313 FallthroughLabel = OutContext.createTempSymbol();
314 int64_t Cond0 = MI.getOperand(i: 1).getImm();
315 int64_t Cond1 = MI.getOperand(i: 2).getImm();
316 EmitToStreamer(S&: *OutStreamer, Inst: MCInstBuilder(SystemZ::BRC)
317 .addImm(Val: Cond0)
318 .addImm(Val: Cond1 ^ Cond0)
319 .addExpr(Val: MCSymbolRefExpr::create(
320 Symbol: FallthroughLabel, Ctx&: OutContext)));
321 }
322 // .begin:
323 // br %r14 # -> stmg %r2, %r15, 24(%r15)
324 // nop
325 // nop
326 // llilf %2,FuncID
327 // j __xray_FunctionExit@GOT
328 //
329 // Update compiler-rt/lib/xray/xray_s390x.cpp accordingly when number
330 // of instructions change.
331 bool HasVectorFeature =
332 TM.getMCSubtargetInfo().hasFeature(Feature: SystemZ::FeatureVector) &&
333 !TM.getMCSubtargetInfo().hasFeature(Feature: SystemZ::FeatureSoftFloat);
334 MCSymbol *FuncExit = OutContext.getOrCreateSymbol(
335 Name: HasVectorFeature ? "__xray_FunctionExitVec" : "__xray_FunctionExit");
336 MCSymbol *BeginOfSled = OutContext.createTempSymbol(Name: "xray_sled_", AlwaysAddSuffix: true);
337 OutStreamer->emitLabel(Symbol: BeginOfSled);
338 EmitToStreamer(S&: *OutStreamer,
339 Inst: MCInstBuilder(SystemZ::BR).addReg(Reg: SystemZ::R14D));
340 EmitNop(OutContext, OutStreamer&: *OutStreamer, NumBytes: 4, STI: getSubtargetInfo());
341 EmitToStreamer(S&: *OutStreamer,
342 Inst: MCInstBuilder(SystemZ::LLILF).addReg(Reg: SystemZ::R2D).addImm(Val: 0));
343 EmitToStreamer(S&: *OutStreamer, Inst: MCInstBuilder(SystemZ::J)
344 .addExpr(Val: MCSymbolRefExpr::create(
345 Symbol: FuncExit, specifier: SystemZ::S_PLT, Ctx&: OutContext)));
346 if (FallthroughLabel)
347 OutStreamer->emitLabel(Symbol: FallthroughLabel);
348 recordSled(Sled: BeginOfSled, MI, Kind: SledKind::FUNCTION_EXIT, Version: 2);
349}
350
351void SystemZELFAsmPrinter::lowerLOAD_TLS_BLOCK_ADDR(const MachineInstr &MI,
352 SystemZMCInstLower &Lower) {
353 Register AddrReg = MI.getOperand(i: 0).getReg();
354 const MachineRegisterInfo &MRI = MI.getParent()->getParent()->getRegInfo();
355
356 // EAR can only load the low subregister so use a shift for %a0 to produce
357 // the GR containing %a0 and %a1.
358 const Register Reg32 =
359 MRI.getTargetRegisterInfo()->getSubReg(Reg: AddrReg, Idx: SystemZ::subreg_l32);
360
361 // ear <reg>, %a0
362 EmitToStreamer(S&: *OutStreamer,
363 Inst: MCInstBuilder(SystemZ::EAR).addReg(Reg: Reg32).addReg(Reg: SystemZ::A0));
364
365 // sllg <reg>, <reg>, 32
366 EmitToStreamer(S&: *OutStreamer, Inst: MCInstBuilder(SystemZ::SLLG)
367 .addReg(Reg: AddrReg)
368 .addReg(Reg: AddrReg)
369 .addReg(Reg: 0)
370 .addImm(Val: 32));
371
372 // ear <reg>, %a1
373 EmitToStreamer(S&: *OutStreamer,
374 Inst: MCInstBuilder(SystemZ::EAR).addReg(Reg: Reg32).addReg(Reg: SystemZ::A1));
375}
376
377void SystemZELFAsmPrinter::lowerLOAD_GLOBAL_STACKGUARD_ADDR(
378 const MachineInstr &MI, SystemZMCInstLower &Lower) {
379 Register AddrReg = MI.getOperand(i: 0).getReg();
380 const MachineFunction &MF = *(MI.getParent()->getParent());
381 const Module *M = MF.getFunction().getParent();
382 const TargetLowering *TLI = MF.getSubtarget().getTargetLowering();
383
384 // Obtain the global value (assert if stack guard variable can't be found).
385 const GlobalVariable *GV = cast<GlobalVariable>(
386 Val: TLI->getSDagStackGuard(M: *M, Libcalls: TLI->getLibcallLoweringInfo()));
387
388 // If configured, emit the `__stack_protector_loc` entry
389 if (M->hasStackProtectorGuardRecord()) {
390 MCSymbol *Sym = OutContext.createTempSymbol();
391 OutStreamer->pushSection();
392 OutStreamer->switchSection(Section: OutContext.getELFSection(
393 Section: "__stack_protector_loc", Type: ELF::SHT_PROGBITS, Flags: ELF::SHF_ALLOC));
394 OutStreamer->emitSymbolValue(Sym, Size: getDataLayout().getPointerSize());
395 OutStreamer->popSection();
396 OutStreamer->emitLabel(Symbol: Sym);
397 }
398 // Emit the address load.
399 if (M->getPICLevel() == PICLevel::NotPIC) {
400 EmitToStreamer(S&: *OutStreamer, Inst: MCInstBuilder(SystemZ::LARL)
401 .addReg(Reg: AddrReg)
402 .addExpr(Val: MCSymbolRefExpr::create(
403 Symbol: getSymbol(GV), Ctx&: OutContext)));
404 } else {
405 EmitToStreamer(S&: *OutStreamer,
406 Inst: MCInstBuilder(SystemZ::LGRL)
407 .addReg(Reg: AddrReg)
408 .addExpr(Val: MCSymbolRefExpr::create(
409 Symbol: getSymbol(GV), specifier: SystemZ::S_GOTENT, Ctx&: OutContext)));
410 }
411}
412
413// The *alignment* of 128-bit vector types is different between the software
414// and hardware vector ABIs. If there is an externally visible use of a
415// vector type in the module it should be annotated with an attribute.
416void SystemZELFAsmPrinter::emitAttributes(Module &M) {
417 if (M.getModuleFlag(Key: "s390x-visible-vector-ABI")) {
418 bool HasVectorFeature =
419 TM.getMCSubtargetInfo().hasFeature(Feature: SystemZ::FeatureVector);
420 OutStreamer->emitGNUAttribute(Tag: 8, Value: HasVectorFeature ? 2 : 1);
421 }
422}
423
424// Convert a SystemZ-specific constant pool modifier into the associated
425// specifier.
426static uint8_t getSpecifierFromModifier(SystemZCP::SystemZCPModifier Modifier) {
427 switch (Modifier) {
428 case SystemZCP::TLSGD:
429 return SystemZ::S_TLSGD;
430 case SystemZCP::TLSLDM:
431 return SystemZ::S_TLSLDM;
432 case SystemZCP::DTPOFF:
433 return SystemZ::S_DTPOFF;
434 case SystemZCP::NTPOFF:
435 return SystemZ::S_NTPOFF;
436 }
437 llvm_unreachable("Invalid SystemCPModifier!");
438}
439
440void SystemZELFAsmPrinter::emitMachineConstantPoolValue(
441 MachineConstantPoolValue *MCPV) {
442 auto *ZCPV = static_cast<SystemZConstantPoolValue *>(MCPV);
443
444 const MCExpr *Expr = MCSymbolRefExpr::create(
445 Symbol: getSymbol(GV: ZCPV->getGlobalValue()),
446 specifier: getSpecifierFromModifier(Modifier: ZCPV->getModifier()), Ctx&: OutContext);
447 uint64_t Size = getDataLayout().getTypeAllocSize(Ty: ZCPV->getType());
448
449 OutStreamer->emitValue(Value: Expr, Size);
450}
451
452void SystemZELFAsmPrinter::emitEndOfAsmFile(Module &M) { emitAttributes(M); }
453