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,
197 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 if (Executable != GOFF::ESD_EXE_Unspecified)
205 OS << Sep << "REFERENCE("
206 << (Executable == GOFF::ESD_EXE_CODE ? "CODE" : "DATA") << ")";
207 if (BindingScope != GOFF::ESD_BSC_Unspecified) {
208 OS << Sep << "SCOPE(";
209 switch (BindingScope) {
210 case GOFF::ESD_BSC_Section:
211 OS << "SECTION";
212 break;
213 case GOFF::ESD_BSC_Module:
214 OS << "MODULE";
215 break;
216 case GOFF::ESD_BSC_Library:
217 OS << "LIBRARY";
218 break;
219 case GOFF::ESD_BSC_ImportExport:
220 OS << "EXPORT";
221 break;
222 default:
223 break;
224 }
225 OS << ')';
226 }
227 OS << '\n';
228}
229
230void SystemZHLASMAsmStreamer::emitLabel(MCSymbol *Symbol, SMLoc Loc) {
231 MCSymbolGOFF *Sym = static_cast<MCSymbolGOFF *>(Symbol);
232
233 MCStreamer::emitLabel(Symbol: Sym, Loc);
234
235 // Emit label and ENTRY statement only if not implied by CSECT. Do not emit a
236 // label if the symbol is on a PR section.
237 bool EmitLabelAndEntry =
238 !static_cast<MCSectionGOFF *>(getCurrentSectionOnly())->isPR();
239 if (!Sym->isTemporary() && Sym->isInEDSection()) {
240 EmitLabelAndEntry =
241 Sym->getName() !=
242 static_cast<MCSectionGOFF &>(Sym->getSection()).getParent()->getName();
243 if (EmitLabelAndEntry) {
244 OS << " ENTRY " << Sym->getName();
245 EmitEOL();
246 }
247
248 emitXATTR(OS, Name: Sym->getName(), Linkage: Sym->getLinkage(), Executable: Sym->getCodeData(),
249 BindingScope: Sym->getBindingScope());
250 EmitEOL();
251 }
252
253 if (EmitLabelAndEntry) {
254 OS << Sym->getName() << " DS 0H";
255 EmitEOL();
256 }
257}
258
259bool SystemZHLASMAsmStreamer::emitSymbolAttribute(MCSymbol *Sym,
260 MCSymbolAttr Attribute) {
261 return static_cast<MCSymbolGOFF *>(Sym)->setSymbolAttribute(Attribute);
262}
263
264void SystemZHLASMAsmStreamer::emitRawTextImpl(StringRef String) {
265 String.consume_back(Suffix: "\n");
266 OS << String;
267 EmitEOL();
268}
269
270// Slight duplicate of MCExpr::print due to HLASM only recognizing limited
271// arithmetic operators (+-*/).
272void SystemZHLASMAsmStreamer::emitHLASMValueImpl(const MCExpr *Value,
273 unsigned Size, bool Parens) {
274 switch (Value->getKind()) {
275 case MCExpr::Constant: {
276 OS << "XL" << Size << '\'';
277 MAI->printExpr(OS, *Value);
278 OS << '\'';
279 return;
280 }
281 case MCExpr::Binary: {
282 const MCBinaryExpr &BE = cast<MCBinaryExpr>(Val: *Value);
283 int64_t Const;
284 // Or is handled differently.
285 if (BE.getOpcode() == MCBinaryExpr::Or) {
286 emitHLASMValueImpl(Value: BE.getLHS(), Size, Parens: true);
287 OS << ',';
288 emitHLASMValueImpl(Value: BE.getRHS(), Size, Parens: true);
289 return;
290 }
291
292 if (Parens)
293 OS << "AD(";
294 emitHLASMValueImpl(Value: BE.getLHS(), Size);
295
296 switch (BE.getOpcode()) {
297 case MCBinaryExpr::LShr: {
298 Const = cast<MCConstantExpr>(Val: BE.getRHS())->getValue();
299 OS << '/' << (1 << Const);
300 if (Parens)
301 OS << ')';
302 return;
303 }
304 case MCBinaryExpr::Add:
305 OS << '+';
306 break;
307 case MCBinaryExpr::Div:
308 OS << '/';
309 break;
310 case MCBinaryExpr::Mul:
311 OS << '*';
312 break;
313 case MCBinaryExpr::Sub:
314 OS << '-';
315 break;
316 default:
317 getContext().reportError(L: SMLoc(),
318 Msg: "Unrecognized HLASM arithmetic expression!");
319 }
320 emitHLASMValueImpl(Value: BE.getRHS(), Size);
321 if (Parens)
322 OS << ')';
323 return;
324 }
325 case MCExpr::Target:
326 MAI->printExpr(OS, *Value);
327 return;
328 default:
329 Parens &= isa<MCSymbolRefExpr>(Val: Value);
330 if (Parens)
331 OS << "AD(";
332 MAI->printExpr(OS, *Value);
333 if (Parens)
334 OS << ')';
335 return;
336 }
337}
338
339void SystemZHLASMAsmStreamer::emitValueImpl(const MCExpr *Value, unsigned Size,
340 SMLoc Loc) {
341 assert(Size <= 8 && "Invalid size");
342 assert(getCurrentSectionOnly() &&
343 "Cannot emit contents before setting section!");
344
345 MCStreamer::emitValueImpl(Value, Size, Loc);
346 OS << " DC ";
347 emitHLASMValueImpl(Value, Size, Parens: true);
348 EmitEOL();
349}
350
351void SystemZHLASMAsmStreamer::finishImpl() {
352 for (auto &Symbol : getAssembler().symbols()) {
353 if (Symbol.isTemporary() || !Symbol.isRegistered() || Symbol.isDefined())
354 continue;
355 auto &Sym = static_cast<MCSymbolGOFF &>(const_cast<MCSymbol &>(Symbol));
356 OS << " " << (Sym.isWeak() ? "WXTRN" : "EXTRN") << " " << Sym.getName();
357 EmitEOL();
358 emitXATTR(OS, Name: Sym.getName(), Linkage: Sym.getLinkage(), Executable: Sym.getCodeData(),
359 BindingScope: Sym.getBindingScope());
360 EmitEOL();
361 }
362
363 // Finish the assembly output.
364 OS << " END";
365 EmitEOL();
366}
367