1//===- SystemZHLASMAsmStreamer.cpp - HLASM Assembly Text Output -----------===//
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#include "SystemZHLASMAsmStreamer.h"
10#include "llvm/ADT/StringExtras.h"
11#include "llvm/BinaryFormat/GOFF.h"
12#include "llvm/MC/MCExpr.h"
13#include "llvm/MC/MCSectionGOFF.h"
14#include "llvm/MC/MCSymbolGOFF.h"
15#include "llvm/Support/Casting.h"
16#include "llvm/Support/Signals.h"
17#include <sstream>
18
19using namespace llvm;
20
21void SystemZHLASMAsmStreamer::visitUsedSymbol(const MCSymbol &Sym) {
22 Assembler->registerSymbol(Symbol: Sym);
23}
24
25void SystemZHLASMAsmStreamer::EmitEOL() {
26 // Comments are emitted on a new line before the instruction.
27 if (IsVerboseAsm)
28 EmitComment();
29
30 std::istringstream Stream(Str);
31 SmallVector<std::string> Lines;
32 std::string Line;
33 while (std::getline(in&: Stream, str&: Line, delim: '\n'))
34 Lines.push_back(Elt: Line);
35
36 for (auto S : Lines) {
37 if (LLVM_LIKELY(S.length() < ContIndicatorColumn)) {
38 FOS << S;
39 // Each line in HLASM must fill the full 80 characters.
40 FOS.PadToColumn(NewCol: InstLimit);
41 FOS << "\n";
42 } else {
43 // If last character before end of the line is not a space
44 // we must insert an additional non-space character that
45 // is not part of the statement coding. We just reuse
46 // the existing character by making the new substring start
47 // 1 character sooner, thus "duplicating" that character
48 // If The last character is a space. We insert an X instead.
49 std::string TmpSubStr = S.substr(pos: 0, n: ContIndicatorColumn);
50 if (!TmpSubStr.compare(pos: ContIndicatorColumn - 1, n1: 1, s: " "))
51 TmpSubStr.replace(pos: ContIndicatorColumn - 1, n1: 1, s: "X");
52
53 FOS << TmpSubStr;
54 FOS.PadToColumn(NewCol: InstLimit);
55 FOS << "\n";
56
57 size_t Emitted = ContIndicatorColumn - 1;
58
59 while (Emitted < S.length()) {
60 if ((S.length() - Emitted) < ContLen)
61 TmpSubStr = S.substr(pos: Emitted, n: S.length());
62 else {
63 TmpSubStr = S.substr(pos: Emitted, n: ContLen);
64 if (!TmpSubStr.compare(pos: ContLen - 1, n1: 1, s: " "))
65 TmpSubStr.replace(pos: ContLen - 1, n1: 1, s: "X");
66 }
67 FOS.PadToColumn(NewCol: ContStartColumn);
68 FOS << TmpSubStr;
69 FOS.PadToColumn(NewCol: InstLimit);
70 FOS << "\n";
71 Emitted += ContLen - 1;
72 }
73 }
74 }
75 Str.clear();
76}
77
78static void emitXATTR(raw_ostream &OS, StringRef Name, MCSectionGOFF *ADA,
79 bool IsIndirectReference, GOFF::ESDLinkageType Linkage,
80 GOFF::ESDExecutable Executable,
81 GOFF::ESDBindingScope BindingScope) {
82 llvm::ListSeparator Sep(",");
83 OS << Name << " XATTR ";
84 OS << Sep << "LINKAGE(" << (Linkage == GOFF::ESD_LT_OS ? "OS" : "XPLINK")
85 << ")";
86
87 const bool NotUnspecified = (Executable != GOFF::ESD_EXE_Unspecified);
88 if (NotUnspecified || IsIndirectReference) {
89 OS << Sep << "REFERENCE(";
90 llvm::ListSeparator SepRef(",");
91
92 if (NotUnspecified)
93 OS << SepRef << (Executable == GOFF::ESD_EXE_CODE ? "CODE" : "DATA");
94
95 if (IsIndirectReference)
96 OS << SepRef << "INDIRECT";
97
98 OS << ")";
99 }
100 // Emit PSECT only for code symbols.
101 if (ADA && Executable != GOFF::ESD_EXE_DATA)
102 OS << Sep << "PSECT(" << ADA->getName() << ")";
103 if (BindingScope != GOFF::ESD_BSC_Unspecified) {
104 OS << Sep << "SCOPE(";
105 switch (BindingScope) {
106 case GOFF::ESD_BSC_Section:
107 OS << "SECTION";
108 break;
109 case GOFF::ESD_BSC_Module:
110 OS << "MODULE";
111 break;
112 case GOFF::ESD_BSC_Library:
113 OS << "LIBRARY";
114 break;
115 case GOFF::ESD_BSC_ImportExport:
116 OS << "EXPORT";
117 break;
118 default:
119 break;
120 }
121 OS << ')';
122 }
123}
124
125static void emitCATTR(raw_ostream &OS, StringRef Name, GOFF::ESDRmode Rmode,
126 GOFF::ESDAlignment Alignment,
127 GOFF::ESDLoadingBehavior LoadBehavior,
128 GOFF::ESDExecutable Executable, bool IsReadOnly,
129 uint32_t SortKey, uint8_t FillByteValue,
130 StringRef PartName) {
131 OS << Name << " CATTR ";
132 OS << "ALIGN(" << static_cast<unsigned>(Alignment) << "),"
133 << "FILL(" << static_cast<unsigned>(FillByteValue) << ")";
134 switch (LoadBehavior) {
135 case GOFF::ESD_LB_Deferred:
136 OS << ",DEFLOAD";
137 break;
138 case GOFF::ESD_LB_NoLoad:
139 OS << ",NOLOAD";
140 break;
141 default:
142 break;
143 }
144 switch (Executable) {
145 case GOFF::ESD_EXE_CODE:
146 OS << ",EXECUTABLE";
147 break;
148 case GOFF::ESD_EXE_DATA:
149 OS << ",NOTEXECUTABLE";
150 break;
151 default:
152 break;
153 }
154 if (IsReadOnly)
155 OS << ",READONLY";
156 if (Rmode != GOFF::ESD_RMODE_None) {
157 OS << ',';
158 OS << "RMODE(";
159 switch (Rmode) {
160 case GOFF::ESD_RMODE_None:
161 llvm_unreachable("");
162 case GOFF::ESD_RMODE_24:
163 OS << "24";
164 break;
165 case GOFF::ESD_RMODE_31:
166 OS << "31";
167 break;
168 case GOFF::ESD_RMODE_64:
169 OS << "64";
170 break;
171 }
172 OS << ')';
173 }
174 if (SortKey)
175 OS << ",PRIORITY(" << SortKey << ")";
176 if (!PartName.empty())
177 OS << ",PART(" << PartName << ")";
178 OS << '\n';
179}
180
181void SystemZHLASMAsmStreamer::changeSection(MCSection *Section,
182 uint32_t Subsection) {
183 auto &Sec = *static_cast<MCSectionGOFF *>(Section);
184 auto EmitExternalName = [&Sec, this]() {
185 if (Sec.hasExternalName())
186 OS << Sec.getName() << " ALIAS C'" << Sec.getExternalName() << "'\n";
187 };
188 switch (Sec.getSymbolType()) {
189 case GOFF::ESD_ST_SectionDefinition: {
190 OS << Sec.getName() << " CSECT\n";
191 Sec.setEmitted();
192 EmitExternalName();
193 break;
194 }
195 case GOFF::ESD_ST_ElementDefinition: {
196 changeSection(Section: Sec.getParent(), Subsection);
197 if (!Sec.isEmitted()) {
198 GOFF::EDAttr ED = Sec.getEDAttributes();
199 emitCATTR(OS, Name: Sec.getName(), Rmode: ED.Rmode, Alignment: Sec.getEDAlignment(),
200 LoadBehavior: ED.LoadBehavior, Executable: GOFF::ESD_EXE_Unspecified, IsReadOnly: ED.IsReadOnly, SortKey: 0,
201 FillByteValue: ED.FillByteValue, PartName: StringRef());
202 if (auto *BeginSym = static_cast<MCSymbolGOFF *>(Sec.getBeginSymbol())) {
203 if (BeginSym->getADA()) {
204 emitXATTR(OS, Name: BeginSym->getName(), ADA: BeginSym->getADA(),
205 /*IsIndirectReference=*/false, Linkage: GOFF::ESD_LT_XPLink,
206 Executable: GOFF::ESD_EXE_Unspecified, BindingScope: GOFF::ESD_BSC_Section);
207 OS << '\n';
208 }
209 }
210 Sec.setEmitted();
211 EmitExternalName();
212 } else
213 OS << Sec.getName() << " CATTR\n";
214 break;
215 }
216 case GOFF::ESD_ST_PartReference: {
217 MCSectionGOFF *ED = Sec.getParent();
218 changeSection(Section: ED->getParent(), Subsection);
219 if (!Sec.isEmitted()) {
220 GOFF::EDAttr EDAttr = ED->getEDAttributes();
221 GOFF::PRAttr PRAttr = Sec.getPRAttributes();
222 emitCATTR(OS, Name: ED->getName(), Rmode: EDAttr.Rmode, Alignment: ED->getEDAlignment(),
223 LoadBehavior: EDAttr.LoadBehavior, Executable: PRAttr.Executable, IsReadOnly: EDAttr.IsReadOnly,
224 SortKey: PRAttr.SortKey, FillByteValue: EDAttr.FillByteValue, PartName: Sec.getName());
225 MCSectionGOFF *ADA =
226 Sec.getBeginSymbol() != nullptr
227 ? static_cast<MCSymbolGOFF *>(Sec.getBeginSymbol())->getADA()
228 : nullptr;
229 emitXATTR(OS, Name: Sec.getName(), ADA, /*IsIndirectReference=*/false,
230 Linkage: PRAttr.Linkage, Executable: PRAttr.Executable, BindingScope: PRAttr.BindingScope);
231 OS << '\n';
232 ED->setEmitted();
233 Sec.setEmitted();
234 EmitExternalName();
235 } else
236 OS << ED->getName() << " CATTR PART(" << Sec.getName() << ")\n";
237 break;
238 }
239 default:
240 llvm_unreachable("Wrong section type");
241 }
242 MCStreamer::changeSection(Section, Subsection);
243 EmitEOL();
244}
245
246void SystemZHLASMAsmStreamer::emitAlignmentDS(uint64_t ByteAlignment,
247 std::optional<int64_t> Value,
248 unsigned ValueSize,
249 unsigned MaxBytesToEmit) {
250 if (!isPowerOf2_64(Value: ByteAlignment))
251 report_fatal_error(reason: "Only power-of-two alignments are supported ");
252
253 OS << " DS 0";
254 switch (ValueSize) {
255 default:
256 llvm_unreachable("Invalid size for machine code value!");
257 case 1:
258 OS << "B";
259 break;
260 case 2:
261 OS << "H";
262 break;
263 case 4:
264 OS << "F";
265 break;
266 case 8:
267 OS << "D";
268 break;
269 case 16:
270 OS << "Q";
271 break;
272 }
273
274 EmitEOL();
275}
276
277void SystemZHLASMAsmStreamer::emitRawComment(const Twine &T, bool TabPrefix) {
278 OS << MAI->getCommentString() << T;
279 EmitEOL();
280}
281
282void SystemZHLASMAsmStreamer::AddComment(const Twine &T, bool EOL) {
283 if (!IsVerboseAsm)
284 return;
285
286 T.toVector(Out&: CommentToEmit);
287
288 if (EOL)
289 CommentToEmit.push_back(Elt: '\n'); // Place comment in a new line.
290}
291
292void SystemZHLASMAsmStreamer::EmitComment() {
293 if (CommentToEmit.empty() && CommentStream.GetNumBytesInBuffer() == 0)
294 return;
295
296 StringRef Comments = CommentToEmit;
297
298 assert(Comments.back() == '\n' && "Comment array not newline terminated");
299 do {
300 // Emit a line of comments, but not exceeding 80 characters.
301 size_t Position = std::min(a: InstLimit - 2, b: Comments.find(C: '\n'));
302 FOS << MAI->getCommentString() << ' ' << Comments.substr(Start: 0, N: Position)
303 << '\n';
304
305 if (Comments[Position] == '\n')
306 Position++;
307 Comments = Comments.substr(Start: Position);
308 } while (!Comments.empty());
309
310 CommentToEmit.clear();
311}
312
313void SystemZHLASMAsmStreamer::emitValueToAlignment(Align Alignment,
314 int64_t Fill,
315 uint8_t FillLen,
316 unsigned MaxBytesToEmit) {
317 emitAlignmentDS(ByteAlignment: Alignment.value(), Value: Fill, ValueSize: FillLen, MaxBytesToEmit);
318}
319
320void SystemZHLASMAsmStreamer::emitCodeAlignment(Align Alignment,
321 const MCSubtargetInfo &STI,
322 unsigned MaxBytesToEmit) {
323 // Emit with a text fill value.
324 if (MAI->getTextAlignFillValue())
325 emitAlignmentDS(ByteAlignment: Alignment.value(), Value: MAI->getTextAlignFillValue(), ValueSize: 1,
326 MaxBytesToEmit);
327 else
328 emitAlignmentDS(ByteAlignment: Alignment.value(), Value: std::nullopt, ValueSize: 1, MaxBytesToEmit);
329}
330
331void SystemZHLASMAsmStreamer::emitBytes(StringRef Data) {
332 assert(getCurrentSectionOnly() &&
333 "Cannot emit contents before setting section!");
334 if (Data.empty())
335 return;
336
337 OS << " DC ";
338 size_t Len = Data.size();
339 SmallVector<uint8_t> Chars;
340 Chars.resize(N: Len);
341 OS << "XL" << Len;
342 uint32_t Index = 0;
343 for (uint8_t C : Data) {
344 Chars[Index] = C;
345 Index++;
346 }
347
348 OS << '\'' << toHex(Input: Chars) << '\'';
349
350 EmitEOL();
351}
352
353void SystemZHLASMAsmStreamer::addEncodingComment(const MCInst &Inst,
354 const MCSubtargetInfo &STI) {
355 raw_ostream &OS = getCommentOS();
356 SmallString<256> Code;
357 SmallVector<MCFixup, 4> Fixups;
358
359 // If we have no code emitter, don't emit code.
360 if (!getAssembler().getEmitterPtr())
361 return;
362
363 getAssembler().getEmitter().encodeInstruction(Inst, CB&: Code, Fixups, STI);
364
365 // If we are showing fixups, create symbolic markers in the encoded
366 // representation. We do this by making a per-bit map to the fixup item index,
367 // then trying to display it as nicely as possible.
368 SmallVector<uint8_t, 64> FixupMap;
369 FixupMap.resize(N: Code.size() * 8);
370 for (unsigned I = 0, E = Code.size() * 8; I != E; ++I)
371 FixupMap[I] = 0;
372
373 for (unsigned I = 0, E = Fixups.size(); I != E; ++I) {
374 MCFixup &F = Fixups[I];
375 MCFixupKindInfo Info =
376 getAssembler().getBackend().getFixupKindInfo(Kind: F.getKind());
377 for (unsigned J = 0; J != Info.TargetSize; ++J) {
378 unsigned Index = F.getOffset() * 8 + Info.TargetOffset + J;
379 assert(Index < Code.size() * 8 && "Invalid offset in fixup!");
380 FixupMap[Index] = 1 + I;
381 }
382 }
383
384 OS << "encoding: [";
385 for (unsigned I = 0, E = Code.size(); I != E; ++I) {
386 if (I)
387 OS << ',';
388
389 // See if all bits are the same map entry.
390 uint8_t MapEntry = FixupMap[I * 8 + 0];
391 for (unsigned J = 1; J != 8; ++J) {
392 if (FixupMap[I * 8 + J] == MapEntry)
393 continue;
394
395 MapEntry = uint8_t(~0U);
396 break;
397 }
398
399 if (MapEntry != uint8_t(~0U)) {
400 if (MapEntry == 0) {
401 OS << format(Fmt: "0x%02x", Vals: uint8_t(Code[I]));
402 } else {
403 if (Code[I]) {
404 // FIXME: Some of the 8 bits require fix up.
405 OS << format(Fmt: "0x%02x", Vals: uint8_t(Code[I])) << '\''
406 << char('A' + MapEntry - 1) << '\'';
407 } else
408 OS << char('A' + MapEntry - 1);
409 }
410 } else {
411 // Otherwise, write out in binary.
412 OS << "0b";
413 for (unsigned J = 8; J--;) {
414 unsigned Bit = (Code[I] >> J) & 1;
415 unsigned FixupBit = I * 8 + (7 - J);
416 if (uint8_t MapEntry = FixupMap[FixupBit]) {
417 assert(Bit == 0 && "Encoder wrote into fixed up bit!");
418 OS << char('A' + MapEntry - 1);
419 } else
420 OS << Bit;
421 }
422 }
423 }
424 OS << "]\n";
425
426 for (unsigned I = 0, E = Fixups.size(); I != E; ++I) {
427 MCFixup &F = Fixups[I];
428 OS << " fixup " << char('A' + I) << " - "
429 << "offset: " << F.getOffset() << ", value: ";
430 MAI->printExpr(OS, *F.getValue());
431 auto Kind = F.getKind();
432 if (mc::isRelocation(FixupKind: Kind))
433 OS << ", relocation type: " << Kind;
434 else {
435 OS << ", kind: ";
436 auto Info = getAssembler().getBackend().getFixupKindInfo(Kind);
437 if (F.isPCRel() && StringRef(Info.Name).starts_with(Prefix: "FK_Data_"))
438 OS << "FK_PCRel_" << (Info.TargetSize / 8);
439 else
440 OS << Info.Name;
441 }
442 OS << "\n";
443 }
444}
445
446void SystemZHLASMAsmStreamer::emitInstruction(const MCInst &Inst,
447 const MCSubtargetInfo &STI) {
448 // Show the encoding in a comment if we have a code emitter.
449 addEncodingComment(Inst, STI);
450 EmitEOL();
451
452 InstPrinter->printInst(MI: &Inst, Address: 0, Annot: "", STI, OS);
453 EmitEOL();
454}
455
456void SystemZHLASMAsmStreamer::emitLabel(MCSymbol *Symbol, SMLoc Loc) {
457 MCSymbolGOFF *Sym = static_cast<MCSymbolGOFF *>(Symbol);
458
459 MCStreamer::emitLabel(Symbol: Sym, Loc);
460
461 // Emit label and ENTRY statement only if not implied by CSECT. Do not emit a
462 // label if the symbol is on a PR section.
463 bool EmitLabelAndEntry =
464 !static_cast<MCSectionGOFF *>(getCurrentSectionOnly())->isPR();
465 if (!Sym->isTemporary() && Sym->isInEDSection()) {
466 EmitLabelAndEntry =
467 Sym->getName() !=
468 static_cast<MCSectionGOFF &>(Sym->getSection()).getParent()->getName();
469 if (EmitLabelAndEntry) {
470 OS << " ENTRY " << Sym->getName();
471 EmitEOL();
472 }
473
474 emitXATTR(OS, Name: Sym->getName(), ADA: Sym->getADA(), IsIndirectReference: Sym->isIndirect(),
475 Linkage: Sym->getLinkage(), Executable: Sym->getCodeData(), BindingScope: Sym->getBindingScope());
476 EmitEOL();
477 if (Sym->hasExternalName())
478 OS << Sym->getName() << " ALIAS C'" << Sym->getExternalName() << "'\n";
479 }
480
481 if (EmitLabelAndEntry) {
482 OS << Sym->getName() << " DS 0H";
483 EmitEOL();
484 }
485}
486
487bool SystemZHLASMAsmStreamer::emitSymbolAttribute(MCSymbol *Sym,
488 MCSymbolAttr Attribute) {
489 return static_cast<MCSymbolGOFF *>(Sym)->setSymbolAttribute(Attribute);
490}
491
492void SystemZHLASMAsmStreamer::emitCommonSymbol(MCSymbol *S, uint64_t Size,
493 Align ByteAlignment) {
494 auto *Symbol = static_cast<MCSymbolGOFF *>(S);
495 MCSectionGOFF *Section =
496 Symbol->getSectionForCommonSymbol(Ctx&: getContext(), ByteAlignment);
497 pushSection();
498 switchSection(Section);
499 emitLabel(Symbol, Loc: SMLoc());
500 emitZeros(NumBytes: Size);
501 popSection();
502}
503
504void SystemZHLASMAsmStreamer::emitRawTextImpl(StringRef String) {
505 String.consume_back(Suffix: "\n");
506 OS << String;
507 EmitEOL();
508}
509
510// Slight duplicate of MCExpr::print due to HLASM only recognizing limited
511// arithmetic operators (+-*/).
512void SystemZHLASMAsmStreamer::emitHLASMValueImpl(const MCExpr *Value,
513 unsigned Size, bool Parens) {
514 switch (Value->getKind()) {
515 case MCExpr::Constant: {
516 OS << "XL" << Size << '\'';
517 MAI->printExpr(OS, *Value);
518 OS << '\'';
519 return;
520 }
521 case MCExpr::Binary: {
522 const MCBinaryExpr &BE = cast<MCBinaryExpr>(Val: *Value);
523 int64_t Const;
524 // Or is handled differently.
525 if (BE.getOpcode() == MCBinaryExpr::Or) {
526 emitHLASMValueImpl(Value: BE.getLHS(), Size, Parens: true);
527 OS << ',';
528 emitHLASMValueImpl(Value: BE.getRHS(), Size, Parens: true);
529 return;
530 }
531
532 if (Parens)
533 OS << "AD(";
534 emitHLASMValueImpl(Value: BE.getLHS(), Size);
535
536 switch (BE.getOpcode()) {
537 case MCBinaryExpr::LShr: {
538 Const = cast<MCConstantExpr>(Val: BE.getRHS())->getValue();
539 OS << '/' << (1 << Const);
540 if (Parens)
541 OS << ')';
542 return;
543 }
544 case MCBinaryExpr::Add:
545 OS << '+';
546 break;
547 case MCBinaryExpr::Div:
548 OS << '/';
549 break;
550 case MCBinaryExpr::Mul:
551 OS << '*';
552 break;
553 case MCBinaryExpr::Sub:
554 OS << '-';
555 break;
556 default:
557 getContext().reportError(L: SMLoc(),
558 Msg: "Unrecognized HLASM arithmetic expression!");
559 }
560 emitHLASMValueImpl(Value: BE.getRHS(), Size);
561 if (Parens)
562 OS << ')';
563 return;
564 }
565 case MCExpr::Target:
566 MAI->printExpr(OS, *Value);
567 return;
568 default:
569 Parens &= isa<MCSymbolRefExpr>(Val: Value);
570 if (Parens)
571 OS << "AD(";
572 MAI->printExpr(OS, *Value);
573 if (Parens)
574 OS << ')';
575 return;
576 }
577}
578
579void SystemZHLASMAsmStreamer::emitValueImpl(const MCExpr *Value, unsigned Size,
580 SMLoc Loc) {
581 assert(Size <= 8 && "Invalid size");
582 assert(getCurrentSectionOnly() &&
583 "Cannot emit contents before setting section!");
584
585 MCStreamer::emitValueImpl(Value, Size, Loc);
586 OS << " DC ";
587 emitHLASMValueImpl(Value, Size, Parens: true);
588 EmitEOL();
589}
590
591void SystemZHLASMAsmStreamer::finishImpl() {
592 for (auto &Symbol : getAssembler().symbols()) {
593 if (Symbol.isTemporary() || !Symbol.isRegistered() || Symbol.isDefined())
594 continue;
595 auto &Sym = static_cast<MCSymbolGOFF &>(const_cast<MCSymbol &>(Symbol));
596 if (Sym.getCodeData() == GOFF::ESD_EXE_DATA) {
597 OS << Sym.getADA()->getParent()->getExternalName() << " CATTR PART("
598 << Sym.getName() << ")";
599 EmitEOL();
600 } else {
601 OS << " " << (Sym.isWeak() ? "WXTRN" : "EXTRN") << " " << Sym.getName();
602 EmitEOL();
603 }
604 emitXATTR(OS, Name: Sym.getName(), ADA: Sym.getADA(), IsIndirectReference: Sym.isIndirect(),
605 Linkage: Sym.getLinkage(), Executable: Sym.getCodeData(), BindingScope: Sym.getBindingScope());
606 EmitEOL();
607 if (Sym.hasExternalName())
608 OS << Sym.getName() << " ALIAS C'" << Sym.getExternalName() << "'\n";
609 }
610
611 // Finish the assembly output.
612 OS << " END";
613 EmitEOL();
614}
615