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