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::AddComment(const Twine &T, bool EOL) {
119 if (!IsVerboseAsm)
120 return;
121
122 T.toVector(Out&: CommentToEmit);
123
124 if (EOL)
125 CommentToEmit.push_back(Elt: '\n'); // Place comment in a new line.
126}
127
128void SystemZHLASMAsmStreamer::EmitComment() {
129 if (CommentToEmit.empty() && CommentStream.GetNumBytesInBuffer() == 0)
130 return;
131
132 StringRef Comments = CommentToEmit;
133
134 assert(Comments.back() == '\n' && "Comment array not newline terminated");
135 do {
136 // Emit a line of comments, but not exceeding 80 characters.
137 size_t Position = std::min(a: InstLimit - 2, b: Comments.find(C: '\n'));
138 FOS << MAI->getCommentString() << ' ' << Comments.substr(Start: 0, N: Position)
139 << '\n';
140
141 if (Comments[Position] == '\n')
142 Position++;
143 Comments = Comments.substr(Start: Position);
144 } while (!Comments.empty());
145
146 CommentToEmit.clear();
147}
148
149void SystemZHLASMAsmStreamer::emitValueToAlignment(Align Alignment,
150 int64_t Fill,
151 uint8_t FillLen,
152 unsigned MaxBytesToEmit) {
153 emitAlignmentDS(ByteAlignment: Alignment.value(), Value: Fill, ValueSize: FillLen, MaxBytesToEmit);
154}
155
156void SystemZHLASMAsmStreamer::emitCodeAlignment(Align Alignment,
157 const MCSubtargetInfo &STI,
158 unsigned MaxBytesToEmit) {
159 // Emit with a text fill value.
160 if (MAI->getTextAlignFillValue())
161 emitAlignmentDS(ByteAlignment: Alignment.value(), Value: MAI->getTextAlignFillValue(), ValueSize: 1,
162 MaxBytesToEmit);
163 else
164 emitAlignmentDS(ByteAlignment: Alignment.value(), Value: std::nullopt, ValueSize: 1, MaxBytesToEmit);
165}
166
167void SystemZHLASMAsmStreamer::emitBytes(StringRef Data) {
168 assert(getCurrentSectionOnly() &&
169 "Cannot emit contents before setting section!");
170 if (Data.empty())
171 return;
172
173 OS << " DC ";
174 size_t Len = Data.size();
175 SmallVector<uint8_t> Chars;
176 Chars.resize(N: Len);
177 OS << "XL" << Len;
178 uint32_t Index = 0;
179 for (uint8_t C : Data) {
180 Chars[Index] = C;
181 Index++;
182 }
183
184 OS << '\'' << toHex(Input: Chars) << '\'';
185
186 EmitEOL();
187}
188
189void SystemZHLASMAsmStreamer::emitInstruction(const MCInst &Inst,
190 const MCSubtargetInfo &STI) {
191
192 InstPrinter->printInst(MI: &Inst, Address: 0, Annot: "", STI, OS);
193 EmitEOL();
194}
195
196static void emitXATTR(raw_ostream &OS, StringRef Name, MCSectionGOFF *ADA,
197 bool IsIndirectReference, GOFF::ESDLinkageType Linkage,
198 GOFF::ESDExecutable Executable,
199 GOFF::ESDBindingScope BindingScope) {
200 llvm::ListSeparator Sep(",");
201 OS << Name << " XATTR ";
202 OS << Sep << "LINKAGE(" << (Linkage == GOFF::ESD_LT_OS ? "OS" : "XPLINK")
203 << ")";
204
205 const bool NotUnspecified = (Executable != GOFF::ESD_EXE_Unspecified);
206 if (NotUnspecified || IsIndirectReference) {
207 OS << Sep << "REFERENCE(";
208 llvm::ListSeparator SepRef(",");
209
210 if (NotUnspecified)
211 OS << SepRef << (Executable == GOFF::ESD_EXE_CODE ? "CODE" : "DATA");
212
213 if (IsIndirectReference)
214 OS << SepRef << "INDIRECT";
215
216 OS << ")";
217 }
218 // Emit PSECT only for code symbols.
219 if (ADA && Executable != GOFF::ESD_EXE_DATA)
220 OS << Sep << "PSECT(" << ADA->getName() << ")";
221 if (BindingScope != GOFF::ESD_BSC_Unspecified) {
222 OS << Sep << "SCOPE(";
223 switch (BindingScope) {
224 case GOFF::ESD_BSC_Section:
225 OS << "SECTION";
226 break;
227 case GOFF::ESD_BSC_Module:
228 OS << "MODULE";
229 break;
230 case GOFF::ESD_BSC_Library:
231 OS << "LIBRARY";
232 break;
233 case GOFF::ESD_BSC_ImportExport:
234 OS << "EXPORT";
235 break;
236 default:
237 break;
238 }
239 OS << ')';
240 }
241}
242
243void SystemZHLASMAsmStreamer::emitLabel(MCSymbol *Symbol, SMLoc Loc) {
244 MCSymbolGOFF *Sym = static_cast<MCSymbolGOFF *>(Symbol);
245
246 MCStreamer::emitLabel(Symbol: Sym, Loc);
247
248 // Emit label and ENTRY statement only if not implied by CSECT. Do not emit a
249 // label if the symbol is on a PR section.
250 bool EmitLabelAndEntry =
251 !static_cast<MCSectionGOFF *>(getCurrentSectionOnly())->isPR();
252 if (!Sym->isTemporary() && Sym->isInEDSection()) {
253 EmitLabelAndEntry =
254 Sym->getName() !=
255 static_cast<MCSectionGOFF &>(Sym->getSection()).getParent()->getName();
256 if (EmitLabelAndEntry) {
257 OS << " ENTRY " << Sym->getName();
258 EmitEOL();
259 }
260
261 emitXATTR(OS, Name: Sym->getName(), ADA: Sym->getADA(), IsIndirectReference: Sym->isIndirect(),
262 Linkage: Sym->getLinkage(), Executable: Sym->getCodeData(), BindingScope: Sym->getBindingScope());
263 EmitEOL();
264 if (Sym->hasExternalName())
265 OS << Sym->getName() << " ALIAS C'" << Sym->getExternalName() << "'\n";
266 }
267
268 if (EmitLabelAndEntry) {
269 OS << Sym->getName() << " DS 0H";
270 EmitEOL();
271 }
272}
273
274bool SystemZHLASMAsmStreamer::emitSymbolAttribute(MCSymbol *Sym,
275 MCSymbolAttr Attribute) {
276 return static_cast<MCSymbolGOFF *>(Sym)->setSymbolAttribute(Attribute);
277}
278
279void SystemZHLASMAsmStreamer::emitRawTextImpl(StringRef String) {
280 String.consume_back(Suffix: "\n");
281 OS << String;
282 EmitEOL();
283}
284
285// Slight duplicate of MCExpr::print due to HLASM only recognizing limited
286// arithmetic operators (+-*/).
287void SystemZHLASMAsmStreamer::emitHLASMValueImpl(const MCExpr *Value,
288 unsigned Size, bool Parens) {
289 switch (Value->getKind()) {
290 case MCExpr::Constant: {
291 OS << "XL" << Size << '\'';
292 MAI->printExpr(OS, *Value);
293 OS << '\'';
294 return;
295 }
296 case MCExpr::Binary: {
297 const MCBinaryExpr &BE = cast<MCBinaryExpr>(Val: *Value);
298 int64_t Const;
299 // Or is handled differently.
300 if (BE.getOpcode() == MCBinaryExpr::Or) {
301 emitHLASMValueImpl(Value: BE.getLHS(), Size, Parens: true);
302 OS << ',';
303 emitHLASMValueImpl(Value: BE.getRHS(), Size, Parens: true);
304 return;
305 }
306
307 if (Parens)
308 OS << "AD(";
309 emitHLASMValueImpl(Value: BE.getLHS(), Size);
310
311 switch (BE.getOpcode()) {
312 case MCBinaryExpr::LShr: {
313 Const = cast<MCConstantExpr>(Val: BE.getRHS())->getValue();
314 OS << '/' << (1 << Const);
315 if (Parens)
316 OS << ')';
317 return;
318 }
319 case MCBinaryExpr::Add:
320 OS << '+';
321 break;
322 case MCBinaryExpr::Div:
323 OS << '/';
324 break;
325 case MCBinaryExpr::Mul:
326 OS << '*';
327 break;
328 case MCBinaryExpr::Sub:
329 OS << '-';
330 break;
331 default:
332 getContext().reportError(L: SMLoc(),
333 Msg: "Unrecognized HLASM arithmetic expression!");
334 }
335 emitHLASMValueImpl(Value: BE.getRHS(), Size);
336 if (Parens)
337 OS << ')';
338 return;
339 }
340 case MCExpr::Target:
341 MAI->printExpr(OS, *Value);
342 return;
343 default:
344 Parens &= isa<MCSymbolRefExpr>(Val: Value);
345 if (Parens)
346 OS << "AD(";
347 MAI->printExpr(OS, *Value);
348 if (Parens)
349 OS << ')';
350 return;
351 }
352}
353
354void SystemZHLASMAsmStreamer::emitValueImpl(const MCExpr *Value, unsigned Size,
355 SMLoc Loc) {
356 assert(Size <= 8 && "Invalid size");
357 assert(getCurrentSectionOnly() &&
358 "Cannot emit contents before setting section!");
359
360 MCStreamer::emitValueImpl(Value, Size, Loc);
361 OS << " DC ";
362 emitHLASMValueImpl(Value, Size, Parens: true);
363 EmitEOL();
364}
365
366void SystemZHLASMAsmStreamer::finishImpl() {
367 for (auto &Symbol : getAssembler().symbols()) {
368 if (Symbol.isTemporary() || !Symbol.isRegistered() || Symbol.isDefined())
369 continue;
370 auto &Sym = static_cast<MCSymbolGOFF &>(const_cast<MCSymbol &>(Symbol));
371 if (Sym.getCodeData() == GOFF::ESD_EXE_DATA) {
372 OS << Sym.getADA()->getParent()->getExternalName() << " CATTR PART("
373 << Sym.getName() << ")";
374 EmitEOL();
375 } else {
376 OS << " " << (Sym.isWeak() ? "WXTRN" : "EXTRN") << " " << Sym.getName();
377 EmitEOL();
378 }
379 emitXATTR(OS, Name: Sym.getName(), ADA: Sym.getADA(), IsIndirectReference: Sym.isIndirect(),
380 Linkage: Sym.getLinkage(), Executable: Sym.getCodeData(), BindingScope: Sym.getBindingScope());
381 EmitEOL();
382 if (Sym.hasExternalName())
383 OS << Sym.getName() << " ALIAS C'" << Sym.getExternalName() << "'\n";
384 }
385
386 // Finish the assembly output.
387 OS << " END";
388 EmitEOL();
389}
390